[
  {
    "path": ".gitignore",
    "content": "*.class\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\n# idea Files\n.idea\n*.iml\ntarget\n\n# log Files\n*.log\n\n2-Dalston版教程示例/trace-2/build/bootstrap.json\n2-Dalston版教程示例/trace-2/build/springAppName_IS_UNDEFINED.json\n2-Dalston版教程示例/trace-2/build/trace-2.json\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/compute-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>compute-service</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>compute-service</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ComputeServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/java/com/didispace/web/ComputeController.java",
    "content": "package com.didispace.web;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ComputeController {\n\n    private final Logger logger = Logger.getLogger(getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @RequestMapping(value = \"/add\" ,method = RequestMethod.GET)\n    public Integer add(@RequestParam Integer a, @RequestParam Integer b) {\n        ServiceInstance instance = client.getLocalServiceInstance();\n        Integer r = a + b;\n        logger.info(\"/add, host:\" + instance.getHost() + \", service_id:\" + instance.getServiceId() + \", result:\" + r);\n        return r;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/resources/application.properties",
    "content": "spring.application.name=compute-service\n\nserver.port=2222\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/compute-service/src/test/java/com/didispace/ApplicationTests.java",
    "content": "package com.didispace;\n\nimport com.didispace.web.ComputeController;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = MockServletContext.class)\n@WebAppConfiguration\npublic class ApplicationTests {\n\n\tprivate MockMvc mvc;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build();\n\t}\n\n\t@Test\n\tpublic void getHello() throws Exception {\n\t\tmvc.perform(MockMvcRequestBuilders.get(\"/hello\").accept(MediaType.APPLICATION_JSON))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(equalTo(\"Hello World\")));\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/eureka-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-server</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka-server</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/eureka-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-1/eureka-server/src/main/resources/application.properties",
    "content": "server.port=1111\n#eureka.instance.hostname=localhost\n\neureka.client.register-with-eureka=false\neureka.client.fetch-registry=false\neureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-feign</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/FeignApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableFeignClients\npublic class FeignApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(FeignApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/service/ComputeClient.java",
    "content": "package com.didispace.service;\n\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(\"compute-service\")\npublic interface ComputeClient {\n\n    @RequestMapping(method = RequestMethod.GET, value = \"/add\")\n    Integer add(@RequestParam(value = \"a\") Integer a, @RequestParam(value = \"b\") Integer b);\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/web/ConsumerController.java",
    "content": "package com.didispace.web;\n\nimport com.didispace.service.ComputeClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    ComputeClient computeClient;\n\n    @RequestMapping(value = \"/add\", method = RequestMethod.GET)\n    public Integer add() {\n        return computeClient.add(10, 20);\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/resources/application.properties",
    "content": "spring.application.name=feign-consumer\nserver.port=3333\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-ribbon</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-ribbon</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-ribbon</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/java/com/didispace/RibbonApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class RibbonApplication {\n\n\t@Bean\n\t@LoadBalanced\n\tRestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(RibbonApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/java/com/didispace/web/ConsumerController.java",
    "content": "package com.didispace.web;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    RestTemplate restTemplate;\n\n    @RequestMapping(value = \"/add\", method = RequestMethod.GET)\n    public String add() {\n        return restTemplate.getForEntity(\"http://COMPUTE-SERVICE/add?a=10&b=20\", String.class).getBody();\n    }\n\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/resources/application.properties",
    "content": "spring.application.name=ribbon-consumer\nserver.port=3333\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/compute-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>compute-service</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>compute-service</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ComputeServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/java/com/didispace/web/ComputeController.java",
    "content": "package com.didispace.web;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ComputeController {\n\n    private final Logger logger = Logger.getLogger(getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @RequestMapping(value = \"/add\" ,method = RequestMethod.GET)\n    public Integer add(@RequestParam Integer a, @RequestParam Integer b) {\n        ServiceInstance instance = client.getLocalServiceInstance();\n        Integer r = a + b;\n        logger.info(\"/add, host:\" + instance.getHost() + \", service_id:\" + instance.getServiceId() + \", result:\" + r);\n        return r;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/resources/application.properties",
    "content": "spring.application.name=compute-service\n\nserver.port=2222\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/compute-service/src/test/java/com/didispace/ApplicationTests.java",
    "content": "package com.didispace;\n\nimport com.didispace.web.ComputeController;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = MockServletContext.class)\n@WebAppConfiguration\npublic class ApplicationTests {\n\n\tprivate MockMvc mvc;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build();\n\t}\n\n\t@Test\n\tpublic void getHello() throws Exception {\n\t\tmvc.perform(MockMvcRequestBuilders.get(\"/hello\").accept(MediaType.APPLICATION_JSON))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(equalTo(\"Hello World\")));\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.cloud</groupId>-->\n\t\t\t<!--<artifactId>spring-cloud-starter-hystrix</artifactId>-->\n\t\t<!--</dependency>-->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-feign</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/FeignApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableFeignClients\npublic class FeignApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(FeignApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/service/ComputeClient.java",
    "content": "package com.didispace.service;\n\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(value = \"compute-service\", fallback = ComputeClientHystrix.class)\npublic interface ComputeClient {\n\n    @RequestMapping(method = RequestMethod.GET, value = \"/add\")\n    Integer add(@RequestParam(value = \"a\") Integer a, @RequestParam(value = \"b\") Integer b);\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/service/ComputeClientHystrix.java",
    "content": "package com.didispace.service;\n\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@Component\npublic class ComputeClientHystrix implements ComputeClient {\n\n    @Override\n    public Integer add(@RequestParam(value = \"a\") Integer a, @RequestParam(value = \"b\") Integer b) {\n        return -9999;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/web/ConsumerController.java",
    "content": "package com.didispace.web;\n\nimport com.didispace.service.ComputeClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    ComputeClient computeClient;\n\n    @RequestMapping(value = \"/add\", method = RequestMethod.GET)\n    public Integer add() {\n        return computeClient.add(10, 20);\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-ribbon</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-ribbon</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-hystrix</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-ribbon</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/RibbonApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableCircuitBreaker\npublic class RibbonApplication {\n\n\t@Bean\n\t@LoadBalanced\n\tRestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(RibbonApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/service/ComputeService.java",
    "content": "package com.didispace.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\n@Service\npublic class ComputeService {\n\n    @Autowired\n    RestTemplate restTemplate;\n\n    @HystrixCommand(fallbackMethod = \"addServiceFallback\")\n    public String addService() {\n        return restTemplate.getForEntity(\"http://COMPUTE-SERVICE/add?a=10&b=20\", String.class).getBody();\n    }\n\n    public String addServiceFallback() {\n        return \"error\";\n    }\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/web/ConsumerController.java",
    "content": "package com.didispace.web;\n\nimport com.didispace.service.ComputeService;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Map;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private ComputeService computeService;\n\n    @RequestMapping(value = \"/add\", method = RequestMethod.GET)\n    public String add() {\n        return computeService.addService();\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-server</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka-server</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-3/eureka-server/src/main/resources/application.properties",
    "content": "server.port=1111\n#eureka.instance.hostname=localhost\n\neureka.client.register-with-eureka=false\neureka.client.fetch-registry=false\neureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/java/com/didispace/web/TestController.java",
    "content": "package com.didispace.web;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RefreshScope\n@RestController\nclass TestController {\n\n    @Value(\"${from}\")\n    private String from;\n\n    @RequestMapping(\"/from\")\n    public String from() {\n\n        return this.from;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=didispace\nspring.cloud.config.profile=dev\nspring.cloud.config.uri=http://localhost:7001/\n\nserver.port=7002\n\n\n\n\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-dev.properties",
    "content": "from=git-dev-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-prod.properties",
    "content": "from=git-prod-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-test.properties",
    "content": "from=git-test-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace.properties",
    "content": "from=git-default-1.0\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@EnableConfigServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/application.properties",
    "content": "spring.application.name=config-server\nserver.port=7001\n\n# git\nspring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringBoot-Learning/\nspring.cloud.config.server.git.searchPaths=Chapter9-1-4/config-repo\nspring.cloud.config.server.git.username=username\nspring.cloud.config.server.git.password=password\n\n# \nspring.profiles.active=native\n\n\n\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-dev.properties",
    "content": "from=local-dev"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-prod.properties",
    "content": "from=local-prod"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-test.properties",
    "content": "from=local-test"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace.properties",
    "content": "from=local"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/api-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>api-gateway</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>api-gateway</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-zuul</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport com.didispace.filter.AccessFilter;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\nimport org.springframework.context.annotation.Bean;\n\n@EnableZuulProxy\n@SpringCloudApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n\t@Bean\n\tpublic AccessFilter accessFilter() {\n\t\treturn new AccessFilter();\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/java/com/didispace/filter/AccessFilter.java",
    "content": "package com.didispace.filter;\n\nimport com.netflix.zuul.ZuulFilter;\nimport com.netflix.zuul.context.RequestContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic class AccessFilter extends ZuulFilter  {\n\n    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);\n\n    @Override\n    public String filterType() {\n        return \"pre\";\n    }\n\n    @Override\n    public int filterOrder() {\n        return 0;\n    }\n\n    @Override\n    public boolean shouldFilter() {\n        return true;\n    }\n\n    @Override\n    public Object run() {\n        RequestContext ctx = RequestContext.getCurrentContext();\n        HttpServletRequest request = ctx.getRequest();\n\n        log.info(String.format(\"%s request to %s\", request.getMethod(), request.getRequestURL().toString()));\n\n        Object accessToken = request.getParameter(\"accessToken\");\n        if(accessToken == null) {\n            log.warn(\"access token is empty\");\n            ctx.setSendZuulResponse(false);\n            ctx.setResponseStatusCode(401);\n            return null;\n        }\n        log.info(\"access token ok\");\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/resources/application.properties",
    "content": "spring.application.name=api-gateway\nserver.port=5555\n\n# routes to serviceId\nzuul.routes.api-a.path=/api-a/**\nzuul.routes.api-a.serviceId=service-A\n\nzuul.routes.api-b.path=/api-b/**\nzuul.routes.api-b.serviceId=service-B\n\n# routes to url\nzuul.routes.api-a-url.path=/api-a-url/**\nzuul.routes.api-a-url.url=http://localhost:2222/\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/eureka-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-server</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka-server</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/eureka-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/eureka-server/src/main/resources/application.properties",
    "content": "server.port=1111\n#eureka.instance.hostname=localhost\n\neureka.client.register-with-eureka=false\neureka.client.fetch-registry=false\neureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-A/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>service-A</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>service-A</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/java/com/didispace/ComputeServiceApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ComputeServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/java/com/didispace/web/ComputeController.java",
    "content": "package com.didispace.web;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ComputeController {\n\n    private final Logger logger = Logger.getLogger(getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @RequestMapping(value = \"/add\" ,method = RequestMethod.GET)\n    public String add(@RequestParam Integer a, @RequestParam Integer b) {\n        ServiceInstance instance = client.getLocalServiceInstance();\n        Integer r = a + b;\n        logger.info(\"/add, host:\" + instance.getHost() + \", service_id:\" + instance.getServiceId() + \", result:\" + r);\n        return \"From Service-A, Result is \" + r;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/resources/application.properties",
    "content": "spring.application.name=service-A\n\nserver.port=2222\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-A/src/test/java/com/didispace/ApplicationTests.java",
    "content": "package com.didispace;\n\nimport com.didispace.web.ComputeController;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = MockServletContext.class)\n@WebAppConfiguration\npublic class ApplicationTests {\n\n\tprivate MockMvc mvc;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build();\n\t}\n\n\t@Test\n\tpublic void getHello() throws Exception {\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-B/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>service-B</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>service-B</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/java/com/didispace/ComputeServiceApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ComputeServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/java/com/didispace/web/ComputeController.java",
    "content": "package com.didispace.web;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ComputeController {\n\n    private final Logger logger = Logger.getLogger(getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @RequestMapping(value = \"/add\" ,method = RequestMethod.GET)\n    public String add(@RequestParam Integer a, @RequestParam Integer b) {\n        ServiceInstance instance = client.getLocalServiceInstance();\n        Integer r = a + b;\n        logger.info(\"/add, host:\" + instance.getHost() + \", service_id:\" + instance.getServiceId() + \", result:\" + r);\n        return \"From Service-B, Result is \" + r;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/resources/application.properties",
    "content": "spring.application.name=service-B\n\nserver.port=3333\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-5/service-B/src/test/java/com/didispace/ApplicationTests.java",
    "content": "package com.didispace;\n\nimport com.didispace.web.ComputeController;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = MockServletContext.class)\n@WebAppConfiguration\npublic class ApplicationTests {\n\n\tprivate MockMvc mvc;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build();\n\t}\n\n\t@Test\n\tpublic void getHello() throws Exception {\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/compute-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>compute-service</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>compute-service</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.RELEASE</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ComputeServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/java/com/didispace/web/ComputeController.java",
    "content": "package com.didispace.web;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ComputeController {\n\n    private final Logger logger = Logger.getLogger(getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @RequestMapping(value = \"/add\" ,method = RequestMethod.GET)\n    public Integer add(@RequestParam Integer a, @RequestParam Integer b) {\n        ServiceInstance instance = client.getLocalServiceInstance();\n        Integer r = a + b;\n        logger.info(\"/add, host:\" + instance.getHost() + \", service_id:\" + instance.getServiceId() + \", result:\" + r);\n        return r;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/resources/application.properties",
    "content": "spring.application.name=compute-service\n\nserver.port=2222\n\neureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/compute-service/src/test/java/com/didispace/ApplicationTests.java",
    "content": "package com.didispace;\n\nimport com.didispace.web.ComputeController;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = MockServletContext.class)\n@WebAppConfiguration\npublic class ApplicationTests {\n\n\tprivate MockMvc mvc;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build();\n\t}\n\n\t@Test\n\tpublic void getHello() throws Exception {\n\t\tmvc.perform(MockMvcRequestBuilders.get(\"/hello\").accept(MediaType.APPLICATION_JSON))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(equalTo(\"Hello World\")));\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/eureka-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-server</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka-server</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/resources/application-peer1.properties",
    "content": "spring.application.name=eureka-server\nserver.port=1111\neureka.instance.hostname=peer1\n\neureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/resources/application-peer2.properties",
    "content": "spring.application.name=eureka-server\nserver.port=1112\neureka.instance.hostname=peer2\n\neureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client-eureka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client-eureka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.retry</groupId>\n\t\t\t<artifactId>spring-retry</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-bus-amqp</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/java/com/didispace/web/TestController.java",
    "content": "package com.didispace.web;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RefreshScope\n@RestController\npublic class TestController {\n\n    @Value(\"${from}\")\n    private String from;\n\n    @RequestMapping(\"/from\")\n    public String from() {\n        return this.from;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=didispace\nserver.port=7002\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\nspring.cloud.config.discovery.enabled=true\nspring.cloud.config.discovery.serviceId=config-server\nspring.cloud.config.profile=dev\n\nspring.cloud.config.failFast=true\n\nspring.rabbitmq.host=localhost\nspring.rabbitmq.port=5672\nspring.rabbitmq.username=springcloud\nspring.rabbitmq.password=123456"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client-eureka-kafka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client-eureka-kafka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.retry</groupId>\n\t\t\t<artifactId>spring-retry</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-bus-kafka</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/java/com/didispace/web/TestController.java",
    "content": "package com.didispace.web;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RefreshScope\n@RestController\npublic class TestController {\n\n    @Value(\"${from}\")\n    private String from;\n\n    @RequestMapping(\"/from\")\n    public String from() {\n        return this.from;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=didispace\nserver.port=7002\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\nspring.cloud.config.discovery.enabled=true\nspring.cloud.config.discovery.serviceId=config-server\nspring.cloud.config.profile=dev\n\nspring.cloud.config.failFast=true\n\n#kafka\nspring.cloud.stream.kafka.binder.zk-nodes=localhost:2181\nspring.cloud.stream.kafka.binder.brokers=localhost:9092\n\nspring.cloud.bus.trace.enabled=true"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-dev.properties",
    "content": "from=git-dev-3.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-prod.properties",
    "content": "from=git-prod-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-test.properties",
    "content": "from=git-test-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace.properties",
    "content": "from=git-default-1.0\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server-eureka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server-eureka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-bus-amqp</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@EnableDiscoveryClient\n@EnableConfigServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/application.properties",
    "content": "spring.application.name=config-server\nserver.port=7001\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n# git\nspring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/\nspring.cloud.config.server.git.searchPaths=Chapter1-1-8/config-repo\nspring.cloud.config.server.git.username=username\nspring.cloud.config.server.git.password=password\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-dev.properties",
    "content": "from=local-dev"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-prod.properties",
    "content": "from=local-prod"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-test.properties",
    "content": "from=local-test"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace.properties",
    "content": "from=local"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server-eureka-kafka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server-eureka-kafka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-bus-kafka</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@EnableDiscoveryClient\n@EnableConfigServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/application.properties",
    "content": "spring.application.name=config-server\nserver.port=7001\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n# git\nspring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/\nspring.cloud.config.server.git.searchPaths=Chapter1-1-7/config-repo\nspring.cloud.config.server.git.username=username\nspring.cloud.config.server.git.password=password\n\n#kafka\nspring.cloud.stream.kafka.binder.zk-nodes=localhost:2181\nspring.cloud.stream.kafka.binder.brokers=localhost:9092"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-dev.properties",
    "content": "from=local-dev"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-prod.properties",
    "content": "from=local-prod"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-test.properties",
    "content": "from=local-test"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace.properties",
    "content": "from=local"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client-eureka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client-eureka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.retry</groupId>\n\t\t\t<artifactId>spring-retry</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/java/com/didispace/web/TestController.java",
    "content": "package com.didispace.web;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RefreshScope\n@RestController\npublic class TestController {\n\n    @Value(\"${from}\")\n    private String from;\n\n    @RequestMapping(\"/from\")\n    public String from() {\n        return this.from;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n}"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=didispace\nserver.port=7002\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\nspring.cloud.config.discovery.enabled=true\nspring.cloud.config.discovery.serviceId=config-server\nspring.cloud.config.profile=dev\n\nspring.cloud.config.failFast=true"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-dev.properties",
    "content": "from=git-dev-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-prod.properties",
    "content": "from=git-prod-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-test.properties",
    "content": "from=git-test-1.0"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace.properties",
    "content": "from=git-default-1.0\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server-eureka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server-eureka</name>\n\t<description>Spring Cloud project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Brixton.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@EnableDiscoveryClient\n@EnableConfigServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/application.properties",
    "content": "spring.application.name=config-server\nserver.port=7001\n\neureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\n# git¹ÜÀíÅäÖÃ\nspring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/\nspring.cloud.config.server.git.searchPaths=Chapter1-1-8/config-repo\nspring.cloud.config.server.git.username=username\nspring.cloud.config.server.git.password=password\n"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-dev.properties",
    "content": "from=local-dev"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-prod.properties",
    "content": "from=local-prod"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-test.properties",
    "content": "from=local-test"
  },
  {
    "path": "1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace.properties",
    "content": "from=local"
  },
  {
    "path": "1-Brixton版教程示例/README.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 我的博客：http://blog.didispace.com\n- **我的小密圈（深度交流与问答）：https://t.xiaomiquan.com/zfEiY3v**\n- GitHub：https://github.com/dyc87112/SpringCloud-Learning\n- Gitee：https://gitee.com/didispace/SpringCloud-Learning\n- Spring For All社区：http://www.spring4all.com/\n- Spring Boot基础教程：http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- Spring Cloud基础教程：http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- **公益调试Eureka：http://eureka.didispace.com**\n\n**优惠云服务推荐**\n\n- [阿里云：ECS云服务器2折起](https://www.aliyun.com/minisite/goods?userCode=wxfqkr0o&share_source=copy_link)\n- [腾讯云：3年时长最低265元/年](https://cloud.tencent.com/redirect.php?redirect=1005&cps_key=f6a8af1297bfac40b9d10ffa1270029a)\n\n本项目教程因做了版本更新，所以对目录结构做了调整，根目录下的各个章节为了博文链接兼容暂时保留。\n\n## 教程列表\n\n#### 《Spring Cloud构建微服务架构》系列博文Brixton版示例\n\n- 1-Brixton版教程示例/chapter1-1-1：[Spring Cloud构建微服务架构（一）服务注册与发现](http://blog.didispace.com/springcloud1/)\n- 1-Brixton版教程示例/chapter1-1-2：[Spring Cloud构建微服务架构（二）服务消费者](http://blog.didispace.com/springcloud2/)\n- 1-Brixton版教程示例/chapter1-1-3：[Spring Cloud构建微服务架构（三）断路器](http://blog.didispace.com/springcloud3/)\n- 1-Brixton版教程示例/chapter1-1-4：[Spring Cloud构建微服务架构（四）分布式配置中心](http://blog.didispace.com/springcloud4/)\n- 1-Brixton版教程示例/chapter1-1-8：[Spring Cloud构建微服务架构（四）分布式配置中心（续）](http://blog.didispace.com/springcloud4-2/)\n- 1-Brixton版教程示例/chapter1-1-5：[Spring Cloud构建微服务架构（五）服务网关](http://blog.didispace.com/springcloud5/)\n- 1-Brixton版教程示例/chapter1-1-6：[Spring Cloud构建微服务架构（六）高可用服务注册中心](http://blog.didispace.com/springcloud6/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Rabbit)](http://blog.didispace.com/springcloud7/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/)\n\n## 我的公众号\n\n![输入图片说明](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![输入图片说明](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/)\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)"
  },
  {
    "path": "2-Dalston版教程示例/README.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 我的博客：http://blog.didispace.com\n- **我的小密圈（深度交流与问答）：https://t.xiaomiquan.com/zfEiY3v**\n- GitHub：https://github.com/dyc87112/SpringCloud-Learning\n- Gitee：https://gitee.com/didispace/SpringCloud-Learning\n- Spring For All社区：http://www.spring4all.com/\n- Spring Boot基础教程：http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- Spring Cloud基础教程：http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- **公益调试Eureka：http://eureka.didispace.com**\n\n**优惠云服务推荐**\n\n- [阿里云：ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o)\n- [腾讯云：3年时长最低265元/年](https://url.cn/5iF8JP2)\n\n## 教程列表\n\n#### 《Spring Cloud构建微服务架构》系列博文Dalston版\n\n- [服务注册与发现（Eureka、Consul）](http://blog.didispace.com/spring-cloud-starter-dalston-1/)\n- [服务消费者（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/)\n- [服务消费者（Ribbon）](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/)\n- [服务消费者（Feign）](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/)\n- [服务消费者（Feign）传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/)\n- [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3)\n- [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1)\n- [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2)\n- [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3)\n- [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/)\n- [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/)\n- [服务网关（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/)\n- [服务网关（路由配置）](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/)\n- [服务网关（过滤器）](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/)\n- [服务网关（API文档汇总）](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/)\n- [消息驱动的微服务（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/)\n- [消息驱动的微服务（核心概念）](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/)\n- [消息驱动的微服务（消费组）](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/)\n- [消息驱动的微服务（消费组案例：解决消息重复消费）](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/)\n- [消息驱动的微服务（消息分区）](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/)\n- [分布式服务跟踪（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/)\n- [分布式服务跟踪（跟踪原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/)\n- [分布式服务跟踪（整合logstash）](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/)\n- [分布式服务跟踪（整合zipkin）](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/)\n- [分布式服务跟踪（收集原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/)\n- [分布式服务跟踪（抽样收集）](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/)\n\n## 我的公众号\n\n![输入图片说明](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![输入图片说明](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/)\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>api-gateway</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>api-gateway</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-zuul</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@EnableZuulProxy\n@SpringCloudApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: api-gateway\n\nserver:\n  port: 1101\n\nzuul:\n  routes:\n#  single-instance\n    api-a:\n      path: /api-a/**\n      url: http://localhost:2001/\n#  multi-instance\n    api-b:\n      path: /api-b/**\n      serviceId: api-b\n\napi-b:\n  ribbon:\n    listOfServers: http://localhost:2001/, http://localhost:2002/"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway-with-eureka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>api-gateway-with-eureka</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>api-gateway-with-eureka</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-zuul</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway-with-eureka/src/main/java/com/didispace/AccessFilter.java",
    "content": "package com.didispace;\n\nimport com.netflix.zuul.ZuulFilter;\nimport com.netflix.zuul.context.RequestContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic class AccessFilter extends ZuulFilter {\n\n    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);\n\n    @Override\n    public String filterType() {\n        return \"pre\";\n    }\n\n    @Override\n    public int filterOrder() {\n        return 0;\n    }\n\n    @Override\n    public boolean shouldFilter() {\n        return true;\n    }\n\n    @Override\n    public Object run() {\n        RequestContext ctx = RequestContext.getCurrentContext();\n        HttpServletRequest request = ctx.getRequest();\n\n      \tlog.info(\"send {} request to {}\", request.getMethod(), request.getRequestURL().toString());\n\n        Object accessToken = request.getParameter(\"accessToken\");\n        if(accessToken == null) {\n            log.warn(\"access token is empty\");\n            ctx.setSendZuulResponse(false);\n            ctx.setResponseStatusCode(401);\n            return null;\n        }\n        log.info(\"access token ok\");\n        return null;\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway-with-eureka/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@EnableZuulProxy\n@SpringCloudApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/api-gateway-with-eureka/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: api-gateway\n\nserver:\n  port: 1101\n\nzuul:\n  routes:\n    api-a:\n      path: /api-a/**\n      serviceId: eureka-client\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://localhost:1001/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Camden.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/config-client/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n//@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/config-client/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: config-client\n  cloud:\n    config:\n      uri: http://localhost:1201/\n      profile: default\n      label: master\n\nserver:\n  port: 2001\n\n"
  },
  {
    "path": "2-Dalston版教程示例/config-server-git/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server-git</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server-git</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.boot</groupId>-->\n\t\t\t<!--<artifactId>spring-boot-starter-security</artifactId>-->\n\t\t<!--</dependency>-->\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/config-server-git/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@EnableConfigServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/config-server-git/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: config-server\n  cloud:\n    config:\n      server:\n        git:\n          uri: http://git.oschina.net/didispace/config-repo-demo/\n#          search-paths: repo\n#          username:\n#          password:\n\nserver:\n  port: 1201\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>consul-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>consul-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-consul-discovery</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/consul-client/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-client/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    DiscoveryClient discoveryClient;\n\n    @GetMapping(\"/dc\")\n    public String dc() {\n        String services = \"Services: \" + discoveryClient.getServices();\n        System.out.println(services);\n        return services;\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-client/src/main/resources/application.properties",
    "content": "spring.application.name=consul-client\nserver.port=2001\n\nspring.cloud.consul.host=localhost\nspring.cloud.consul.port=8500\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>consul-consumer</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>consul-consumer</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/consul-consumer/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@Bean\n\tpublic RestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-consumer/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    LoadBalancerClient loadBalancerClient;\n    @Autowired\n    RestTemplate restTemplate;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        ServiceInstance serviceInstance = loadBalancerClient.choose(\"consul-client\");\n        String url = \"http://\" + serviceInstance.getHost() + \":\" + serviceInstance.getPort() + \"/dc\";\n        System.out.println(url);\n        return restTemplate.getForObject(url, String.class);\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/consul-consumer/src/main/resources/application.properties",
    "content": "spring.application.name=consul-consumer\nserver.port=2101\n\nspring.cloud.consul.host=localhost\nspring.cloud.consul.port=8500\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.boot</groupId>-->\n\t\t\t<!--<artifactId>spring-boot-starter-actuator</artifactId>-->\n\t\t<!--</dependency>-->\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-client/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-client/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    DiscoveryClient discoveryClient;\n\n    @GetMapping(\"/dc\")\n    public String dc() {\n        String services = \"Services: \" + discoveryClient.getServices();\n        System.out.println(services);\n        return services;\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-client/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-client\nserver.port=2001\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n#eureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/,http://peer2:1002/eureka/\n\nlogging.file=${spring.application.name}.log\n\n#eureka.instance.prefer-ip-address=true\n\n# \n#eureka.client.healthcheck.enabled=true\n\n# ˿\n#eureka.instance.instance-id=${spring.application.name}:${random.int}\n#server.port=0\n#server.port=${random.int[10000,19999]}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>eureka-consumer</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>eureka-consumer</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@Bean\n\tpublic RestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    LoadBalancerClient loadBalancerClient;\n    @Autowired\n    RestTemplate restTemplate;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        ServiceInstance serviceInstance = loadBalancerClient.choose(\"eureka-client\");\n        String url = \"http://\" + serviceInstance.getHost() + \":\" + serviceInstance.getPort() + \"/dc\";\n        System.out.println(url);\n        return restTemplate.getForObject(url, String.class);\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-consumer\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>eureka-consumer-feign</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>eureka-consumer-feign</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-feign</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\n\n@EnableFeignClients\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/DcClient.java",
    "content": "package com.didispace;\n\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n/**\n * @author 翟永超\n * @create 2017/6/24.\n * @blog http://blog.didispace.com\n */\n@FeignClient(\"eureka-client\")\npublic interface DcClient {\n\n    @GetMapping(\"/dc\")\n    String consumer();\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    DcClient dcClient;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        return dcClient.consumer();\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-consumer\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>eureka-consumer-feign-hystrix</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>eureka-consumer-feign-hystrix</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-feign</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\n\n@EnableFeignClients\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcClient.java",
    "content": "package com.didispace;\n\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n/**\n * @author 翟永超\n * @create 2017/6/24.\n * @blog http://blog.didispace.com\n */\n@FeignClient(name = \"eureka-client\", fallback = DcClientFallback.class)\npublic interface DcClient {\n\n    @GetMapping(\"/dc\")\n    String consumer();\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcClientFallback.java",
    "content": "package com.didispace;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author 翟永超\n * @create 2017/6/24.\n * @blog http://blog.didispace.com\n */\n@Component\npublic class DcClientFallback implements DcClient {\n\n    @Override\n    public String consumer() {\n        return \"fallback\";\n    }\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    DcClient dcClient;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        return dcClient.consumer();\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-consumer\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nfeign.hystrix.enabled=true\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>eureka-consumer-ribbon</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>eureka-consumer-ribbon</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@Bean\n\t@LoadBalanced\n\tpublic RestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    RestTemplate restTemplate;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        return restTemplate.getForObject(\"http://eureka-client/dc\", String.class);\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-consumer\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>eureka-consumer-ribbon-hystrix</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <name>eureka-consumer-ribbon-hystrix</name>\n    <description>Spring Cloud In Action</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Dalston.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.netflix.hystrix.EnableHystrix;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableCircuitBreaker\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@Bean\n\t@LoadBalanced\n\tpublic RestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/java/com/didispace/DcController.java",
    "content": "package com.didispace;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.ribbon.proxy.annotation.Hystrix;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@RestController\npublic class DcController {\n\n    @Autowired\n    ConsumerService consumerService;\n\n    @GetMapping(\"/consumer\")\n    public String dc() {\n        return consumerService.consumer();\n    }\n\n    @Service\n    class ConsumerService {\n\n        @Autowired\n        RestTemplate restTemplate;\n\n        @HystrixCommand(fallbackMethod = \"fallback\")\n        public String consumer() {\n            return restTemplate.getForObject(\"http://eureka-client/dc\", String.class);\n        }\n\n        public String fallback() {\n            return \"fallbck\";\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-consumer-ribbon-hystrix\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nlogging.file=${spring.application.name}.log\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign-api</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign-api</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.6.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR2</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-api/src/main/java/com/didispace/api/HelloService.java",
    "content": "package com.didispace.api;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n/**\n * @author 翟永超\n * @create 2017/8/8.\n * @blog http://blog.didispace.com\n */\npublic interface HelloService {\n\n    @GetMapping(\"/hello\")\n    String hello(@RequestParam(value = \"name\") String name);\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.6.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.didispace</groupId>\n\t\t\t<artifactId>eureka-feign-api</artifactId>\n\t\t\t<version>1.0.0</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR2</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-client/src/main/java/com/didispace/api/impl/Application.java",
    "content": "package com.didispace.api.impl;\n\nimport com.didispace.api.HelloService;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@RestController\n\tclass HelloController implements HelloService {\n\n\t\t@Override\n\t\tpublic String hello(String name) {\n\t\t\treturn \"hello \" + name;\n\t\t}\n\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-client/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-feign-client\nserver.port=2101\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign-consumer</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign-consumer</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.6.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-feign</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.didispace</groupId>\n\t\t\t<artifactId>eureka-feign-api</artifactId>\n\t\t\t<version>1.0.0</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR2</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-consumer/src/main/java/com/didispace/api/consumer/Application.java",
    "content": "package com.didispace.api.consumer;\n\nimport com.didispace.api.HelloService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableFeignClients\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@FeignClient(\"eureka-feign-client\")\n\tinterface HelloServiceClient extends HelloService {\n\n\t}\n\n\t@RestController\n\tclass TestController {\n\n\t\t@Autowired\n\t\tprivate HelloServiceClient helloServiceClient;\n\n\t\t@GetMapping(\"/test\")\n\t\tpublic String test(String name) {\n\t\t\treturn helloServiceClient.hello(name);\n\t\t}\n\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-consumer/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-feign-consumer\nserver.port=2102\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign-upload-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign-upload-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.6.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-feign</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>io.github.openfeign.form</groupId>\n\t\t\t<artifactId>feign-form</artifactId>\n\t\t\t<version>3.0.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.github.openfeign.form</groupId>\n\t\t\t<artifactId>feign-form-spring</artifactId>\n\t\t\t<version>3.0.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-fileupload</groupId>\n\t\t\t<artifactId>commons-fileupload</artifactId>\n\t\t\t<version>1.3.3</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.16.20</version>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR2</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/src/main/java/com/didispace/api/consumer/Application.java",
    "content": "package com.didispace.api.consumer;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\n\n\n@EnableFeignClients\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(Application.class).web(true).run(args);\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/src/main/java/com/didispace/api/consumer/UploadService.java",
    "content": "package com.didispace.api.consumer;\n\nimport feign.codec.Encoder;\nimport feign.form.spring.SpringFormEncoder;\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestPart;\nimport org.springframework.web.multipart.MultipartFile;\n\n@FeignClient(value = \"eureka-feign-upload-server\", configuration = UploadService.MultipartSupportConfig.class)\npublic interface UploadService {\n\n    @PostMapping(value = \"/uploadFile\", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    String handleFileUpload(@RequestPart(value = \"file\") MultipartFile file);\n\n    @Configuration\n    class MultipartSupportConfig {\n        @Bean\n        public Encoder feignFormEncoder() {\n            return new SpringFormEncoder();\n        }\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-feign-upload-client\nserver.port=10002\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/src/test/java/com/didispace/api/consumer/UploadTester.java",
    "content": "package com.didispace.api.consumer;\n\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.fileupload.disk.DiskFileItem;\nimport org.apache.commons.fileupload.disk.DiskFileItemFactory;\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.multipart.commons.CommonsMultipartFile;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n@Slf4j\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringBootTest\npublic class UploadTester {\n\n    @Autowired\n    private UploadService uploadService;\n\n    @Test\n    @SneakyThrows\n    public void testHandleFileUpload() {\n\n        File file = new File(\"upload.txt\");\n        DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem(\"file\",\n                MediaType.TEXT_PLAIN_VALUE, true, file.getName());\n\n        try (InputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) {\n            IOUtils.copy(input, os);\n        } catch (Exception e) {\n            throw new IllegalArgumentException(\"Invalid file: \" + e, e);\n        }\n\n        MultipartFile multi = new CommonsMultipartFile(fileItem);\n\n        log.info(uploadService.handleFileUpload(multi));\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-client/upload.txt",
    "content": "aaa\nbbb"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-feign-upload-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-feign-upload-server</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.6.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-feign</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR2</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-server/src/main/java/com/didispace/api/consumer/Application.java",
    "content": "package com.didispace.api.consumer;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestPart;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n@EnableFeignClients\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n    @RestController\n    public class UploadController {\n\n        @PostMapping(value = \"/uploadFile\", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n        public String handleFileUpload(@RequestPart(value = \"file\") MultipartFile file) {\n            return file.getName();\n        }\n\n    }\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(Application.class).web(true).run(args);\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-feign-upload-server/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-feign-upload-server\nserver.port=10001\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>eureka-server</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>eureka-server</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka-server</artifactId>\n\t\t</dependency>\n\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.boot</groupId>-->\n\t\t\t<!--<artifactId>spring-boot-starter-actuator</artifactId>-->\n\t\t<!--</dependency>-->\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/eureka-server/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-server/src/main/resources/application-peer1.properties",
    "content": "spring.application.name=eureka-server\nserver.port=1001\n\neureka.instance.hostname=peer1\neureka.client.serviceUrl.defaultZone=http://peer2:1002/eureka/\n\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-server/src/main/resources/application-peer2.properties",
    "content": "spring.application.name=eureka-server\nserver.port=1002\n\neureka.instance.hostname=peer2\neureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/\n\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/eureka-server/src/main/resources/application.properties",
    "content": "spring.application.name=eureka-server\nserver.port=1001\n\neureka.instance.hostname=localhost\neureka.client.register-with-eureka=false\neureka.client.fetch-registry=false\n\nlogging.file=${spring.application.name}.log\n\n# رձ\n#eureka.server.enable-self-preservation=false\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>hystrix-collapser-consumer</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>hystrix-collapser-consumer</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.boot</groupId>-->\n\t\t\t<!--<artifactId>spring-boot-starter-actuator</artifactId>-->\n\t\t<!--</dependency>-->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-hystrix</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-consumer/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.netflix.hystrix.EnableHystrix;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableHystrix\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\t@Bean\n\t@LoadBalanced\n\tpublic RestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-consumer/src/main/java/com/didispace/UserService.java",
    "content": "package com.didispace;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n/**\n * @author 翟永超\n * @create 2017/7/25.\n * @blog http://blog.didispace.com\n */\n@Slf4j\n@Service\npublic class UserService {\n\n    @Autowired\n    RestTemplate restTemplate;\n\n    @HystrixCollapser(\n            scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,\n            batchMethod = \"findByIds\",\n            collapserProperties = {\n                @HystrixProperty(name=\"timerDelayInMilliseconds\", value = \"100\")\n            }\n    )\n    public Future<String> findById(Long id) {\n        log.info(\"findById : \" + id);\n        return null;\n    }\n\n    @HystrixCommand(commandProperties = @HystrixProperty(name = \"execution.isolation.thread.timeoutInMilliseconds\", value = \"5000\"))\n    public List<String> findByIds(List<Long> ids) {\n        log.info(\"findByIds : \" + ids);\n        List<String> result = restTemplate.getForObject(\"http://hystrix-collapser-provider/users?ids={1}\",\n                List.class, StringUtils.join(ids, \",\"));\n        log.info(result.toString());\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-consumer/src/main/resources/application.properties",
    "content": "spring.application.name=hystrix-collapser-consumer\nserver.port=2002\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-consumer/src/test/java/CollapserTest.java",
    "content": "import com.didispace.Application;\nimport com.didispace.UserService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.Future;\n\n/**\n * @author 翟永超\n * @create 2017/7/25.\n * @blog http://blog.didispace.com\n */\n@Slf4j\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class CollapserTest {\n\n    @Autowired\n    UserService userService;\n\n    @Test\n    public void test() throws Exception {\n        Future<String> u1 = userService.findById(1L);\n        Future<String> u2 = userService.findById(2L);\n        Future<String> u3 = userService.findById(3L);\n        Future<String> u4 = userService.findById(4L);\n\n\n        log.info(u1.get().toString());\n        log.info(u2.get().toString());\n        log.info(u3.get().toString());\n        log.info(u4.get().toString());\n\n\n        Assert.assertEquals(\"aaa\",u1.get());\n        Assert.assertEquals(\"bbb\",u2.get());\n        Assert.assertEquals(\"ccc\",u3.get());\n        Assert.assertEquals(\"ddd\",u4.get());\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>hystrix-collapser-provider</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>hystrix-collapser-provider</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<!--<dependency>-->\n\t\t\t<!--<groupId>org.springframework.boot</groupId>-->\n\t\t\t<!--<artifactId>spring-boot-starter-actuator</artifactId>-->\n\t\t<!--</dependency>-->\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-provider/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-provider/src/main/java/com/didispace/UserController.java",
    "content": "package com.didispace;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author 翟永超\n * @create 2017/4/15.\n * @blog http://blog.didispace.com\n */\n@Slf4j\n@RestController\npublic class UserController {\n\n    private static Map<Long, String> users = new HashMap<>();\n\n    static {\n        users.put(1L, \"aaa\");\n        users.put(2L, \"bbb\");\n        users.put(3L, \"ccc\");\n        users.put(4L, \"ddd\");\n        users.put(5L, \"eee\");\n    }\n\n    @RequestMapping(value = \"/users/{id}\", method = RequestMethod.GET)\n    public String findById(@PathVariable Long id) {\n        log.info(\"findById : \" + id);\n        return users.get(id);\n    }\n\n    @RequestMapping(value = \"/users\", method = RequestMethod.GET)\n    public List<String> findByIds(@RequestParam String ids) {\n        log.info(\"findByIds : \" + ids);\n        List<String> result = new ArrayList<>();\n        for(String id : ids.split(\",\")) {\n            if(users.get(Long.valueOf(id)) != null)\n                result.add(users.get(Long.valueOf(id)));\n        }\n\n        log.info(\"findByIds : \" + result);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-collapser-provider/src/main/resources/application.properties",
    "content": "spring.application.name=hystrix-collapser-provider\nserver.port=2001\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-dashboard/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>hystrix-dashboard</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>hystrix-dashboard</name>\n\n\t<parent>\n\t\t<groupId>org.springframework.cloud</groupId>\n\t\t<artifactId>spring-cloud-starter-parent</artifactId>\n\t\t<version>Dalston.SR1</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-hystrix</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-dashboard/src/main/java/com/didispace/HystrixDashboardApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;\n\n@EnableHystrixDashboard\n@SpringCloudApplication\npublic class HystrixDashboardApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(HystrixDashboardApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/hystrix-dashboard/src/main/resources/application.properties",
    "content": "spring.application.name=hystrix-dashboard\nserver.port=1301"
  },
  {
    "path": "2-Dalston版教程示例/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>spring-cloud-dalston</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <modules>\n\n        <module>eureka-server</module>\n        <module>eureka-client</module>\n        <module>consul-client</module>\n\n        <module>eureka-consumer</module>\n        <module>consul-consumer</module>\n\n        <!--使用ribbon和feign调用服务接口-->\n        <module>eureka-consumer-ribbon</module>\n        <module>eureka-consumer-feign</module>\n\n        <!--分布式配置中心-->\n        <module>config-server-git</module>\n        <module>config-client</module>\n\n        <!--使用hystrix-->\n        <module>eureka-consumer-ribbon-hystrix</module>\n        <module>eureka-consumer-feign-hystrix</module>\n\n        <module>hystrix-dashboard</module>\n        <module>turbine</module>\n        <module>turbine-amqp</module>\n\n        <!--使用feign-->\n        <module>eureka-feign-api</module>\n        <module>eureka-feign-client</module>\n        <module>eureka-feign-consumer</module>\n        <!--使用feign上传文件-->\n        <module>eureka-feign-upload-client</module>\n        <module>eureka-feign-upload-server</module>\n\n        <!--hystrix请求合并-->\n        <module>hystrix-collapser-provider</module>\n        <module>hystrix-collapser-consumer</module>\n\n        <!--api网关-->\n        <module>api-gateway</module>\n        <module>api-gateway-with-eureka</module>\n\n        <!--使用spring cloud stream-->\n        <module>stream-hello</module>\n        <module>stream-consumer-group</module> <!-- 负载均衡消费 -->\n        <module>stream-consumer-self</module> <!-- 消费自己的消息 -->\n\n        <!--分布式跟踪-->\n        <module>trace-1</module>\n        <module>trace-2</module>\n        <module>zipkin-server</module>\n        <module>zipkin-server-stream</module>\n\n        <!--swagger api 文档汇总-->\n        <module>swagger-service-a</module>\n        <module>swagger-service-b</module>\n        <module>swagger-api-gateway</module>\n\n    </modules>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-consumer-group</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR4</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleApplication.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ExampleApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ExampleApplication.class, args);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleBinder.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\ninterface ExampleBinder {\n\n    String NAME = \"example-topic\";\n\n    @Input(NAME)\n    SubscribableChannel input();\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleReceiver.java",
    "content": "package com.didispace.stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.StreamListener;\n\n@EnableBinding(ExampleBinder.class)\npublic class ExampleReceiver {\n\n    private static Logger logger = LoggerFactory.getLogger(ExampleReceiver.class);\n\n    @StreamListener(ExampleBinder.NAME)\n    public void receive(String payload) {\n        logger.info(\"Received: \" + payload);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/src/main/resources/application.properties",
    "content": "spring.application.name=stream-consumer-group\nserver.port=0\n\nspring.cloud.stream.bindings.example-topic.group=aaa\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-group/src/test/java/com/didispace/ExampleApplicationTests.java",
    "content": "package com.didispace;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@EnableBinding(value = {ExampleApplicationTests.ExampleBinder.class})\npublic class ExampleApplicationTests {\n\n\t@Autowired\n\tprivate ExampleBinder exampleBinder;\n\n\t@Test\n\tpublic void exampleBinderTester() {\n        exampleBinder.output().send(MessageBuilder.withPayload(\"Produce a message from : http://blog.didispace.com\").build());\n\t}\n\n\tpublic interface ExampleBinder {\n\n\t\tString NAME = \"example-topic\";\n\n\t\t@Output(NAME)\n\t\tMessageChannel output();\n\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-consumer-self</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR4</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n\n@EnableBinding(TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestController.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\npublic class TestController {\n\n    @Autowired\n    private TestTopic testTopic;\n\n    @GetMapping(\"/sendMessage\")\n    public String messageWithMQ(@RequestParam String message) {\n        testTopic.output().send(MessageBuilder.withPayload(message).build());\n        return \"ok\";\n    }\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestListener.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class TestListener {\n\n    @StreamListener(TestTopic.INPUT)\n    public void receive(String payload) {\n        log.info(\"Received: \" + payload);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestTopic.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface TestTopic {\n\n    String OUTPUT = \"example-topic\";\n    String INPUT = \"example-topic\";\n\n    @Output(OUTPUT)\n    MessageChannel output();\n\n    @Input(INPUT)\n    SubscribableChannel input();\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-consumer-self/src/main/resources/application.properties",
    "content": "spring.application.name=stream-consumer-self\nserver.port=8080\n\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-hello/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-hello</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>stream-hello</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR4</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-hello/src/main/java/com/didispace/stream/SinkApplication.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SinkApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SinkApplication.class, args);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-hello/src/main/java/com/didispace/stream/SinkReceiver.java",
    "content": "package com.didispace.stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.cloud.stream.messaging.Sink;\n\n@EnableBinding(Sink.class)\npublic class SinkReceiver {\n\n    private static Logger logger = LoggerFactory.getLogger(SinkReceiver.class);\n\n    @StreamListener(Sink.INPUT)\n    public void receive(Object payload) {\n        logger.info(\"Received: \" + payload);\n    }\n\n}"
  },
  {
    "path": "2-Dalston版教程示例/stream-hello/src/main/resources/application.properties",
    "content": "spring.application.name=stream-hello\nserver.port=0\n\n\n\n"
  },
  {
    "path": "2-Dalston版教程示例/stream-hello/src/test/java/com/didispace/SinkApplicationTests.java",
    "content": "package com.didispace;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@EnableBinding(value = {SinkApplicationTests.SinkSender.class})\npublic class SinkApplicationTests {\n\n\t@Autowired\n\tprivate SinkSender sinkSender;\n\n\t@Test\n\tpublic void sinkSenderTester() {\n\t\tsinkSender.output().send(MessageBuilder.withPayload(\"produce a message ：http://blog.didispace.com\").build());\n\t}\n\n\tpublic interface SinkSender {\n\n\t\tString OUTPUT = \"input\";\n\n\t\t@Output(SinkSender.OUTPUT)\n\t\tMessageChannel output();\n\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-api-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>swagger-api-gateway</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>swagger-api-gateway</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.4.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-zuul</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.spring4all</groupId>\n\t\t\t<artifactId>swagger-spring-boot-starter</artifactId>\n\t\t\t<version>1.7.0.RELEASE</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/swagger-api-gateway/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport com.spring4all.swagger.EnableSwagger2Doc;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Component;\nimport springfox.documentation.swagger.web.SwaggerResource;\nimport springfox.documentation.swagger.web.SwaggerResourcesProvider;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@EnableSwagger2Doc\n@EnableZuulProxy\n@SpringCloudApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n\t@Component\n\t@Primary\n\tclass DocumentationConfig implements SwaggerResourcesProvider {\n\t\t@Override\n\t\tpublic List<SwaggerResource> get() {\n\t\t\tList resources = new ArrayList<>();\n\t\t\tresources.add(swaggerResource(\"service-a\", \"/swagger-service-a/v2/api-docs\", \"2.0\"));\n\t\t\tresources.add(swaggerResource(\"service-b\", \"/swagger-service-b/v2/api-docs\", \"2.0\"));\n\t\t\treturn resources;\n\t\t}\n\n\t\tprivate SwaggerResource swaggerResource(String name, String location, String version) {\n\t\t\tSwaggerResource swaggerResource = new SwaggerResource();\n\t\t\tswaggerResource.setName(name);\n\t\t\tswaggerResource.setLocation(location);\n\t\t\tswaggerResource.setSwaggerVersion(version);\n\t\t\treturn swaggerResource;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-api-gateway/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: swagger-api-gateway\n\nserver:\n  port: 11000\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://eureka.didispace.com/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-a/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>swagger-service-a</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>swagger-service-a</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.spring4all</groupId>\n\t\t\t<artifactId>swagger-spring-boot-starter</artifactId>\n\t\t\t<version>1.7.0.RELEASE</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-a/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport com.spring4all.swagger.EnableSwagger2Doc;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableSwagger2Doc\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n\t@RestController\n\tclass AaaController {\n\n\t\t@Autowired\n\t\tDiscoveryClient discoveryClient;\n\n\t\t@GetMapping(\"/service-a\")\n\t\tpublic String dc() {\n\t\t\tString services = \"Services: \" + discoveryClient.getServices();\n\t\t\tSystem.out.println(services);\n\t\t\treturn services;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-a/src/main/resources/application.properties",
    "content": "spring.application.name=swagger-service-a\nserver.port=10010\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n\nswagger.base-package=com.didispace\n\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-b/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>swagger-service-b</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>swagger-service-b</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n        <dependency>\n            <groupId>com.spring4all</groupId>\n            <artifactId>swagger-spring-boot-starter</artifactId>\n            <version>1.7.0.RELEASE</version>\n        </dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-b/src/main/java/com/didispace/Application.java",
    "content": "package com.didispace;\n\nimport com.spring4all.swagger.EnableSwagger2Doc;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableSwagger2Doc\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class Application {\n\n\tpublic static void main(String[] args) {\n\t\tnew SpringApplicationBuilder(Application.class).web(true).run(args);\n\t}\n\n\t@RestController\n\tclass BbbController {\n\n\t\t@Autowired\n\t\tDiscoveryClient discoveryClient;\n\n\t\t@GetMapping(\"/service-b\")\n\t\tpublic String dc() {\n\t\t\tString services = \"Services: \" + discoveryClient.getServices();\n\t\t\tSystem.out.println(services);\n\t\t\treturn services;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/swagger-service-b/src/main/resources/application.properties",
    "content": "spring.application.name=swagger-service-b\nserver.port=10020\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n\nswagger.base-package=com.didispace\n"
  },
  {
    "path": "2-Dalston版教程示例/trace-1/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>trace-1</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>trace-1</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-sleuth</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-ribbon</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-sleuth-zipkin</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>net.logstash.logback</groupId>\n\t\t\t<artifactId>logstash-logback-encoder</artifactId>\n\t\t\t<version>4.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/trace-1/src/main/java/com/didispace/TraceApplication.java",
    "content": "package com.didispace;\n\nimport org.apache.log4j.Logger;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TraceApplication {\n\n\tprivate final Logger logger = Logger.getLogger(getClass());\n\n//\t@Bean\n//\tpublic AlwaysSampler defaultSampler() {\n//\t\treturn new AlwaysSampler();\n//\t}\n\n\t@Bean\n\t@LoadBalanced\n\tRestTemplate restTemplate() {\n\t\treturn new RestTemplate();\n\t}\n\n\t@RequestMapping(value = \"/trace-1\", method = RequestMethod.GET)\n\tpublic String trace() {\n\t\tlogger.info(\"===<call trace-1>===\");\n\t\treturn restTemplate().getForEntity(\"http://trace-2/trace-2\", String.class).getBody();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(TraceApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/trace-1/src/main/resources/application.properties",
    "content": "spring.application.name=trace-1\nserver.port=9101\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n\n#spring.zipkin.base-url=http://localhost:9411\n\n#spring.sleuth.sampler.percentage=0.1\n\n# log trace detail\n#logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG"
  },
  {
    "path": "2-Dalston版教程示例/trace-2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>trace-2</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>trace-2</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-eureka</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-ribbon</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-sleuth</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>net.logstash.logback</groupId>\n\t\t\t<artifactId>logstash-logback-encoder</artifactId>\n\t\t\t<version>4.6</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-sleuth-zipkin</artifactId>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/trace-2/src/main/java/com/didispace/TraceApplication.java",
    "content": "package com.didispace;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@RestController\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TraceApplication {\n\n\tprivate final Logger logger = LoggerFactory.getLogger(getClass());\n\n\t@RequestMapping(value = \"/trace-2\", method = RequestMethod.GET)\n\tpublic String trace(HttpServletRequest request) {\n\t\tlogger.info(\"===<call trace-2, TraceId={}, SpanId={}>===\",\n\t\t\t\trequest.getHeader(\"X-B3-TraceId\"), request.getHeader(\"X-B3-SpanId\"));\n\t\treturn \"Trace\";\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(TraceApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/trace-2/src/main/resources/application.properties",
    "content": "spring.application.name=trace-2\nserver.port=9102\n\neureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/\n\n#spring.zipkin.base-url=http://localhost:9411\n#\n#spring.sleuth.sampler.percentage=0.5\n#\n## log trace detail\n#logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG"
  },
  {
    "path": "2-Dalston版教程示例/trace-2/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n    ​\n    <springProperty scope=\"context\" name=\"springAppName\" source=\"spring.application.name\"/>\n    <!-- Example for logging into the build folder of your project -->\n    <property name=\"LOG_FILE\" value=\"${BUILD_FOLDER:-build}/${springAppName}\"/>​\n\n    <property name=\"CONSOLE_LOG_PATTERN\"\n              value=\"%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([${springAppName:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow} %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}\"/>\n\n    <!-- Appender to log to console -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <!-- Minimum logging level to be presented in the console logs-->\n            <level>INFO</level>\n        </filter>\n        <encoder>\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!-- Appender to log to file -->​\n    <!--<appender name=\"flatfile\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">-->\n        <!--<file>${LOG_FILE}</file>-->\n        <!--<rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">-->\n            <!--<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>-->\n            <!--<maxHistory>7</maxHistory>-->\n        <!--</rollingPolicy>-->\n        <!--<encoder>-->\n            <!--<pattern>${CONSOLE_LOG_PATTERN}</pattern>-->\n            <!--<charset>utf8</charset>-->\n        <!--</encoder>-->\n    <!--</appender>-->\n    ​\n    <!-- Appender to log to file in a JSON format -->\n    <appender name=\"logstash\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}.json</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>\n            <maxHistory>7</maxHistory>\n        </rollingPolicy>\n        <encoder class=\"net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder\">\n            <providers>\n                <timestamp>\n                    <timeZone>UTC</timeZone>\n                </timestamp>\n                <pattern>\n                    <pattern>\n                        {\n                        \"severity\": \"%level\",\n                        \"service\": \"${springAppName:-}\",\n                        \"trace\": \"%X{X-B3-TraceId:-}\",\n                        \"span\": \"%X{X-B3-SpanId:-}\",\n                        \"exportable\": \"%X{X-Span-Export:-}\",\n                        \"pid\": \"${PID:-}\",\n                        \"thread\": \"%thread\",\n                        \"class\": \"%logger{40}\",\n                        \"rest\": \"%message\"\n                        }\n                    </pattern>\n                </pattern>\n            </providers>\n        </encoder>\n    </appender>\n    ​\n    <root level=\"INFO\">\n        <appender-ref ref=\"console\"/>\n        <appender-ref ref=\"logstash\"/>\n        <!--<appender-ref ref=\"flatfile\"/>-->\n    </root>\n</configuration>"
  },
  {
    "path": "2-Dalston版教程示例/turbine/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>turbine</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>turbine</name>\n\n\t<parent>\n\t\t<groupId>org.springframework.cloud</groupId>\n\t\t<artifactId>spring-cloud-starter-parent</artifactId>\n\t\t<version>Dalston.SR1</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-turbine</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/turbine/src/main/java/com/didispace/TurbineApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.turbine.EnableTurbine;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@EnableAutoConfiguration\n@EnableTurbine\n@EnableDiscoveryClient\npublic class TurbineApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(TurbineApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/turbine/src/main/resources/application.properties",
    "content": "spring.application.name=turbine\n\nserver.port=8989\nmanagement.port=8990\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/\n\nturbine.app-config=eureka-consumer-ribbon-hystrix\nturbine.cluster-name-expression=\"default\"\nturbine.combine-host-port=true"
  },
  {
    "path": "2-Dalston版教程示例/turbine-amqp/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>turbine-amqp</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>turbine-amqp</name>\n\n\t<parent>\n\t\t<groupId>org.springframework.cloud</groupId>\n\t\t<artifactId>spring-cloud-starter-parent</artifactId>\n\t\t<version>Dalston.SR1</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-turbine-amqp</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/turbine-amqp/src/main/java/com/didispace/TurbineApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@EnableAutoConfiguration\n@EnableTurbineStream\n@EnableDiscoveryClient\npublic class TurbineApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(TurbineApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/turbine-amqp/src/main/resources/application.properties",
    "content": "spring.application.name=turbine-amqp\n\nserver.port=8989\nmanagement.port=8990\n\neureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>zipkin-server</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>zipkin-server</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.zipkin.java</groupId>\n\t\t\t<artifactId>zipkin-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.zipkin.java</groupId>\n\t\t\t<artifactId>zipkin-autoconfigure-ui</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server/src/main/java/com/didispace/ZipkinApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport zipkin.server.EnableZipkinServer;\n\n@EnableZipkinServer\n@SpringBootApplication\npublic class ZipkinApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ZipkinApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server/src/main/resources/application.properties",
    "content": "spring.application.name=zipkin-server\nserver.port=9411\n\n#eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server-stream/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>zipkin-server-stream</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>zipkin-server-stream</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.10.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.zipkin.java</groupId>\n\t\t\t<artifactId>zipkin-autoconfigure-ui</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Dalston.SR5</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server-stream/src/main/java/com/didispace/ZipkinApplication.java",
    "content": "package com.didispace;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;\nimport org.springframework.context.annotation.Bean;\nimport zipkin.server.EnableZipkinServer;\nimport zipkin.storage.SpanStore;\n\n@EnableZipkinStreamServer\n@SpringBootApplication\npublic class ZipkinApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ZipkinApplication.class, args);\n\t}\n\n}\n"
  },
  {
    "path": "2-Dalston版教程示例/zipkin-server-stream/src/main/resources/application.properties",
    "content": "spring.application.name=zipkin-server-stream\nserver.port=9411\n\nspring.rabbitmq.host=localhost\nspring.rabbitmq.port=5672\nspring.rabbitmq.username=springcloud\nspring.rabbitmq.password=123456\n\n#eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/\n\nlogging.file=${spring.application.name}.log"
  },
  {
    "path": "3-Edgware/README.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 我的博客：http://blog.didispace.com\n- **我的小密圈（深度交流与问答）：https://t.xiaomiquan.com/zfEiY3v**\n- GitHub：https://github.com/dyc87112/SpringCloud-Learning\n- Gitee：https://gitee.com/didispace/SpringCloud-Learning\n- Spring For All社区：http://www.spring4all.com/\n- Spring Boot基础教程：http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- Spring Cloud基础教程：http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- **公益调试Eureka：http://eureka.didispace.com**\n\n**优惠云服务推荐**\n\n- [阿里云：ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o)\n- [腾讯云：3年时长最低265元/年](https://url.cn/5iF8JP2)\n\n## 教程列表\n\n### 《Spring Cloud构建微服务架构》系列博文\n\n#### Finchley版\n\n**本系列主要补充之前版本新增或是变动的主要内容，基础使用依然可以参考Dalston版教程**\n\n- [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/)\n\n##### Spring Cloud Aliabab专题\n\n- [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/)\n- [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/)\n- [Spring Cloud Alibaba到底坑不坑？](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/)\n\n*注册中心与配置中心：Nacos*\n\n- [Spring Cloud Alibaba基础教程：使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/)\n- [Spring Cloud Alibaba基础教程：Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/)\n- [Spring Cloud Alibaba基础教程：支持的几种服务消费方式（RestTemplate、WebClient、Feign）](http://blog.didispace.com/spring-cloud-alibaba-2/)\n- [Spring Cloud Alibaba基础教程：使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/)\n- [Spring Cloud Alibaba基础教程：Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/)\n- [Spring Cloud Alibaba基础教程：Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/)\n\n*分布式流量防卫兵：Sentinel*\n\n- [Spring Cloud Alibaba基础教程：使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/)\n- [Spring Cloud Alibaba基础教程：@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/)\n\n**Spring Cloud Stream专题补充**\n\n- [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1)\n- [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6)\n- [Spring Cloud Stream使用延迟消息实现定时任务（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-7)\n- [Spring Cloud Stream消费失败后的处理策略（一）：自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2)\n- [Spring Cloud Stream消费失败后的处理策略（二）：自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3)\n- [Spring Cloud Stream消费失败后的处理策略（三）：使用DLQ队列（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-4)\n- [Spring Cloud Stream消费失败后的处理策略（四）：重新入队（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-5)\n\n#### Edgware版\n\n> 本系列主要是对Dalston版的补充，包含Edgware版的主要新增或变动的内容，对于Spring Cloud的基础使用依然建议参考Dalston版教程\n\n- [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1)\n\n#### Dalston版\n\n- [服务注册与发现（Eureka、Consul）](http://blog.didispace.com/spring-cloud-starter-dalston-1/)\n- [服务消费者（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/)\n- [服务消费者（Ribbon）](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/)\n- [服务消费者（Feign）](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/)\n- [服务消费者（Feign）传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/)\n- [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3)\n- [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1)\n- [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2)\n- [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3)\n- [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/)\n- [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/)\n- [服务网关（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/)\n- [服务网关（路由配置）](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/)\n- [服务网关（过滤器）](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/)\n- [服务网关（API文档汇总）](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/)\n- [消息驱动的微服务（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/)\n- [消息驱动的微服务（核心概念）](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/)\n- [消息驱动的微服务（消费组）](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/)\n- [消息驱动的微服务（消费组案例：解决消息重复消费）](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/)\n- [消息驱动的微服务（消息分区）](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/)\n- [分布式服务跟踪（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/)\n- [分布式服务跟踪（跟踪原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/)\n- [分布式服务跟踪（整合logstash）](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/)\n- [分布式服务跟踪（整合zipkin）](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/)\n- [分布式服务跟踪（收集原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/)\n- [分布式服务跟踪（抽样收集）](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/)\n\n#### Brixton版\n\n- 1-Brixton版教程示例/chapter1-1-1：[Spring Cloud构建微服务架构（一）服务注册与发现](http://blog.didispace.com/springcloud1/)\n- 1-Brixton版教程示例/chapter1-1-2：[Spring Cloud构建微服务架构（二）服务消费者](http://blog.didispace.com/springcloud2/)\n- 1-Brixton版教程示例/chapter1-1-3：[Spring Cloud构建微服务架构（三）断路器](http://blog.didispace.com/springcloud3/)\n- 1-Brixton版教程示例/chapter1-1-4：[Spring Cloud构建微服务架构（四）分布式配置中心](http://blog.didispace.com/springcloud4/)\n- 1-Brixton版教程示例/chapter1-1-8：[Spring Cloud构建微服务架构（四）分布式配置中心（续）](http://blog.didispace.com/springcloud4-2/)\n- 1-Brixton版教程示例/chapter1-1-5：[Spring Cloud构建微服务架构（五）服务网关](http://blog.didispace.com/springcloud5/)\n- 1-Brixton版教程示例/chapter1-1-6：[Spring Cloud构建微服务架构（六）高可用服务注册中心](http://blog.didispace.com/springcloud6/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Rabbit)](http://blog.didispace.com/springcloud7/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/)\n\n### 《Spring Cloud源码分析》系列博文\n\n- [Spring Cloud源码分析（一）Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（四）Zuul：核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/)\n- 未完待续\n\n### 《Spring Cloud实战小贴士》系列博文\n\n- [Spring Cloud实战小贴士：版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/)\n- [Spring Cloud实战小贴士：随机端口](http://blog.didispace.com/spring-cloud-tips-2/)\n- [Spring Cloud实战小贴士：健康检查](http://blog.didispace.com/spring-cloud-tips-3/)\n- [Spring Cloud实战小贴士：Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（一）](http://blog.didispace.com/spring-cloud-zuul-exception/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（二）](http://blog.didispace.com/spring-cloud-zuul-exception-2/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（三）【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/)\n- [Spring Cloud实战小贴士：Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/)\n- [Spring Cloud实战小贴士：Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/)\n- [Spring Cloud实战小贴士：Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/)\n- [Spring Cloud实战小贴士：Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/)\n\n#### 其他文章\n\n- [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/)\n- [为Spring Cloud Ribbon配置请求重试（Camden.SR2+）](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/)\n- [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/)\n- [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/)\n- [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/)\n- [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/)\n- [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/)\n\n## 我的公众号\n\n![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/)\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)\n"
  },
  {
    "path": "3-Edgware/config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-client</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-client</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.11.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Edgware.SR3</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "3-Edgware/config-client/src/main/java/com/didispace/config/client/ConfigClientApplication.java",
    "content": "package com.didispace.config.client;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\npublic class ConfigClientApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigClientApplication.class);\n    }\n\n    @RefreshScope\n    @RestController\n    class TestController {\n\n        @Value(\"${com.didispace.message}\")\n        private String message;\n\n        @GetMapping(\"/test\")\n        public String test() {\n            return message;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "3-Edgware/config-client/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=config-client\nserver.port=10025\n\nspring.cloud.config.uri=http://localhost:10020/\nspring.cloud.config.profile=stage\nspring.cloud.config.label=master\n\nmanagement.security.enabled=false\n"
  },
  {
    "path": "3-Edgware/config-server-db/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>config-server-db</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>config-server-db</name>\n\t<description>Spring Cloud In Action</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.11.RELEASE</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-config-server</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-jdbc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.flywaydb</groupId>\n\t\t\t<artifactId>flyway-core</artifactId>\n\t\t\t<version>5.0.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t<version>5.1.21</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Edgware.SR3</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "3-Edgware/config-server-db/src/main/java/com/didispace/config/server/db/ConfigServerBootstrap.java",
    "content": "package com.didispace.config.server.db;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n@EnableConfigServer\n@SpringBootApplication\npublic class ConfigServerBootstrap {\n\n    public static void main(String[] args) {\n        ApplicationContext context = SpringApplication.run(ConfigServerBootstrap.class);\n\n        JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);\n        jdbcTemplate.execute(\"delete from properties\");\n        jdbcTemplate.execute(\"INSERT INTO properties VALUES(1, 'com.didispace.message', 'test-stage-master', 'config-client', 'stage', 'master')\");\n        jdbcTemplate.execute(\"INSERT INTO properties VALUES(2, 'com.didispace.message', 'test-online-master', 'config-client', 'online', 'master')\");\n        jdbcTemplate.execute(\"INSERT INTO properties VALUES(3, 'com.didispace.message', 'test-online-develop', 'config-client', 'online', 'develop')\");\n        jdbcTemplate.execute(\"INSERT INTO properties VALUES(4, 'com.didispace.message', 'hello-online-master', 'hello-service', 'online', 'master')\");\n        jdbcTemplate.execute(\"INSERT INTO properties VALUES(5, 'com.didispace.message', 'hello-online-develop', 'hello-service', 'online', 'develop')\");\n\n    }\n\n    // 1. 直接访问config-server-db获取获取信息\n\n    // curl http://localhost:10020/config-client/stage/\n    // {\n    //    \"name\": \"config-client\",\n    //    \"profiles\": [\n    //        \"stage\"\n    //    ],\n    //    \"label\": null,\n    //    \"version\": null,\n    //    \"state\": null,\n    //    \"propertySources\": [\n    //        {\n    //            \"name\": \"config-client-stage\",\n    //            \"source\": {\n    //                \"com.didispace.message\": \"test-stage-master\"\n    //            }\n    //        }\n    //    ]\n    //}\n\n    // curl http://localhost:10020/hello-service/stage/develop\n    // {\n    //    \"name\": \"hello-service\",\n    //    \"profiles\": [\n    //        \"online\"\n    //    ],\n    //    \"label\": \"develop\",\n    //    \"version\": null,\n    //    \"state\": null,\n    //    \"propertySources\": [\n    //        {\n    //            \"name\": \"hello-service-online\",\n    //            \"source\": {\n    //                \"com.didispace.message\": \"hello-online-develop\"\n    //            }\n    //        }\n    //    ]\n    //}\n\n    // 2. 访问客户端获取数据\n    // curl http://localhost:10025/test\n    // test-stage-master\n\n    // 3. 数据库中修改配置，刷新配置\n    // curl -X POST http://localhost:10025/refresh\n    // [\"com.didispace.message\"]\n\n    // 4. 验证配置\n    // curl http://localhost:10025/test\n    // test-stage-master-xxx\n\n\n\n}\n"
  },
  {
    "path": "3-Edgware/config-server-db/src/main/resources/application.properties",
    "content": "spring.application.name=config-server-db\nserver.port=10020\n\nspring.profiles.active=jdbc\n\nspring.cloud.config.server.jdbc.sql=SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?\n\nspring.datasource.url=jdbc:mysql://localhost:3306/config-server-db\nspring.datasource.username=root\nspring.datasource.password=\nspring.datasource.driver-class-name=com.mysql.jdbc.Driver\n\nflyway.locations=/schema\n"
  },
  {
    "path": "3-Edgware/config-server-db/src/main/resources/schema/V1__Base_version.sql",
    "content": "CREATE TABLE `properties` (\n  `id` int(11) NOT NULL,\n  `key` varchar(50) NOT NULL,\n  `value` varchar(500) NOT NULL,\n  `application` varchar(50) NOT NULL,\n  `profile` varchar(50) NOT NULL,\n  `label` varchar(50) NOT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n"
  },
  {
    "path": "3-Edgware/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>spring-cloud-edgware</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <modules>\n\n        <!--db存储的配置中心与客户端-->\n        <module>config-server-db</module>\n        <module>config-client</module>\n        \n\n    </modules>\n\n</project>"
  },
  {
    "path": "4-Finchley/README.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 我的博客：http://blog.didispace.com\n- **我的小密圈（深度交流与问答）：https://t.xiaomiquan.com/zfEiY3v**\n- GitHub：https://github.com/dyc87112/SpringCloud-Learning\n- Gitee：https://gitee.com/didispace/SpringCloud-Learning\n- Spring For All社区：http://www.spring4all.com/\n- Spring Boot基础教程：http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- Spring Cloud基础教程：http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/\n- **公益调试Eureka：http://eureka.didispace.com**\n\n**优惠云服务推荐**\n\n- [阿里云：ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o)\n- [腾讯云：3年时长最低265元/年](https://url.cn/5iF8JP2)\n\n## 教程列表\n\n### 《Spring Cloud构建微服务架构》系列博文\n\n#### Finchley版\n\n本系列主要补充之前版本新增或是变动的主要内容，基础使用依然可以参考Dalston版教程\n\n- [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/)\n\n##### Spring Cloud Aliabab专题\n\n- [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/)\n- [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/)\n- [Spring Cloud Alibaba到底坑不坑？](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/)\n\n*注册中心与配置中心：Nacos*\n\n- [Spring Cloud Alibaba基础教程：使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/)\n- [Spring Cloud Alibaba基础教程：Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/)\n- [Spring Cloud Alibaba基础教程：支持的几种服务消费方式（RestTemplate、WebClient、Feign）](http://blog.didispace.com/spring-cloud-alibaba-2/)\n- [Spring Cloud Alibaba基础教程：使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/)\n- [Spring Cloud Alibaba基础教程：Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/)\n- [Spring Cloud Alibaba基础教程：Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/)\n\n*分布式流量防卫兵：Sentinel*\n\n- [Spring Cloud Alibaba基础教程：使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/)\n- [Spring Cloud Alibaba基础教程：@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/)\n\n*国内使用最多的RPC框架整合：Dubbo*\n\n- [Spring Cloud Alibaba基础教程：与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/)\n\n##### Spring Cloud Stream专题补充\n\n- [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1)\n- [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6)\n- [Spring Cloud Stream使用延迟消息实现定时任务（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-7)\n- [Spring Cloud Stream消费失败后的处理策略（一）：自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2)\n- [Spring Cloud Stream消费失败后的处理策略（二）：自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3)\n- [Spring Cloud Stream消费失败后的处理策略（三）：使用DLQ队列（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-4)\n- [Spring Cloud Stream消费失败后的处理策略（四）：重新入队（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-5)\n\n#### Edgware版\n\n> 本系列主要是对Dalston版的补充，包含Edgware版的主要新增或变动的内容，对于Spring Cloud的基础使用依然建议参考Dalston版教程\n\n- [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1)\n\n#### Dalston版\n\n- [服务注册与发现（Eureka、Consul）](http://blog.didispace.com/spring-cloud-starter-dalston-1/)\n- [服务消费者（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/)\n- [服务消费者（Ribbon）](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/)\n- [服务消费者（Feign）](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/)\n- [服务消费者（Feign）传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/)\n- [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3)\n- [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1)\n- [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2)\n- [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3)\n- [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/)\n- [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/)\n- [服务网关（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/)\n- [服务网关（路由配置）](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/)\n- [服务网关（过滤器）](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/)\n- [服务网关（API文档汇总）](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/)\n- [消息驱动的微服务（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/)\n- [消息驱动的微服务（核心概念）](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/)\n- [消息驱动的微服务（消费组）](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/)\n- [消息驱动的微服务（消费组案例：解决消息重复消费）](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/)\n- [消息驱动的微服务（消息分区）](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/)\n- [分布式服务跟踪（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/)\n- [分布式服务跟踪（跟踪原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/)\n- [分布式服务跟踪（整合logstash）](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/)\n- [分布式服务跟踪（整合zipkin）](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/)\n- [分布式服务跟踪（收集原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/)\n- [分布式服务跟踪（抽样收集）](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/)\n\n#### Brixton版\n\n- 1-Brixton版教程示例/chapter1-1-1：[Spring Cloud构建微服务架构（一）服务注册与发现](http://blog.didispace.com/springcloud1/)\n- 1-Brixton版教程示例/chapter1-1-2：[Spring Cloud构建微服务架构（二）服务消费者](http://blog.didispace.com/springcloud2/)\n- 1-Brixton版教程示例/chapter1-1-3：[Spring Cloud构建微服务架构（三）断路器](http://blog.didispace.com/springcloud3/)\n- 1-Brixton版教程示例/chapter1-1-4：[Spring Cloud构建微服务架构（四）分布式配置中心](http://blog.didispace.com/springcloud4/)\n- 1-Brixton版教程示例/chapter1-1-8：[Spring Cloud构建微服务架构（四）分布式配置中心（续）](http://blog.didispace.com/springcloud4-2/)\n- 1-Brixton版教程示例/chapter1-1-5：[Spring Cloud构建微服务架构（五）服务网关](http://blog.didispace.com/springcloud5/)\n- 1-Brixton版教程示例/chapter1-1-6：[Spring Cloud构建微服务架构（六）高可用服务注册中心](http://blog.didispace.com/springcloud6/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Rabbit)](http://blog.didispace.com/springcloud7/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/)\n\n### 《Spring Cloud源码分析》系列博文\n\n- [Spring Cloud源码分析（一）Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（四）Zuul：核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/)\n- 未完待续\n\n### 《Spring Cloud实战小贴士》系列博文\n\n- [Spring Cloud实战小贴士：版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/)\n- [Spring Cloud实战小贴士：随机端口](http://blog.didispace.com/spring-cloud-tips-2/)\n- [Spring Cloud实战小贴士：健康检查](http://blog.didispace.com/spring-cloud-tips-3/)\n- [Spring Cloud实战小贴士：Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（一）](http://blog.didispace.com/spring-cloud-zuul-exception/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（二）](http://blog.didispace.com/spring-cloud-zuul-exception-2/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（三）【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/)\n- [Spring Cloud实战小贴士：Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/)\n- [Spring Cloud实战小贴士：Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/)\n- [Spring Cloud实战小贴士：Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/)\n- [Spring Cloud实战小贴士：Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/)\n\n#### 其他文章\n\n- [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/)\n- [为Spring Cloud Ribbon配置请求重试（Camden.SR2+）](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/)\n- [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/)\n- [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/)\n- [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/)\n- [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/)\n- [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/)\n\n## 我的公众号\n\n![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/)\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)\n"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-dubbo-api</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-api/src/main/java/com/didispace/alibaba/dubbo/api/HelloService.java",
    "content": "package com.didispace.alibaba.dubbo.api;\n\n/**\n * Created by 程序猿DD/翟永超 on 2019/8/5.\n * <p>\n * Blog: http://blog.didispace.com/\n * Github: https://github.com/dyc87112/\n */\npublic interface HelloService {\n\n    String hello(String name);\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-dubbo-client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.didispace</groupId>\n            <artifactId>alibaba-dubbo-api</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <!--<groupId>com.alibaba.cloud</groupId>-->\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-client/src/main/java/com/didispace/alibaba/dubbo/client/DubboClientApplication.java",
    "content": "package com.didispace.alibaba.dubbo.client;\n\nimport com.didispace.alibaba.dubbo.api.HelloService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class DubboClientApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DubboClientApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Reference\n        HelloService helloService;\n\n        @GetMapping(\"/test\")\n        public String test() {\n            return helloService.hello(\"didispace.com\");\n        }\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-client/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-dubbo-client\nserver.port=8002\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=-1\ndubbo.registry.address=spring-cloud://localhost\ndubbo.cloud.subscribed-services=alibaba-dubbo-server"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-dubbo-server</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.didispace</groupId>\n            <artifactId>alibaba-dubbo-api</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <!--<groupId>com.alibaba.cloud</groupId>-->\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-server/src/main/java/com/didispace/alibaba/dubbo/server/DubboServerApplication.java",
    "content": "package com.didispace.alibaba.dubbo.server;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class DubboServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DubboServerApplication.class, args);\n    }\n\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-server/src/main/java/com/didispace/alibaba/dubbo/server/HelloServiceImpl.java",
    "content": "package com.didispace.alibaba.dubbo.server;\n\nimport com.didispace.alibaba.dubbo.api.HelloService;\nimport org.apache.dubbo.config.annotation.Service;\n\n// 注意：是 org.apache.dubbo.config.annotation.Service 注解\n@Service\npublic class HelloServiceImpl implements HelloService {\n\n    @Override\n    public String hello(String name) {\n        return \"hello \" + name;\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-dubbo-server/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-dubbo-server\nserver.port=8001\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n\n# 指定 Dubbo 服务实现类的扫描基准包\ndubbo.scan.base-packages=com.didispace.alibaba.dubbo.server\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=-1\ndubbo.registry.address=spring-cloud://localhost\n\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-config-client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-config-client/src/main/java/com/didispace/alibaba/nacos/config/client/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.config.client;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    @RefreshScope\n    static class TestController {\n\n        @Value(\"${didispace.title:}\")\n        private String title;\n\n        @GetMapping(\"/test\")\n        public String hello() {\n            return title;\n        }\n\n    }\n\n    /**\n     * 多文件加载的例子使用的验证接口\n     *\n     * blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/\n     */\n    @Slf4j\n    @RestController\n    @RefreshScope\n    static class Test2Controller {\n\n        @Value(\"${didispace.title:}\")\n        private String title;\n        @Value(\"${aaa:}\")\n        private String aaa;\n        @Value(\"${bbb:}\")\n        private String bbb;\n\n        @GetMapping(\"/test2\")\n        public String test2() {\n            return title + \", \" + aaa + \", \" + bbb;\n        }\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-config-client/src/main/resources/bootstrap.properties",
    "content": "spring.application.name=alibaba-nacos-config-client\nserver.port=8001\n\nspring.cloud.nacos.config.server-addr=127.0.0.1:8848\n\n# blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/\n#spring.cloud.nacos.config.prefix=example\n#spring.cloud.nacos.config.file-extension=properties\n#spring.cloud.nacos.config.group=DEFAULT_GROUP\n\n# blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/\n#spring.profiles.active=DEV\n#spring.cloud.nacos.config.group=DEV_GROUP\n#spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a\n\n# blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/\n#spring.cloud.nacos.config.ext-config[0].data-id=actuator.properties\n#spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP\n#spring.cloud.nacos.config.ext-config[0].refresh=true\n#spring.cloud.nacos.config.ext-config[1].data-id=log.properties\n#spring.cloud.nacos.config.ext-config[1].group=DEFAULT_GROUP\n#spring.cloud.nacos.config.ext-config[1].refresh=true\n#spring.cloud.nacos.config.shared-dataids=actuator.properties,log.properties\n#spring.cloud.nacos.config.refreshable-dataids=actuator.properties,log.properties"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-discovery-client-common</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-common/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.discovery.client;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Autowired\n        LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/test\")\n        public String test() {\n            // 通过spring cloud common中的负载均衡接口选取服务提供节点实现接口调用\n            ServiceInstance serviceInstance = loadBalancerClient.choose(\"alibaba-nacos-discovery-server\");\n            String url = serviceInstance.getUri() + \"/hello?name=\" + \"didi\";\n            RestTemplate restTemplate = new RestTemplate();\n            String result = restTemplate.getForObject(url, String.class);\n            return \"Invoke : \" + url + \", return : \" + result;\n        }\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-common/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-nacos-discovery-client-common\nserver.port=9000\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-discovery-client-feign</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-feign/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.discovery.client;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\n@EnableFeignClients\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Autowired\n        Client client;\n\n        @GetMapping(\"/test\")\n        public String test() {\n            String result = client.hello(\"didi\");\n            return \"Return : \" + result;\n        }\n    }\n\n\n    @FeignClient(\"alibaba-nacos-discovery-server\")\n    interface Client {\n\n        @GetMapping(\"/hello\")\n        String hello(@RequestParam(name = \"name\") String name);\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-feign/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-nacos-discovery-client-feign\nserver.port=9000\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-resttemplate/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-discovery-client-resttemplate</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-resttemplate/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.discovery.client;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Autowired\n        RestTemplate restTemplate;\n\n        @GetMapping(\"/test\")\n        public String test() {\n            String result = restTemplate.getForObject(\"http://alibaba-nacos-discovery-server/hello?name=didi\", String.class);\n            return \"Return : \" + result;\n        }\n    }\n\n    @Bean\n    @LoadBalanced\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-resttemplate/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-nacos-discovery-client-resttemplate\nserver.port=9000\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-webclient/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-discovery-client-webclient</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-webclient/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.discovery.client;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactor.core.publisher.Mono;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private WebClient.Builder webClientBuilder;\n\n        @GetMapping(\"/test\")\n        public Mono<String> test() {\n            Mono<String> result = webClientBuilder.build()\n                    .get()\n                    .uri(\"http://alibaba-nacos-discovery-server/hello?name=didi\")\n                    .retrieve()\n                    .bodyToMono(String.class);\n            return result;\n        }\n    }\n\n    @Bean\n    @LoadBalanced\n    public WebClient.Builder loadBalancedWebClientBuilder() {\n        return WebClient.builder();\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-client-webclient/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-nacos-discovery-client-webclient\nserver.port=9000\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-nacos-discovery-server</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-server/src/main/java/com/didispace/alibaba/nacos/discovery/server/TestApplication.java",
    "content": "package com.didispace.alibaba.nacos.discovery.server;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/hello\")\n        public String hello(@RequestParam String name) {\n            log.info(\"invoked name = \" + name);\n            return \"hello \" + name;\n        }\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-nacos-discovery-server/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-nacos-discovery-server\nserver.port=8001\n\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-annotation/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-sentinel-annotation</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java",
    "content": "package com.didispace.alibaba.sentinel;\n\nimport com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\n\n\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Bean\n    public SentinelResourceAspect sentinelResourceAspect() {\n        return new SentinelResourceAspect();\n    }\n\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/service/TestService.java",
    "content": "package com.didispace.alibaba.sentinel.service;\n\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\n\n@Slf4j\n@Service\npublic class TestService {\n\n    // 限流与阻塞处理\n    @SentinelResource(value = \"doSomeThing\", blockHandler = \"exceptionHandler\")\n    public void doSomeThing(String str) {\n        log.info(str);\n    }\n\n    public void exceptionHandler(String str, BlockException ex) {\n        log.error(\"blockHandler：\" + str, ex);\n    }\n\n    // 熔断与降级处理\n    @SentinelResource(value = \"doSomeThing2\", fallback = \"fallbackHandler\")\n    public void doSomeThing2(String str) {\n        log.info(str);\n        throw new RuntimeException(\"发生异常\");\n    }\n\n    public void fallbackHandler(String str) {\n        log.error(\"fallbackHandler：\" + str);\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/web/TestController.java",
    "content": "package com.didispace.alibaba.sentinel.web;\n\nimport com.didispace.alibaba.sentinel.service.TestService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Date;\n\n@Slf4j\n@RestController\npublic class TestController {\n\n    @Autowired\n    private TestService testService;\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        testService.doSomeThing(\"hello \" + new Date());\n        return \"didispace.com\";\n    }\n\n    @GetMapping(\"/hello2\")\n    public String hello2() {\n        testService.doSomeThing2(\"hello2 \" + new Date());\n        return \"didispace.com\";\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-annotation/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-sentinel-annotation\nserver.port=8001\n\n# sentinel dashboard\nspring.cloud.sentinel.transport.dashboard=localhost:8080"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>com.alibaba.csp</groupId>\n        <artifactId>sentinel-parent</artifactId>\n        <version>1.6.0</version>\n    </parent>\n\n    <artifactId>alibaba-sentinel-dashboard-apollo</artifactId>\n    <packaging>jar</packaging>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <spring.boot.version>2.0.5.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-web-servlet</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-logging</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n            <version>1.2.14</version>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-lang</groupId>\n            <artifactId>commons-lang</artifactId>\n            <version>2.6</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n            <version>4.5.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpcore</artifactId>\n            <version>4.4.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpasyncclient</artifactId>\n            <version>4.1.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpcore-nio</artifactId>\n            <version>4.4.6</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n\n        <!-- for Nacos rule publisher sample -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-nacos</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <!-- for Apollo rule publisher sample -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-openapi</artifactId>\n            <version>1.2.0</version>\n            <!--<scope>test</scope>-->\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n        \t<groupId>com.github.stefanbirkner</groupId>\n        \t<artifactId>system-rules</artifactId>\n        \t<version>1.16.1</version>\n        \t<scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <finalName>sentinel-dashboard</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                    <mainClass>com.alibaba.csp.sentinel.dashboard.DashboardApplication</mainClass>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>${maven.compiler.source}</source>\n                    <target>${maven.compiler.target}</target>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <version>${maven.deploy.version}</version>\n                <configuration>\n                    <skip>true</skip>\n                </configuration>\n            </plugin>\n        </plugins>\n\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n\n            <resource>\n                <directory>src/main/webapp/</directory>\n                <excludes>\n                    <exclude>resources/node_modules/**</exclude>\n                </excludes>\n            </resource>\n        </resources>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/DashboardApplication.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard;\n\nimport com.alibaba.csp.sentinel.init.InitExecutor;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Sentinel dashboard application.\n *\n * @author Carpenter Lee\n */\n@SpringBootApplication\npublic class DashboardApplication {\n\n    public static void main(String[] args) {\n        triggerSentinelInit();\n        SpringApplication.run(DashboardApplication.class, args);\n    }\n\n    private static void triggerSentinelInit() {\n        new Thread(() -> InitExecutor.doInit()).start();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\n/**\n * Interface for authentication and authorization.\n *\n * @author Carpenter Lee\n * @since 1.5.0\n */\npublic interface AuthService<R> {\n\n    /**\n     * Get the authentication user.\n     *\n     * @param request the request contains the user information\n     * @return the auth user represent the current user, when the user is illegal, a null value will return.\n     */\n    AuthUser getAuthUser(R request);\n\n    /**\n     * Privilege type.\n     */\n    enum PrivilegeType {\n        /**\n         * Read rule\n         */\n        READ_RULE,\n        /**\n         * Create or modify rule\n         */\n        WRITE_RULE,\n        /**\n         * Delete rule\n         */\n        DELETE_RULE,\n        /**\n         * Read metrics\n         */\n        READ_METRIC,\n        /**\n         * Add machine\n         */\n        ADD_MACHINE,\n        /**\n         * All privileges above are granted.\n         */\n        ALL\n    }\n\n    /**\n     * Represents the current user.\n     */\n    interface AuthUser {\n\n        /**\n         * Query whether current user has the specific privilege to the target, the target\n         * may be an app name or an ip address, or other destination.\n         * <p>\n         * This method will use return value to represent  whether user has the specific\n         * privileges to the target, but to throw a RuntimeException to represent no auth\n         * is also a good way.\n         * </p>\n         *\n         * @param target        the target to check\n         * @param privilegeType the privilege type to check\n         * @return if current user has the specific privileges to the target, return true,\n         * otherwise return false.\n         */\n        boolean authTarget(String target, PrivilegeType privilegeType);\n\n        /**\n         * Check whether current user is a super-user.\n         *\n         * @return if current user is super user return true, else return false.\n         */\n        boolean isSuperUser();\n\n        /**\n         * Get current user's nick name.\n         *\n         * @return current user's nick name.\n         */\n        String getNickName();\n\n        /**\n         * Get current user's login name.\n         *\n         * @return current user's login name.\n         */\n        String getLoginName();\n\n        /**\n         * Get current user's ID.\n         *\n         * @return ID of current user\n         */\n        String getId();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * A fake AuthService implementation, which will pass all user auth checking.\n *\n * @author Carpenter Lee\n * @since 1.5.0\n */\n@Component\npublic class FakeAuthServiceImpl implements AuthService<HttpServletRequest> {\n\n    @Override\n    public AuthUser getAuthUser(HttpServletRequest request) {\n        return new AuthUserImpl();\n    }\n\n    static final class AuthUserImpl implements AuthUser {\n\n        @Override\n        public boolean authTarget(String target, PrivilegeType privilegeType) {\n            // fake implementation, always return true\n            return true;\n        }\n\n        @Override\n        public boolean isSuperUser() {\n            // fake implementation, always return true\n            return true;\n        }\n\n        @Override\n        public String getNickName() {\n            return \"FAKE_NICK_NAME\";\n        }\n\n        @Override\n        public String getLoginName() {\n            return \"FAKE_LOGIN_NAME\";\n        }\n\n        @Override\n        public String getId() {\n            return \"FAKE_EMP_ID\";\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\n/**\n * @author cdfive\n * @since 1.6.0\n */\n@Primary\n@Component\npublic class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> {\n\n    public static final String WEB_SESSTION_KEY = \"session_sentinel_admin\";\n\n    @Override\n    public AuthUser getAuthUser(HttpServletRequest request) {\n        HttpSession session = request.getSession();\n        Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY);\n        if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) {\n            return (AuthUser) sentinelUserObj;\n        }\n\n        return null;\n    }\n\n    public static final class SimpleWebAuthUserImpl implements AuthUser {\n\n        private String username;\n\n        public SimpleWebAuthUserImpl(String username) {\n            this.username = username;\n        }\n\n        @Override\n        public boolean authTarget(String target, PrivilegeType privilegeType) {\n            return true;\n        }\n\n        @Override\n        public boolean isSuperUser() {\n            return true;\n        }\n\n        @Override\n        public String getNickName() {\n            return username;\n        }\n\n        @Override\n        public String getLoginName() {\n            return username;\n        }\n\n        @Override\n        public String getId() {\n            return username;\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandFailedException.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\n/**\n * @author Eric Zhao\n */\npublic class CommandFailedException extends RuntimeException {\n\n    public CommandFailedException() {}\n\n    public CommandFailedException(String message) {\n        super(message);\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandNotFoundException.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class CommandNotFoundException extends Exception {\n\n    public CommandNotFoundException() { }\n\n    public CommandNotFoundException(String message) {\n        super(message);\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.command.CommandConstants;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\nimport com.alibaba.csp.sentinel.dashboard.util.AsyncUtils;\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;\nimport com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;\nimport com.alibaba.csp.sentinel.slots.system.SystemRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.concurrent.FutureCallback;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.client.DefaultRedirectStrategy;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.apache.http.impl.nio.reactor.IOReactorConfig;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.apache.http.util.EntityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.lang.Nullable;\nimport org.springframework.stereotype.Component;\n\n/**\n * Communicate with Sentinel client.\n *\n * @author leyou\n */\n@Component\npublic class SentinelApiClient {\n    private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);\n\n    private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());\n\n    private static final String RESOURCE_URL_PATH = \"jsonTree\";\n    private static final String CLUSTER_NODE_PATH = \"clusterNode\";\n    private static final String GET_RULES_PATH = \"getRules\";\n    private static final String SET_RULES_PATH = \"setRules\";\n    private static final String GET_PARAM_RULE_PATH = \"getParamFlowRules\";\n    private static final String SET_PARAM_RULE_PATH = \"setParamFlowRules\";\n\n    private static final String FETCH_CLUSTER_MODE_PATH = \"getClusterMode\";\n    private static final String MODIFY_CLUSTER_MODE_PATH = \"setClusterMode\";\n    private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = \"cluster/client/fetchConfig\";\n    private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = \"cluster/client/modifyConfig\";\n\n    private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = \"cluster/server/fetchConfig\";\n    private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = \"cluster/server/info\";\n\n    private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = \"cluster/server/modifyTransportConfig\";\n    private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = \"cluster/server/modifyFlowConfig\";\n    private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = \"cluster/server/modifyNamespaceSet\";\n\n    private static final String FLOW_RULE_TYPE = \"flow\";\n    private static final String DEGRADE_RULE_TYPE = \"degrade\";\n    private static final String SYSTEM_RULE_TYPE = \"system\";\n    private static final String AUTHORITY_TYPE = \"authority\";\n\n    private CloseableHttpAsyncClient httpClient;\n\n    private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0);\n    \n    @Autowired\n    private AppManagement appManagement;\n\n    public SentinelApiClient() {\n        IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000)\n            .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();\n        httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {\n            @Override\n            protected boolean isRedirectable(final String method) {\n                return false;\n            }\n        }).setMaxConnTotal(4000).setMaxConnPerRoute(1000).setDefaultIOReactorConfig(ioConfig).build();\n        httpClient.start();\n    }\n\n    private boolean isSuccess(int statusCode) {\n        return statusCode >= 200 && statusCode < 300;\n    }\n    \n    private boolean isCommandNotFound(int statusCode, String body) {\n        return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);\n    }\n    \n    private StringBuilder queryString(Map<String, String> params) {\n        StringBuilder queryStringBuilder = new StringBuilder();\n        for (Entry<String, String> entry : params.entrySet()) {\n            if (StringUtil.isEmpty(entry.getValue())) {\n                continue;\n            }\n            String name = urlEncode(entry.getKey());\n            String value = urlEncode(entry.getValue());\n            if (name != null && value != null) {\n                if (queryStringBuilder.length() > 0) {\n                    queryStringBuilder.append('&');\n                }\n                queryStringBuilder.append(name).append('=').append(value);\n            }\n        }\n        return queryStringBuilder;\n    }\n    \n    private HttpUriRequest postRequest(String url, Map<String, String> params) {\n        HttpPost httpPost = new HttpPost(url);\n        if (params != null && params.size() > 0) {\n            List<NameValuePair> list = new ArrayList<>(params.size());\n            for (Entry<String, String> entry : params.entrySet()) {\n                list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));\n            }\n            try {\n                httpPost.setEntity(new UrlEncodedFormEntity(list));\n            } catch (UnsupportedEncodingException e) {\n                logger.warn(\"httpPostContent encode entity error: {}\", params, e);\n                return null;\n            }\n        }\n        return httpPost;\n    }\n    \n    private String urlEncode(String str) {\n        try {\n            return URLEncoder.encode(str, DEFAULT_CHARSET.name());\n        } catch (UnsupportedEncodingException e) {\n            logger.info(\"encode string error: {}\", str, e);\n            return null;\n        }\n    }\n    \n    private String getBody(HttpResponse response) throws Exception {\n        Charset charset = null;\n        try {\n            String contentTypeStr = response.getFirstHeader(\"Content-type\").getValue();\n            if (StringUtil.isNotEmpty(contentTypeStr)) {\n                ContentType contentType = ContentType.parse(contentTypeStr);\n                charset = contentType.getCharset();\n            }\n        } catch (Exception ignore) {\n        }\n        return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);\n    }\n    \n    /**\n     * With no param\n     * \n     * @param ip\n     * @param port\n     * @param api\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String ip, int port, String api, boolean useHttpPost) {\n        return executeCommand(ip, port, api, null, useHttpPost);\n    }\n    \n    /**\n     * No app specified, force to GET\n     * \n     * @param ip\n     * @param port\n     * @param api\n     * @param params\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String ip, int port, String api, Map<String, String> params, boolean useHttpPost) {\n        return executeCommand(null, ip, port, api, params, useHttpPost);\n    }\n\n    /**\n     * Prefer to execute request using POST\n     * \n     * @param app\n     * @param ip\n     * @param port\n     * @param api\n     * @param params\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String app, String ip, int port, String api, Map<String, String> params, boolean useHttpPost) {\n        CompletableFuture<String> future = new CompletableFuture<>();\n        if (StringUtil.isBlank(ip) || StringUtil.isBlank(api)) {\n            future.completeExceptionally(new IllegalArgumentException(\"Bad URL or command name\"));\n            return future;\n        }\n        StringBuilder urlBuilder = new StringBuilder();\n        urlBuilder.append(\"http://\");\n        urlBuilder.append(ip).append(':').append(port).append('/').append(api);\n        if (params == null) {\n            params = Collections.emptyMap();\n        }\n        boolean supportPost = StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version160)))\n                .orElse(false);\n        if (!useHttpPost || !supportPost) {\n            // Using GET in older versions, append parameters after url\n            if (!params.isEmpty()) {\n                if (urlBuilder.indexOf(\"?\") == -1) {\n                    urlBuilder.append('?');\n                } else {\n                    urlBuilder.append('&');\n                }\n                urlBuilder.append(queryString(params));\n            }\n            return executeCommand(new HttpGet(urlBuilder.toString()));\n        } else {\n            // Using POST\n            return executeCommand(postRequest(urlBuilder.toString(), params));\n        }\n    }\n    \n    private CompletableFuture<String> executeCommand(HttpUriRequest request) {\n        CompletableFuture<String> future = new CompletableFuture<>();\n        httpClient.execute(request, new FutureCallback<HttpResponse>() {\n            @Override\n            public void completed(final HttpResponse response) {\n                int statusCode = response.getStatusLine().getStatusCode();\n                try {\n                    String value = getBody(response);\n                    if (isSuccess(statusCode)) {\n                        future.complete(value);\n                    } else {\n                        if (isCommandNotFound(statusCode, value)) {\n                            future.completeExceptionally(new CommandNotFoundException(request.getURI().getPath()));\n                        } else {\n                            future.completeExceptionally(new CommandFailedException(value));\n                        }\n                    }\n\n                } catch (Exception ex) {\n                    future.completeExceptionally(ex);\n                    logger.error(\"HTTP request failed: {}\", request.getURI().toString(), ex);\n                }\n            }\n\n            @Override\n            public void failed(final Exception ex) {\n                future.completeExceptionally(ex);\n                logger.error(\"HTTP request failed: {}\", request.getURI().toString(), ex);\n            }\n\n            @Override\n            public void cancelled() {\n                future.complete(null);\n            }\n        });\n        return future;\n    }\n    \n    public void close() throws Exception {\n        httpClient.close();\n    }\n    \n    @Nullable\n    private <T> CompletableFuture<List<T>> fetchItemsAsync(String ip, int port, String api, String type, Class<T> ruleType) {\n        AssertUtil.notEmpty(ip, \"Bad machine IP\");\n        AssertUtil.isTrue(port > 0, \"Bad machine port\");\n        Map<String, String> params = null;\n        if (StringUtil.isNotEmpty(type)) {\n            params = new HashMap<>(1);\n            params.put(\"type\", type);\n        }\n        return executeCommand(ip, port, api, params, false)\n                .thenApply(json -> JSON.parseArray(json, ruleType));\n    }\n    \n    @Nullable\n    private <T> List<T> fetchItems(String ip, int port, String api, String type, Class<T> ruleType) {\n        try {\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            Map<String, String> params = null;\n            if (StringUtil.isNotEmpty(type)) {\n                params = new HashMap<>(1);\n                params.put(\"type\", type);\n            }\n            return fetchItemsAsync(ip, port, api, type, ruleType).get();\n        } catch (InterruptedException | ExecutionException e) {\n            logger.error(\"Error when fetching items from api: {} -> {}\", api, type, e);\n            return null;\n        } catch (Exception e) {\n            logger.error(\"Error when fetching items: {} -> {}\", api, type, e);\n            return null;\n        }\n    }\n    \n    private <T extends Rule> List<T> fetchRules(String ip, int port, String type, Class<T> ruleType) {\n        return fetchItems(ip, port, GET_RULES_PATH, type, ruleType);\n    }\n    \n    private boolean setRules(String app, String ip, int port, String type, List<? extends RuleEntity> entities) {\n        if (entities == null) {\n            return true;\n        }\n        try {\n            AssertUtil.notEmpty(app, \"Bad app name\");\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            String data = JSON.toJSONString(\n                    entities.stream().map(r -> r.toRule()).collect(Collectors.toList()));\n            Map<String, String> params = new HashMap<>(2);\n            params.put(\"type\", type);\n            params.put(\"data\", data);\n            String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get();\n            logger.info(\"setRules: {}\", result);\n            return true;\n        } catch (InterruptedException | ExecutionException e) {\n            logger.warn(\"setRules api failed: {}\", type, e);\n            return false;\n        } catch (Exception e) {\n            logger.warn(\"setRules failed\", e);\n            return false;\n        }\n    }\n\n    public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {\n        return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class);\n    }\n\n    /**\n     * Fetch cluster node.\n     *\n     * @param ip          ip to fetch\n     * @param port        port of the ip\n     * @param includeZero whether zero value should in the result list.\n     * @return\n     */\n    public List<NodeVo> fetchClusterNodeOfMachine(String ip, int port, boolean includeZero) {\n        String type = \"noZero\";\n        if (includeZero) {\n            type = \"zero\";\n        }\n        return fetchItems(ip, port, CLUSTER_NODE_PATH, type, NodeVo.class);\n    }\n\n    public List<FlowRuleEntity> fetchFlowRuleOfMachine(String app, String ip, int port) {\n        List<FlowRule> rules = fetchRules(ip, port, FLOW_RULE_TYPE, FlowRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    public List<DegradeRuleEntity> fetchDegradeRuleOfMachine(String app, String ip, int port) {\n        List<DegradeRule> rules = fetchRules(ip, port, DEGRADE_RULE_TYPE, DegradeRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    public List<SystemRuleEntity> fetchSystemRuleOfMachine(String app, String ip, int port) {\n        List<SystemRule> rules = fetchRules(ip, port, SYSTEM_RULE_TYPE, SystemRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Fetch all parameter flow rules from provided machine.\n     *\n     * @param app  application name\n     * @param ip   machine client IP\n     * @param port machine client port\n     * @return all retrieved parameter flow rules\n     * @since 0.2.1\n     */\n    public CompletableFuture<List<ParamFlowRuleEntity>> fetchParamFlowRulesOfMachine(String app, String ip, int port) {\n        try {\n            AssertUtil.notEmpty(app, \"Bad app name\");\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            return fetchItemsAsync(ip, port, GET_PARAM_RULE_PATH, null, ParamFlowRule.class)\n                .thenApply(rules -> rules.stream()\n                    .map(e -> ParamFlowRuleEntity.fromAuthorityRule(app, ip, port, e))\n                    .collect(Collectors.toList())\n                );\n        } catch (Exception e) {\n            logger.error(\"Error when fetching parameter flow rules\", e);\n            return AsyncUtils.newFailedFuture(e);\n        }\n    }\n\n    /**\n     * Fetch all authority rules from provided machine.\n     *\n     * @param app  application name\n     * @param ip   machine client IP\n     * @param port machine client port\n     * @return all retrieved authority rules\n     * @since 0.2.1\n     */\n    public List<AuthorityRuleEntity> fetchAuthorityRulesOfMachine(String app, String ip, int port) {\n        AssertUtil.notEmpty(app, \"Bad app name\");\n        AssertUtil.notEmpty(ip, \"Bad machine IP\");\n        AssertUtil.isTrue(port > 0, \"Bad machine port\");\n        Map<String, String> params = new HashMap<>(1);\n        params.put(\"type\", AUTHORITY_TYPE);\n        List<AuthorityRule> rules = fetchRules(ip, port, AUTHORITY_TYPE, AuthorityRule.class);\n        return Optional.ofNullable(rules).map(r -> r.stream()\n                    .map(e -> AuthorityRuleEntity.fromAuthorityRule(app, ip, port, e))\n                    .collect(Collectors.toList())\n                ).orElse(null);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setFlowRuleOfMachine(String app, String ip, int port, List<FlowRuleEntity> rules) {\n        return setRules(app, ip, port, FLOW_RULE_TYPE, rules);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setDegradeRuleOfMachine(String app, String ip, int port, List<DegradeRuleEntity> rules) {\n        return setRules(app, ip, port, DEGRADE_RULE_TYPE, rules);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setSystemRuleOfMachine(String app, String ip, int port, List<SystemRuleEntity> rules) {\n        return setRules(app, ip, port, SYSTEM_RULE_TYPE, rules);\n    }\n\n    public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List<AuthorityRuleEntity> rules) {\n        return setRules(app, ip, port, AUTHORITY_TYPE, rules);\n    }\n\n    public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) {\n        if (rules == null) {\n            return CompletableFuture.completedFuture(null);\n        }\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            String data = JSON.toJSONString(\n                rules.stream().map(ParamFlowRuleEntity::getRule).collect(Collectors.toList())\n            );\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", data);\n            return executeCommand(app, ip, port, SET_PARAM_RULE_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Push parameter flow rules to client failed: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when setting parameter flow rule\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    // Cluster related\n\n    public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_MODE_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterStateSimpleEntity.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster mode\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterMode(String ip, int port, int mode) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"mode\", String.valueOf(mode));\n            return executeCommand(ip, port, MODIFY_CLUSTER_MODE_PATH, params, false)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster mode: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster mode\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<ClusterClientInfoVO> fetchClusterClientInfoAndConfig(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_CLIENT_CONFIG_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster client config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterClientConfig(String app, String ip, int port, ClusterClientConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(config));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_CLIENT_CONFIG_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster client config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster client config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerFlowConfig(String app, String ip, int port, ServerFlowConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(config));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server flow config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server flow config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerTransportConfig(String app, String ip, int port, ServerTransportConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(2);\n            params.put(\"port\", config.getPort().toString());\n            params.put(\"idleSeconds\", config.getIdleSeconds().toString());\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, params, false)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server transport config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server transport config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerNamespaceSet(String app, String ip, int port, Set<String> set) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(set));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server NamespaceSet\", e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server NamespaceSet\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<ClusterServerStateVO> fetchClusterServerBasicInfo(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_SERVER_BASIC_INFO_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterServerStateVO.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster sever all config and basic info\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.lang.NonNull;\n\n/**\n * <p>Dashboard local config support.</p>\n * <p>\n * Dashboard supports configuration loading by several ways by order:<br>\n * 1. System.properties<br>\n * 2. Env\n * </p>\n *\n * @author jason\n * @since 1.5.0\n */\npublic class DashboardConfig {\n\n    public static final int DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS = 60_000;\n\n    /**\n     * Login username\n     */\n    public static final String CONFIG_AUTH_USERNAME = \"sentinel.dashboard.auth.username\";\n\n    /**\n     * Login password\n     */\n    public static final String CONFIG_AUTH_PASSWORD = \"sentinel.dashboard.auth.password\";\n\n    /**\n     * Hide application name in sidebar when it has no healthy machines after specific period in millisecond.\n     */\n    public static final String CONFIG_HIDE_APP_NO_MACHINE_MILLIS = \"sentinel.dashboard.app.hideAppNoMachineMillis\";\n    /**\n     * Remove application when it has no healthy machines after specific period in millisecond.\n     */\n    public static final String CONFIG_REMOVE_APP_NO_MACHINE_MILLIS = \"sentinel.dashboard.removeAppNoMachineMillis\";\n    /**\n     * Timeout\n     */\n    public static final String CONFIG_UNHEALTHY_MACHINE_MILLIS = \"sentinel.dashboard.unhealthyMachineMillis\";\n    /**\n     * Auto remove unhealthy machine after specific period in millisecond.\n     */\n    public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = \"sentinel.dashboard.autoRemoveMachineMillis\";\n\n    private static final ConcurrentMap<String, Object> cacheMap = new ConcurrentHashMap<>();\n    \n    @NonNull\n    private static String getConfig(String name) {\n        // env\n        String val = System.getenv(name);\n        if (StringUtils.isNotEmpty(val)) {\n            return val;\n        }\n        // properties\n        val = System.getProperty(name);\n        if (StringUtils.isNotEmpty(val)) {\n            return val;\n        }\n        return \"\";\n    }\n\n    protected static String getConfigStr(String name) {\n        if (cacheMap.containsKey(name)) {\n            return (String) cacheMap.get(name);\n        }\n\n        String val = getConfig(name);\n\n        if (StringUtils.isBlank(val)) {\n            return null;\n        }\n\n        cacheMap.put(name, val);\n        return val;\n    }\n\n    protected static int getConfigInt(String name, int defaultVal, int minVal) {\n        if (cacheMap.containsKey(name)) {\n            return (int)cacheMap.get(name);\n        }\n        int val = NumberUtils.toInt(getConfig(name));\n        if (val == 0) {\n            val = defaultVal;\n        } else if (val < minVal) {\n            val = minVal;\n        }\n        cacheMap.put(name, val);\n        return val;\n    }\n\n    public static String getAuthUsername() {\n        return getConfigStr(CONFIG_AUTH_USERNAME);\n    }\n\n    public static String getAuthPassword() {\n        return getConfigStr(CONFIG_AUTH_PASSWORD);\n    }\n\n    public static int getHideAppNoMachineMillis() {\n        return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000);\n    }\n    \n    public static int getRemoveAppNoMachineMillis() {\n        return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000);\n    }\n    \n    public static int getAutoRemoveMachineMillis() {\n        return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000);\n    }\n    \n    public static int getUnhealthyMachineMillis() {\n        return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS, 30000);\n    }\n    \n    public static void clearCache() {\n        cacheMap.clear();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;\nimport com.alibaba.csp.sentinel.dashboard.filter.AuthFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.ViewControllerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport javax.servlet.Filter;\n\n/**\n * @author leyou\n */\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    private final Logger logger = LoggerFactory.getLogger(WebConfig.class);\n\n    @Autowired\n    private AuthFilter authFilter;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        registry.addResourceHandler(\"/**\").addResourceLocations(\"classpath:/resources/\");\n    }\n\n    @Override\n    public void addViewControllers(ViewControllerRegistry registry) {\n        registry.addViewController(\"/\").setViewName(\"forward:/index.htm\");\n    }\n\n    /**\n     * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel\n     * for Web application.\n     */\n    @Bean\n    public FilterRegistrationBean sentinelFilterRegistration() {\n        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();\n        registration.setFilter(new CommonFilter());\n        registration.addUrlPatterns(\"/*\");\n        registration.setName(\"sentinelFilter\");\n        registration.setOrder(1);\n\n        logger.info(\"Sentinel servlet CommonFilter registered\");\n\n        return registration;\n    }\n\n    @Bean\n    public FilterRegistrationBean authenticationFilterRegistration() {\n        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();\n        registration.setFilter(authFilter);\n        registration.addUrlPatterns(\"/*\");\n        registration.setName(\"authenticationFilter\");\n        registration.setOrder(0);\n        return registration;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.MachineInfoVo;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Carpenter Lee\n */\n@RestController\n@RequestMapping(value = \"/app\")\npublic class AppController {\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @GetMapping(\"/names.json\")\n    public Result<List<String>> queryApps(HttpServletRequest request) {\n        return Result.ofSuccess(appManagement.getAppNames());\n    }\n\n    @GetMapping(\"/briefinfos.json\")\n    public Result<List<AppInfo>> queryAppInfos(HttpServletRequest request) {\n        List<AppInfo> list = new ArrayList<>(appManagement.getBriefApps());\n        Collections.sort(list, Comparator.comparing(AppInfo::getApp));\n        return Result.ofSuccess(list);\n    }\n\n    @GetMapping(value = \"/{app}/machines.json\")\n    public Result<List<MachineInfoVo>> getMachinesByApp(@PathVariable(\"app\") String app) {\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null) {\n            return Result.ofSuccess(null);\n        }\n        List<MachineInfo> list = new ArrayList<>(appInfo.getMachines());\n        Collections.sort(list, (o1, o2) -> {\n            int t = o1.getApp().compareTo(o2.getApp());\n            if (t != 0) {\n                return t;\n            }\n            t = o1.getIp().compareTo(o2.getIp());\n            if (t != 0) {\n                return t;\n            }\n            return o1.getPort().compareTo(o2.getPort());\n        });\n        return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list));\n    }\n    \n    @RequestMapping(value = \"/{app}/machine/remove.json\")\n    public Result<String> removeMachineById(\n            @PathVariable(\"app\") String app,\n            @RequestParam(name = \"ip\") String ip,\n            @RequestParam(name = \"port\") int port) {\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null) {\n            return Result.ofSuccess(null);\n        }\n        if (appManagement.removeMachine(app, ip, port)) {\n            return Result.ofSuccessMsg(\"success\");\n        } else {\n            return Result.ofFail(1, \"remove failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl;\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * @author cdfive\n * @since 1.6.0\n */\n@RestController\n@RequestMapping(\"/auth\")\npublic class AuthController {\n\n    private static Logger LOGGER = LoggerFactory.getLogger(AuthController.class);\n\n    @Value(\"${auth.username:sentinel}\")\n    private String authUsername;\n\n    @Value(\"${auth.password:sentinel}\")\n    private String authPassword;\n\n    @PostMapping(\"/login\")\n    public Result login(HttpServletRequest request, String username, String password) {\n        if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) {\n            authUsername = DashboardConfig.getAuthUsername();\n        }\n\n        if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) {\n            authPassword = DashboardConfig.getAuthPassword();\n        }\n\n        /*\n         * If auth.username or auth.password is blank(set in application.properties or VM arguments),\n         * auth will pass, as the front side validate the input which can't be blank,\n         * so user can input any username or password(both are not blank) to login in that case.\n         */\n        if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username)\n                || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) {\n            LOGGER.error(\"Login failed: Invalid username or password, username=\" + username + \", password=\" + password);\n            return Result.ofFail(-1, \"Invalid username or password\");\n        }\n\n        AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username);\n        request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY, authUser);\n        return Result.ofSuccess(authUser);\n    }\n\n    @RequestMapping(value = \"/logout\", method = RequestMethod.POST)\n    public Result logout(HttpServletRequest request) {\n        request.getSession().invalidate();\n        return Result.ofSuccess(null);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@RestController\n@RequestMapping(value = \"/authority\")\npublic class AuthorityRuleController {\n\n    private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private RuleRepository<AuthorityRuleEntity, Long> repository;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @GetMapping(\"/rules\")\n    public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(HttpServletRequest request,\n                                                                        @RequestParam String app,\n                                                                        @RequestParam String ip,\n                                                                        @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        try {\n            List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying authority rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"bad rule body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null || entity.getPort() <= 0) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (entity.getRule() == null) {\n            return Result.ofFail(-1, \"rule can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource name cannot be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp should be valid\");\n        }\n        if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE\n            && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) {\n            return Result.ofFail(-1, \"Unknown strategy (must be blacklist or whitelist)\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<AuthorityRuleEntity> apiAddAuthorityRule(HttpServletRequest request,\n                                                           @RequestBody AuthorityRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add authority rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"Publish authority rules failed after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(HttpServletRequest request,\n                                                              @PathVariable(\"id\") Long id,\n                                                              @RequestBody AuthorityRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(null);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"Failed to save authority rule\");\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to save authority rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"Publish authority rules failed after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id cannot be null\");\n        }\n        AuthorityRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.error(\"Publish authority rules failed after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author leyou\n */\n@Controller\n@RequestMapping(value = \"/degrade\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class DegradeController {\n\n    private final Logger logger = LoggerFactory.getLogger(DegradeController.class);\n\n    @Autowired\n    private InMemDegradeRuleStore repository;\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @ResponseBody\n    @RequestMapping(\"/rules.json\")\n    public Result<List<DegradeRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"queryApps error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/new.json\")\n    public Result<DegradeRuleEntity> add(HttpServletRequest request,\n                                         String app, String ip, Integer port, String limitApp, String resource,\n                                         Double count, Integer timeWindow, Integer grade) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n\n        if (StringUtil.isBlank(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (StringUtil.isBlank(limitApp)) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(resource)) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (count == null) {\n            return Result.ofFail(-1, \"count can't be null\");\n        }\n        if (timeWindow == null) {\n            return Result.ofFail(-1, \"timeWindow can't be null\");\n        }\n        if (grade == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {\n            return Result.ofFail(-1, \"Invalid grade: \" + grade);\n        }\n        DegradeRuleEntity entity = new DegradeRuleEntity();\n        entity.setApp(app.trim());\n        entity.setIp(ip.trim());\n        entity.setPort(port);\n        entity.setLimitApp(limitApp.trim());\n        entity.setResource(resource.trim());\n        entity.setCount(count);\n        entity.setTimeWindow(timeWindow);\n        entity.setGrade(grade);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"add error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(app, ip, port)) {\n            logger.info(\"publish degrade rules fail after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/save.json\")\n    public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,\n                                                     Long id, String app, String limitApp, String resource,\n                                                     Double count, Integer timeWindow, Integer grade) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        if (grade != null) {\n            if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {\n                return Result.ofFail(-1, \"Invalid grade: \" + grade);\n            }\n        }\n        DegradeRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n\n        if (StringUtil.isNotBlank(limitApp)) {\n            entity.setLimitApp(limitApp.trim());\n        }\n        if (StringUtil.isNotBlank(resource)) {\n            entity.setResource(resource.trim());\n        }\n        if (count != null) {\n            entity.setCount(count);\n        }\n        if (timeWindow != null) {\n            entity.setTimeWindow(timeWindow);\n        }\n        if (grade != null) {\n            entity.setGrade(grade);\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish degrade rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/delete.json\")\n    public Result<Long> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n\n        DegradeRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Throwable throwable) {\n            logger.error(\"delete error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish degrade rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DemoController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Random;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.EntryType;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.context.ContextUtil;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\n\n@Controller\n@RequestMapping(value = \"/demo\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class DemoController {\n\n    Logger logger = LoggerFactory.getLogger(MachineRegistryController.class);\n\n    @RequestMapping(\"/greeting\")\n    public String greeting() {\n        return \"index\";\n    }\n\n    @RequestMapping(\"/link\")\n    @ResponseBody\n    public String link() throws BlockException {\n\n        Entry entry = SphU.entry(\"head1\", EntryType.IN);\n\n        Entry entry1 = SphU.entry(\"head2\", EntryType.IN);\n        Entry entry2 = SphU.entry(\"head3\", EntryType.IN);\n        Entry entry3 = SphU.entry(\"head4\", EntryType.IN);\n\n        entry3.exit();\n        entry2.exit();\n        entry1.exit();\n        entry.exit();\n        return \"successfully create a call link\";\n    }\n\n    @RequestMapping(\"/loop\")\n    @ResponseBody\n    public String loop(String name, int time) throws BlockException {\n        for (int i = 0; i < 10; i++) {\n            Thread timer = new Thread(new RunTask(name, time, false));\n            timer.setName(\"false\");\n            timer.start();\n        }\n        return \"successfully create a loop thread\";\n    }\n\n    @RequestMapping(\"/slow\")\n    @ResponseBody\n    public String slow(String name, int time) throws BlockException {\n        for (int i = 0; i < 10; i++) {\n            Thread timer = new Thread(new RunTask(name, time, true));\n            timer.setName(\"false\");\n            timer.start();\n        }\n        return \"successfully create a loop thread\";\n    }\n\n    static class RunTask implements Runnable {\n        int time;\n        boolean stop = false;\n        String name;\n        boolean slow = false;\n\n        public RunTask(String name, int time, boolean slow) {\n            super();\n            this.time = time;\n            this.name = name;\n            this.slow = slow;\n        }\n\n        @Override\n        public void run() {\n            long startTime = System.currentTimeMillis();\n            ContextUtil.enter(String.valueOf(startTime));\n            while (!stop) {\n\n                long now = System.currentTimeMillis();\n                if (now - startTime > time * 1000) {\n                    stop = true;\n                }\n                Entry e1 = null;\n                try {\n                    e1 = SphU.entry(name);\n\n                    if (slow == true) {\n                        TimeUnit.MILLISECONDS.sleep(3000);\n                    }\n\n                } catch (Exception e) {\n                } finally {\n                    if (e1 != null) {\n                        e1.exit();\n                    }\n                }\n                Random random2 = new Random();\n                try {\n                    TimeUnit.MILLISECONDS.sleep(random2.nextInt(200));\n                } catch (InterruptedException e) {\n                    // TODO Auto-generated catch block\n                    e.printStackTrace();\n                }\n\n            }\n            ContextUtil.exit();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Flow rule controller.\n *\n * @author leyou\n * @author Eric Zhao\n */\n@RestController\n@RequestMapping(value = \"/v1/flow\")\npublic class FlowControllerV1 {\n\n    private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class);\n\n    @Autowired\n    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n\n    @GetMapping(\"/rules\")\n    public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request,\n                                                             @RequestParam String app,\n                                                             @RequestParam String ip,\n                                                             @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying flow rules\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (entity.getGrade() == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (entity.getGrade() != 0 && entity.getGrade() != 1) {\n            return Result.ofFail(-1, \"grade must be 0 or 1, but \" + entity.getGrade() + \" got\");\n        }\n        if (entity.getCount() == null || entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be at lease zero\");\n        }\n        if (entity.getStrategy() == null) {\n            return Result.ofFail(-1, \"strategy can't be null\");\n        }\n        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {\n            return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n        }\n        if (entity.getControlBehavior() == null) {\n            return Result.ofFail(-1, \"controlBehavior can't be null\");\n        }\n        int controlBehavior = entity.getControlBehavior();\n        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {\n            return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n        }\n        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {\n            return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n        }\n        if (entity.isClusterMode() && entity.getClusterConfig() == null) {\n            return Result.ofFail(-1, \"cluster config should be valid\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        entity.setLimitApp(entity.getLimitApp().trim());\n        entity.setResource(entity.getResource().trim());\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.error(\"Publish flow rules failed after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/save.json\")\n    public Result<FlowRuleEntity> updateIfNotNull(HttpServletRequest request, Long id, String app,\n                                                  String limitApp, String resource, Integer grade,\n                                                  Double count, Integer strategy, String refResource,\n                                                  Integer controlBehavior, Integer warmUpPeriodSec,\n                                                  Integer maxQueueingTimeMs) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        FlowRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n        if (StringUtil.isNotBlank(limitApp)) {\n            entity.setLimitApp(limitApp.trim());\n        }\n        if (StringUtil.isNotBlank(resource)) {\n            entity.setResource(resource.trim());\n        }\n        if (grade != null) {\n            if (grade != 0 && grade != 1) {\n                return Result.ofFail(-1, \"grade must be 0 or 1, but \" + grade + \" got\");\n            }\n            entity.setGrade(grade);\n        }\n        if (count != null) {\n            entity.setCount(count);\n        }\n        if (strategy != null) {\n            if (strategy != 0 && strategy != 1 && strategy != 2) {\n                return Result.ofFail(-1, \"strategy must be in [0, 1, 2], but \" + strategy + \" got\");\n            }\n            entity.setStrategy(strategy);\n            if (strategy != 0) {\n                if (StringUtil.isBlank(refResource)) {\n                    return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n                }\n                entity.setRefResource(refResource.trim());\n            }\n        }\n        if (controlBehavior != null) {\n            if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) {\n                return Result.ofFail(-1, \"controlBehavior must be in [0, 1, 2], but \" + controlBehavior + \" got\");\n            }\n            if (controlBehavior == 1 && warmUpPeriodSec == null) {\n                return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n            }\n            if (controlBehavior == 2 && maxQueueingTimeMs == null) {\n                return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n            }\n            entity.setControlBehavior(controlBehavior);\n            if (warmUpPeriodSec != null) {\n                entity.setWarmUpPeriodSec(warmUpPeriodSec);\n            }\n            if (maxQueueingTimeMs != null) {\n                entity.setMaxQueueingTimeMs(maxQueueingTimeMs);\n            }\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"save entity fail\");\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish flow rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/delete.json\")\n    public Result<Long> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish flow rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineDiscovery;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Controller\n@RequestMapping(value = \"/registry\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class MachineRegistryController {\n\n    private final Logger logger = LoggerFactory.getLogger(MachineRegistryController.class);\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @ResponseBody\n    @RequestMapping(\"/machine\")\n    public Result<?> receiveHeartBeat(String app, Long version, String v, String hostname, String ip, Integer port) {\n        if (app == null) {\n            app = MachineDiscovery.UNKNOWN_APP_NAME;\n        }\n        if (ip == null) {\n            return Result.ofFail(-1, \"ip can't be null\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (port == -1) {\n            logger.info(\"Receive heartbeat from \" + ip + \" but port not set yet\");\n            return Result.ofFail(-1, \"your port not set yet\");\n        }\n        String sentinelVersion = StringUtil.isEmpty(v) ? \"unknown\" : v;\n        version = version == null ? System.currentTimeMillis() : version;\n        try {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(app);\n            machineInfo.setHostname(hostname);\n            machineInfo.setIp(ip);\n            machineInfo.setPort(port);\n            machineInfo.setHeartbeatVersion(version);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setVersion(sentinelVersion);\n            appManagement.addMachine(machineInfo);\n            return Result.ofSuccessMsg(\"success\");\n        } catch (Exception e) {\n            logger.error(\"Receive heartbeat error\", e);\n            return Result.ofFail(-1, e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MetricController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.MetricVo;\n\n/**\n * @author leyou\n */\n@Controller\n@RequestMapping(value = \"/metric\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class MetricController {\n\n    private static Logger logger = LoggerFactory.getLogger(MetricController.class);\n\n    private static final long maxQueryIntervalMs = 1000 * 60 * 60;\n\n    @Autowired\n    private MetricsRepository<MetricEntity> metricStore;\n\n    @ResponseBody\n    @RequestMapping(\"/queryTopResourceMetric.json\")\n    public Result<?> queryTopResourceMetric(final String app,\n                                            Integer pageIndex,\n                                            Integer pageSize,\n                                            Boolean desc,\n                                            Long startTime, Long endTime, String searchKey) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (pageIndex == null || pageIndex <= 0) {\n            pageIndex = 1;\n        }\n        if (pageSize == null) {\n            pageSize = 6;\n        }\n        if (pageSize >= 20) {\n            pageSize = 20;\n        }\n        if (desc == null) {\n            desc = true;\n        }\n        if (endTime == null) {\n            endTime = System.currentTimeMillis();\n        }\n        if (startTime == null) {\n            startTime = endTime - 1000 * 60 * 5;\n        }\n        if (endTime - startTime > maxQueryIntervalMs) {\n            return Result.ofFail(-1, \"time intervalMs is too big, must <= 1h\");\n        }\n        List<String> resources = metricStore.listResourcesOfApp(app);\n        logger.debug(\"queryTopResourceMetric(), resources.size()={}\", resources.size());\n\n        if (resources == null || resources.isEmpty()) {\n            return Result.ofSuccess(null);\n        }\n        if (!desc) {\n            Collections.reverse(resources);\n        }\n        if (StringUtil.isNotEmpty(searchKey)) {\n            List<String> searched = new ArrayList<>();\n            for (String resource : resources) {\n                if (resource.contains(searchKey)) {\n                    searched.add(resource);\n                }\n            }\n            resources = searched;\n        }\n        int totalPage = (resources.size() + pageSize - 1) / pageSize;\n        List<String> topResource = new ArrayList<>();\n        if (pageIndex <= totalPage) {\n            topResource = resources.subList((pageIndex - 1) * pageSize,\n                Math.min(pageIndex * pageSize, resources.size()));\n        }\n        final Map<String, Iterable<MetricVo>> map = new ConcurrentHashMap<>();\n        logger.debug(\"topResource={}\", topResource);\n        long time = System.currentTimeMillis();\n        for (final String resource : topResource) {\n            List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(\n                app, resource, startTime, endTime);\n            logger.debug(\"resource={}, entities.size()={}\", resource, entities == null ? \"null\" : entities.size());\n            List<MetricVo> vos = MetricVo.fromMetricEntities(entities, resource);\n            Iterable<MetricVo> vosSorted = sortMetricVoAndDistinct(vos);\n            map.put(resource, vosSorted);\n        }\n        logger.debug(\"queryTopResourceMetric() total query time={} ms\", System.currentTimeMillis() - time);\n        Map<String, Object> resultMap = new HashMap<>(16);\n        resultMap.put(\"totalCount\", resources.size());\n        resultMap.put(\"totalPage\", totalPage);\n        resultMap.put(\"pageIndex\", pageIndex);\n        resultMap.put(\"pageSize\", pageSize);\n\n        Map<String, Iterable<MetricVo>> map2 = new LinkedHashMap<>();\n        // order matters.\n        for (String identity : topResource) {\n            map2.put(identity, map.get(identity));\n        }\n        resultMap.put(\"metric\", map2);\n        return Result.ofSuccess(resultMap);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/queryByAppAndResource.json\")\n    public Result<?> queryByAppAndResource(String app, String identity, Long startTime, Long endTime) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(identity)) {\n            return Result.ofFail(-1, \"identity can't be null or empty\");\n        }\n        if (endTime == null) {\n            endTime = System.currentTimeMillis();\n        }\n        if (startTime == null) {\n            startTime = endTime - 1000 * 60;\n        }\n        if (endTime - startTime > maxQueryIntervalMs) {\n            return Result.ofFail(-1, \"time intervalMs is too big, must <= 1h\");\n        }\n        List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(\n            app, identity, startTime, endTime);\n        List<MetricVo> vos = MetricVo.fromMetricEntities(entities, identity);\n        return Result.ofSuccess(sortMetricVoAndDistinct(vos));\n    }\n\n    private Iterable<MetricVo> sortMetricVoAndDistinct(List<MetricVo> vos) {\n        if (vos == null) {\n            return null;\n        }\n        Map<Long, MetricVo> map = new TreeMap<>();\n        for (MetricVo vo : vos) {\n            MetricVo oldVo = map.get(vo.getTimestamp());\n            if (oldVo == null || vo.getGmtCreate() > oldVo.getGmtCreate()) {\n                map.put(vo.getTimestamp(), vo);\n            }\n        }\n        return map.values();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@RestController\n@RequestMapping(value = \"/paramFlow\")\npublic class ParamFlowRuleController {\n\n    private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n    @Autowired\n    private RuleRepository<ParamFlowRuleEntity, Long> repository;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    private boolean checkIfSupported(String app, String ip, int port) {\n        try {\n            return Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version020)))\n                .orElse(true);\n            // If error occurred or cannot retrieve machine info, return true.\n        } catch (Exception ex) {\n            return true;\n        }\n    }\n\n    @GetMapping(\"/rules\")\n    public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(HttpServletRequest request,\n                                                                        @RequestParam String app,\n                                                                        @RequestParam String ip,\n                                                                        @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        if (!checkIfSupported(app, ip, port)) {\n            return unsupportedVersion();\n        }\n        try {\n            return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)\n                .thenApply(repository::saveAll)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when querying parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private boolean isNotSupported(Throwable ex) {\n        return ex instanceof CommandNotFoundException;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<ParamFlowRuleEntity> apiAddParamFlowRule(HttpServletRequest request,\n                                                           @RequestBody ParamFlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {\n            return unsupportedVersion();\n        }\n        entity.setId(null);\n        entity.getRule().setResource(entity.getResource().trim());\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();\n            return Result.ofSuccess(entity);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when adding new parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when adding new parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"bad rule body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null || entity.getPort() <= 0) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (entity.getRule() == null) {\n            return Result.ofFail(-1, \"rule can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource name cannot be null or empty\");\n        }\n        if (entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be valid\");\n        }\n        if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) {\n            return Result.ofFail(-1, \"Unknown mode (blockGrade) for parameter flow control\");\n        }\n        if (entity.getParamIdx() == null || entity.getParamIdx() < 0) {\n            return Result.ofFail(-1, \"paramIdx should be valid\");\n        }\n        return null;\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(HttpServletRequest request,\n                                                              @PathVariable(\"id\") Long id,\n                                                              @RequestBody ParamFlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        ParamFlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" does not exist\");\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {\n            return unsupportedVersion();\n        }\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();\n            return Result.ofSuccess(entity);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when updating parameter flow rules, id=\" + id, ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when updating parameter flow rules, id=\" + id, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id cannot be null\");\n        }\n        ParamFlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n            publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();\n            return Result.ofSuccess(id);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when deleting parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when deleting parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {\n        List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);\n    }\n\n    private <R> Result<R> unsupportedVersion() {\n        return Result.ofFail(4041,\n            \"Sentinel client not supported for parameter flow control (unsupported version or dependency absent)\");\n    }\n\n    private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ResourceController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.ResourceVo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Carpenter Lee\n */\n@RestController\n@RequestMapping(value = \"/resource\")\npublic class ResourceController {\n\n    private static Logger logger = LoggerFactory.getLogger(ResourceController.class);\n\n    @Autowired\n    private SentinelApiClient httpFetcher;\n\n    /**\n     * Fetch real time statistics info of the machine.\n     *\n     * @param ip        ip to fetch\n     * @param port      port of the ip\n     * @param type      one of [root, default, cluster], 'root' means fetching from tree root node, 'default' means\n     *                  fetching from tree default node, 'cluster' means fetching from cluster node.\n     * @param searchKey key to search\n     * @return node statistics info.\n     */\n    @GetMapping(\"/machineResource.json\")\n    public Result<List<ResourceVo>> fetchResourceChainListOfMachine(String ip, Integer port, String type,\n                                                                    String searchKey) {\n        if (StringUtil.isEmpty(ip) || port == null) {\n            return Result.ofFail(-1, \"invalid param, give ip, port\");\n        }\n        final String ROOT = \"root\";\n        final String DEFAULT = \"default\";\n        if (StringUtil.isEmpty(type)) {\n            type = ROOT;\n        }\n        if (ROOT.equalsIgnoreCase(type) || DEFAULT.equalsIgnoreCase(type)) {\n            List<NodeVo> nodeVos = httpFetcher.fetchResourceOfMachine(ip, port, type);\n            if (nodeVos == null) {\n                return Result.ofSuccess(null);\n            }\n            ResourceTreeNode treeNode = ResourceTreeNode.fromNodeVoList(nodeVos);\n            treeNode.searchIgnoreCase(searchKey);\n            return Result.ofSuccess(ResourceVo.fromResourceTreeNode(treeNode));\n        } else {\n            // Normal (cluster node).\n            List<NodeVo> nodeVos = httpFetcher.fetchClusterNodeOfMachine(ip, port, true);\n            if (nodeVos == null) {\n                return Result.ofSuccess(null);\n            }\n            if (StringUtil.isNotEmpty(searchKey)) {\n                nodeVos = nodeVos.stream().filter(node -> node.getResource()\n                    .toLowerCase().contains(searchKey.toLowerCase()))\n                    .collect(Collectors.toList());\n            }\n            return Result.ofSuccess(ResourceVo.fromNodeVoList(nodeVos));\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author leyou(lihao)\n */\n@Controller\n@RequestMapping(value = \"/system\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class SystemController {\n    private static Logger logger = LoggerFactory.getLogger(SystemController.class);\n\n    @Autowired\n    private InMemSystemRuleStore repository;\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @ResponseBody\n    @RequestMapping(\"/rules.json\")\n    Result<List<SystemRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<SystemRuleEntity> rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"queryApps error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private int countNotNullAndNotNegative(Number... values) {\n        int notNullCount = 0;\n        for (int i = 0; i < values.length; i++) {\n            if (values[i] != null && values[i].doubleValue() >= 0) {\n                notNullCount++;\n            }\n        }\n        return notNullCount;\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/new.json\")\n    Result<?> add(HttpServletRequest request,\n                  String app, String ip, Integer port, Double avgLoad, Long avgRt, Long maxThread, Double qps) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n        if (StringUtil.isBlank(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps);\n        if (notNullCount != 1) {\n            return Result.ofFail(-1, \"only one of [avgLoad, avgRt, maxThread, qps] \"\n                + \"value must be set >= 0, but \" + notNullCount + \" values get\");\n        }\n        SystemRuleEntity entity = new SystemRuleEntity();\n        entity.setApp(app.trim());\n        entity.setIp(ip.trim());\n        entity.setPort(port);\n        // -1 is a fake value\n        if (avgLoad != null) {\n            entity.setAvgLoad(avgLoad);\n        } else {\n            entity.setAvgLoad(-1D);\n        }\n        if (avgRt != null) {\n            entity.setAvgRt(avgRt);\n        } else {\n            entity.setAvgRt(-1L);\n        }\n        if (maxThread != null) {\n            entity.setMaxThread(maxThread);\n        } else {\n            entity.setMaxThread(-1L);\n        }\n        if (qps != null) {\n            entity.setQps(qps);\n        } else {\n            entity.setQps(-1D);\n        }\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"add error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(app, ip, port)) {\n            logger.info(\"publish system rules fail after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/save.json\")\n    Result<?> updateIfNotNull(HttpServletRequest request,\n                              Long id, String app, Double avgLoad, Long avgRt, Long maxThread, Double qps) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        SystemRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n        if (avgLoad != null) {\n            if (avgLoad < 0) {\n                return Result.ofFail(-1, \"avgLoad must >= 0\");\n            }\n            entity.setAvgLoad(avgLoad);\n        }\n        if (avgRt != null) {\n            if (avgRt < 0) {\n                return Result.ofFail(-1, \"avgRt must >= 0\");\n            }\n            entity.setAvgRt(avgRt);\n        }\n        if (maxThread != null) {\n            if (maxThread < 0) {\n                return Result.ofFail(-1, \"maxThread must >= 0\");\n            }\n            entity.setMaxThread(maxThread);\n        }\n        if (qps != null) {\n            if (qps < 0) {\n                return Result.ofFail(-1, \"qps must >= 0\");\n            }\n            entity.setQps(qps);\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish system rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/delete.json\")\n    Result<?> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        SystemRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Throwable throwable) {\n            logger.error(\"delete error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish system rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<SystemRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterAssignController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.cluster;\n\nimport java.util.Collections;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest;\nimport com.alibaba.csp.sentinel.dashboard.service.ClusterAssignService;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\n@RestController\n@RequestMapping(\"/cluster/assign\")\npublic class ClusterAssignController {\n\n    private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class);\n\n    @Autowired\n    private ClusterAssignService clusterAssignService;\n\n    @PostMapping(\"/all_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiAssignAllClusterServersOfApp(@PathVariable String app,\n                                                                            @RequestBody\n                                                                                ClusterAppFullAssignRequest assignRequest) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (assignRequest == null || assignRequest.getClusterMap() == null\n            || assignRequest.getRemainingList() == null) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(),\n                assignRequest.getRemainingList()));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when assigning full cluster servers for app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @PostMapping(\"/single_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiAssignSingleClusterServersOfApp(@PathVariable String app,\n                                                                               @RequestBody ClusterAppSingleServerAssignRequest assignRequest) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (assignRequest == null || assignRequest.getClusterMap() == null) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()),\n                assignRequest.getRemainingList()));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when assigning single cluster servers for app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @PostMapping(\"/unbind_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiUnbindClusterServersOfApp(@PathVariable String app,\n                                                                         @RequestBody Set<String> machineIds) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (machineIds == null || machineIds.isEmpty()) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when unbinding cluster server {} for app <{}>\", machineIds, app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterConfigController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.cluster;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.ExecutionException;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;\nimport com.alibaba.csp.sentinel.dashboard.service.ClusterConfigService;\nimport com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@RestController\n@RequestMapping(value = \"/cluster\")\npublic class ClusterConfigController {\n\n    private final Logger logger = LoggerFactory.getLogger(ClusterConfigController.class);\n\n    private final SentinelVersion version140 = new SentinelVersion().setMajorVersion(1).setMinorVersion(4);\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @Autowired\n    private ClusterConfigService clusterConfigService;\n\n    @PostMapping(\"/config/modify_single\")\n    public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {\n        if (StringUtil.isBlank(payload)) {\n            return Result.ofFail(-1, \"empty request body\");\n        }\n        try {\n            JSONObject body = JSON.parseObject(payload);\n            if (body.containsKey(KEY_MODE)) {\n                int mode = body.getInteger(KEY_MODE);\n                switch (mode) {\n                    case ClusterStateManager.CLUSTER_CLIENT:\n                        ClusterClientModifyRequest data = JSON.parseObject(payload, ClusterClientModifyRequest.class);\n                        Result<Boolean> res = checkValidRequest(data);\n                        if (res != null) {\n                            return res;\n                        }\n                        clusterConfigService.modifyClusterClientConfig(data).get();\n                        return Result.ofSuccess(true);\n                    case ClusterStateManager.CLUSTER_SERVER:\n                        ClusterServerModifyRequest d = JSON.parseObject(payload, ClusterServerModifyRequest.class);\n                        Result<Boolean> r = checkValidRequest(d);\n                        if (r != null) {\n                            return r;\n                        }\n                        // TODO: bad design here, should refactor!\n                        clusterConfigService.modifyClusterServerConfig(d).get();\n                        return Result.ofSuccess(true);\n                    default:\n                        return Result.ofFail(-1, \"invalid mode\");\n                }\n            }\n            return Result.ofFail(-1, \"invalid parameter\");\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when modifying cluster config\", ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable ex) {\n            logger.error(\"Error when modifying cluster config\", ex);\n            return Result.ofFail(-1, ex.getMessage());\n        }\n    }\n\n    private <T> Result<T> errorResponse(ExecutionException ex) {\n        if (isNotSupported(ex.getCause())) {\n            return unsupportedVersion();\n        } else {\n            return Result.ofThrowable(-1, ex.getCause());\n        }\n    }\n\n    @GetMapping(\"/state_single\")\n    public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,\n                                                              @RequestParam String ip,\n                                                              @RequestParam Integer port) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        if (!checkIfSupported(app, ip, port)) {\n            return unsupportedVersion();\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app, ip, port)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster state\", ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster state\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/server_state/{app}\")\n    public Result<List<AppClusterServerStateWrapVO>> apiGetClusterServerStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(ClusterEntityUtils::wrapToAppClusterServerState)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster server state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster server state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/client_state/{app}\")\n    public Result<List<AppClusterClientStateWrapVO>> apiGetClusterClientStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(ClusterEntityUtils::wrapToAppClusterClientState)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster token client state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster token client state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/state/{app}\")\n    public Result<List<ClusterUniversalStatePairVO>> apiGetClusterStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private boolean isNotSupported(Throwable ex) {\n        return ex instanceof CommandNotFoundException;\n    }\n\n    private boolean checkIfSupported(String app, String ip, int port) {\n        try {\n            return Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version140)))\n                .orElse(true);\n            // If error occurred or cannot retrieve machine info, return true.\n        } catch (Exception ex) {\n            return true;\n        }\n    }\n\n    private Result<Boolean> checkValidRequest(ClusterModifyRequest request) {\n        if (StringUtil.isEmpty(request.getApp())) {\n            return Result.ofFail(-1, \"app cannot be empty\");\n        }\n        if (StringUtil.isEmpty(request.getIp())) {\n            return Result.ofFail(-1, \"ip cannot be empty\");\n        }\n        if (request.getPort() == null || request.getPort() < 0) {\n            return Result.ofFail(-1, \"invalid port\");\n        }\n        if (request.getMode() == null || request.getMode() < 0) {\n            return Result.ofFail(-1, \"invalid mode\");\n        }\n        if (!checkIfSupported(request.getApp(), request.getIp(), request.getPort())) {\n            return unsupportedVersion();\n        }\n        return null;\n    }\n\n    private <R> Result<R> unsupportedVersion() {\n        return Result.ofFail(4041, \"Sentinel client not supported for cluster flow control (unsupported version or dependency absent)\");\n    }\n\n    private static final String KEY_MODE = \"mode\";\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.v2;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Flow rule controller (v2).\n *\n * @author Eric Zhao\n * @since 1.4.0\n */\n@RestController\n@RequestMapping(value = \"/v2/flow\")\npublic class FlowControllerV2 {\n\n    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);\n\n    @Autowired\n    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;\n\n    @Autowired\n    @Qualifier(\"flowRuleApolloProvider\")\n    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;\n    @Autowired\n    @Qualifier(\"flowRuleApolloPublisher\")\n    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @GetMapping(\"/rules\")\n    public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        try {\n            List<FlowRuleEntity> rules = ruleProvider.getRules(app);\n            if (rules != null && !rules.isEmpty()) {\n                for (FlowRuleEntity entity : rules) {\n                    entity.setApp(app);\n                    if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {\n                        entity.setId(entity.getClusterConfig().getFlowId());\n                    }\n                }\n            }\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying flow rules\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"invalid body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (entity.getGrade() == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (entity.getGrade() != 0 && entity.getGrade() != 1) {\n            return Result.ofFail(-1, \"grade must be 0 or 1, but \" + entity.getGrade() + \" got\");\n        }\n        if (entity.getCount() == null || entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be at lease zero\");\n        }\n        if (entity.getStrategy() == null) {\n            return Result.ofFail(-1, \"strategy can't be null\");\n        }\n        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {\n            return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n        }\n        if (entity.getControlBehavior() == null) {\n            return Result.ofFail(-1, \"controlBehavior can't be null\");\n        }\n        int controlBehavior = entity.getControlBehavior();\n        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {\n            return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n        }\n        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {\n            return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n        }\n        if (entity.isClusterMode() && entity.getClusterConfig() == null) {\n            return Result.ofFail(-1, \"cluster config should be valid\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        entity.setLimitApp(entity.getLimitApp().trim());\n        entity.setResource(entity.getResource().trim());\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp());\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<FlowRuleEntity> apiUpdateFlowRule(HttpServletRequest request,\n                                                    @PathVariable(\"id\") Long id,\n                                                    @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" does not exist\");\n        }\n        if (entity == null) {\n            return Result.ofFail(-1, \"invalid body\");\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE);\n\n        entity.setApp(oldEntity.getApp());\n        entity.setIp(oldEntity.getIp());\n        entity.setPort(oldEntity.getPort());\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"save entity fail\");\n            }\n            publishRules(oldEntity.getApp());\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to update flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n            publishRules(oldEntity.getApp());\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private void publishRules(/*@NonNull*/ String app) throws Exception {\n        List<FlowRuleEntity> rules = repository.findAllByApp(app);\n        rulePublisher.publish(app, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\n\n/**\n * @author leyou\n */\npublic class ApplicationEntity {\n\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String activeConsole;\n    private Date lastFetch;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getActiveConsole() {\n        return activeConsole;\n    }\n\n    public Date getLastFetch() {\n        return lastFetch;\n    }\n\n    public void setLastFetch(Date lastFetch) {\n        this.lastFetch = lastFetch;\n    }\n\n    public void setActiveConsole(String activeConsole) {\n        this.activeConsole = activeConsole;\n    }\n\n    public AppInfo toAppInfo() {\n        return new AppInfo(app);\n    }\n\n    @Override\n    public String toString() {\n        return \"ApplicationEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", activeConsole='\" + activeConsole + '\\'' +\n            \", lastFetch=\" + lastFetch +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * @author leyou\n */\npublic class MachineEntity {\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String ip;\n    private String hostname;\n    private Date timestamp;\n    private Integer port;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public Date getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Date timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    public MachineInfo toMachineInfo() {\n        MachineInfo machineInfo = new MachineInfo();\n\n        machineInfo.setApp(app);\n        machineInfo.setHostname(hostname);\n        machineInfo.setIp(ip);\n        machineInfo.setPort(port);\n        machineInfo.setLastHeartbeat(timestamp.getTime());\n        machineInfo.setHeartbeatVersion(timestamp.getTime());\n\n        return machineInfo;\n    }\n\n    @Override\n    public String toString() {\n        return \"MachineEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", hostname='\" + hostname + '\\'' +\n            \", timestamp=\" + timestamp +\n            \", port=\" + port +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\n/**\n * @author leyou\n */\npublic class MetricEntity {\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    /**\n     * 监控信息的时间戳\n     */\n    private Date timestamp;\n    private String resource;\n    private Long passQps;\n    private Long successQps;\n    private Long blockQps;\n    private Long exceptionQps;\n\n    /**\n     * summary rt of all success exit qps.\n     */\n    private double rt;\n\n    /**\n     * 本次聚合的总条数\n     */\n    private int count;\n\n    private int resourceCode;\n\n    public static MetricEntity copyOf(MetricEntity oldEntity) {\n        MetricEntity entity = new MetricEntity();\n        entity.setId(oldEntity.getId());\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(oldEntity.getGmtModified());\n        entity.setApp(oldEntity.getApp());\n        entity.setTimestamp(oldEntity.getTimestamp());\n        entity.setResource(oldEntity.getResource());\n        entity.setPassQps(oldEntity.getPassQps());\n        entity.setBlockQps(oldEntity.getBlockQps());\n        entity.setSuccessQps(oldEntity.getSuccessQps());\n        entity.setExceptionQps(oldEntity.getExceptionQps());\n        entity.setRt(oldEntity.getRt());\n        entity.setCount(oldEntity.getCount());\n        entity.setResource(oldEntity.getResource());\n        return entity;\n    }\n\n    public synchronized void addPassQps(Long passQps) {\n        this.passQps += passQps;\n    }\n\n    public synchronized void addBlockQps(Long blockQps) {\n        this.blockQps += blockQps;\n    }\n\n    public synchronized void addExceptionQps(Long exceptionQps) {\n        this.exceptionQps += exceptionQps;\n    }\n\n    public synchronized void addCount(int count) {\n        this.count += count;\n    }\n\n    public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) {\n        this.rt += avgRt * successQps;\n        this.successQps += successQps;\n    }\n\n    /**\n     * {@link #rt} = {@code avgRt * successQps}\n     *\n     * @param avgRt      average rt of {@code successQps}\n     * @param successQps\n     */\n    public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) {\n        this.rt = avgRt * successQps;\n        this.successQps = successQps;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Date getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Date timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n        this.resourceCode = resource.hashCode();\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public double getRt() {\n        return rt;\n    }\n\n    public void setRt(double rt) {\n        this.rt = rt;\n    }\n\n    public int getCount() {\n        return count;\n    }\n\n    public void setCount(int count) {\n        this.count = count;\n    }\n\n    public int getResourceCode() {\n        return resourceCode;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    @Override\n    public String toString() {\n        return \"MetricEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", timestamp=\" + timestamp +\n            \", resource='\" + resource + '\\'' +\n            \", passQps=\" + passQps +\n            \", blockQps=\" + blockQps +\n            \", successQps=\" + successQps +\n            \", exceptionQps=\" + exceptionQps +\n            \", rt=\" + rt +\n            \", count=\" + count +\n            \", resourceCode=\" + resourceCode +\n            '}';\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\n/**\n * @author leyou\n */\npublic class MetricPositionEntity {\n    private long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String ip;\n    /**\n     * Sentinel在该应用上使用的端口\n     */\n    private int port;\n\n    /**\n     * 机器名，冗余字段\n     */\n    private String hostname;\n\n    /**\n     * 上一次拉取的最晚时间戳\n     */\n    private Date lastFetch;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public Date getLastFetch() {\n        return lastFetch;\n    }\n\n    public void setLastFetch(Date lastFetch) {\n        this.lastFetch = lastFetch;\n    }\n\n    @Override\n    public String toString() {\n        return \"MetricPositionEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", hostname='\" + hostname + '\\'' +\n            \", lastFetch=\" + lastFetch +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersion.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class SentinelVersion {\n    private int majorVersion;\n    private int minorVersion;\n    private int fixVersion;\n    private String postfix;\n    \n    public SentinelVersion() {\n        this(0, 0, 0);\n    }\n    \n    public SentinelVersion(int major, int minor, int fix) {\n        this(major, minor, fix, null);\n    }\n    \n    public SentinelVersion(int major, int minor, int fix, String postfix) {\n        this.majorVersion = major;\n        this.minorVersion = minor;\n        this.fixVersion = fix;\n        this.postfix = postfix;\n    }\n    \n    /**\n     * 000, 000, 000\n     */\n    public int getFullVersion() {\n        return majorVersion * 1000000 + minorVersion * 1000 + fixVersion;\n    }\n\n    public int getMajorVersion() {\n        return majorVersion;\n    }\n\n    public SentinelVersion setMajorVersion(int majorVersion) {\n        this.majorVersion = majorVersion;\n        return this;\n    }\n\n    public int getMinorVersion() {\n        return minorVersion;\n    }\n\n    public SentinelVersion setMinorVersion(int minorVersion) {\n        this.minorVersion = minorVersion;\n        return this;\n    }\n\n    public int getFixVersion() {\n        return fixVersion;\n    }\n\n    public SentinelVersion setFixVersion(int fixVersion) {\n        this.fixVersion = fixVersion;\n        return this;\n    }\n\n    public String getPostfix() {\n        return postfix;\n    }\n\n    public SentinelVersion setPostfix(String postfix) {\n        this.postfix = postfix;\n        return this;\n    }\n\n    public boolean greaterThan(SentinelVersion version) {\n        if (version == null) {\n            return true;\n        }\n        return getFullVersion() > version.getFullVersion();\n    }\n\n    public boolean greaterOrEqual(SentinelVersion version) {\n        if (version == null) {\n            return true;\n        }\n        return getFullVersion() >= version.getFullVersion();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) { return true; }\n        if (o == null || getClass() != o.getClass()) { return false; }\n\n        SentinelVersion that = (SentinelVersion)o;\n\n        if (getFullVersion() != that.getFullVersion()) { return false; }\n        return postfix != null ? postfix.equals(that.postfix) : that.postfix == null;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = majorVersion;\n        result = 31 * result + minorVersion;\n        result = 31 * result + fixVersion;\n        result = 31 * result + (postfix != null ? postfix.hashCode() : 0);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"SentinelVersion{\" +\n            \"majorVersion=\" + majorVersion +\n            \", minorVersion=\" + minorVersion +\n            \", fixVersion=\" + fixVersion +\n            \", postfix='\" + postfix + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AbstractRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.AbstractRule;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic abstract class AbstractRuleEntity<T extends AbstractRule> implements RuleEntity {\n\n    protected Long id;\n\n    protected String app;\n    protected String ip;\n    protected Integer port;\n\n    protected T rule;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public AbstractRuleEntity<T> setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public AbstractRuleEntity<T> setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public AbstractRuleEntity<T> setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public T getRule() {\n        return rule;\n    }\n\n    public AbstractRuleEntity<T> setRule(T rule) {\n        this.rule = rule;\n        return this;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public AbstractRuleEntity<T> setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n        return this;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public AbstractRuleEntity<T> setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AuthorityRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> {\n\n    public AuthorityRuleEntity() {}\n\n    public AuthorityRuleEntity(AuthorityRule authorityRule) {\n        AssertUtil.notNull(authorityRule, \"Authority rule should not be null\");\n        this.rule = authorityRule;\n    }\n\n    public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integer port, AuthorityRule rule) {\n        AuthorityRuleEntity entity = new AuthorityRuleEntity(rule);\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        return entity;\n    }\n\n    @JsonIgnore\n    public String getLimitApp() {\n        return rule.getLimitApp();\n    }\n\n    @JsonIgnore\n    public String getResource() {\n        return rule.getResource();\n    }\n\n    @JsonIgnore\n    public int getStrategy() {\n        return rule.getStrategy();\n    }\n    \n    @Override\n    public Rule toRule() {\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/DegradeRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;\n\n/**\n * @author leyou\n */\npublic class DegradeRuleEntity implements RuleEntity {\n    private Long id;\n    private String app;\n    private String ip;\n    private Integer port;\n    private String resource;\n    private String limitApp;\n    private Double count;\n    private Integer timeWindow;\n    /**\n     * 0 rt 限流; 1为异常;\n     */\n    private Integer grade;\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static DegradeRuleEntity fromDegradeRule(String app, String ip, Integer port, DegradeRule rule) {\n        DegradeRuleEntity entity = new DegradeRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setResource(rule.getResource());\n        entity.setLimitApp(rule.getLimitApp());\n        entity.setCount(rule.getCount());\n        entity.setTimeWindow(rule.getTimeWindow());\n        entity.setGrade(rule.getGrade());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public String getLimitApp() {\n        return limitApp;\n    }\n\n    public void setLimitApp(String limitApp) {\n        this.limitApp = limitApp;\n    }\n\n    public Double getCount() {\n        return count;\n    }\n\n    public void setCount(Double count) {\n        this.count = count;\n    }\n\n    public Integer getTimeWindow() {\n        return timeWindow;\n    }\n\n    public void setTimeWindow(Integer timeWindow) {\n        this.timeWindow = timeWindow;\n    }\n\n    public Integer getGrade() {\n        return grade;\n    }\n\n    public void setGrade(Integer grade) {\n        this.grade = grade;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public DegradeRule toRule() {\n        DegradeRule rule = new DegradeRule();\n        rule.setResource(resource);\n        rule.setLimitApp(limitApp);\n        rule.setCount(count);\n        rule.setTimeWindow(timeWindow);\n        rule.setGrade(grade);\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\n\n/**\n * @author leyou\n */\npublic class FlowRuleEntity implements RuleEntity {\n\n    private Long id;\n    private String app;\n    private String ip;\n    private Integer port;\n    private String limitApp;\n    private String resource;\n    /**\n     * 0为线程数;1为qps\n     */\n    private Integer grade;\n    private Double count;\n    /**\n     * 0为直接限流;1为关联限流;2为链路限流\n     ***/\n    private Integer strategy;\n    private String refResource;\n    /**\n     * 0. default, 1. warm up, 2. rate limiter\n     */\n    private Integer controlBehavior;\n    private Integer warmUpPeriodSec;\n    /**\n     * max queueing time in rate limiter behavior\n     */\n    private Integer maxQueueingTimeMs;\n\n    private boolean clusterMode;\n    /**\n     * Flow rule config for cluster mode.\n     */\n    private ClusterFlowConfig clusterConfig;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static FlowRuleEntity fromFlowRule(String app, String ip, Integer port, FlowRule rule) {\n        FlowRuleEntity entity = new FlowRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setLimitApp(rule.getLimitApp());\n        entity.setResource(rule.getResource());\n        entity.setGrade(rule.getGrade());\n        entity.setCount(rule.getCount());\n        entity.setStrategy(rule.getStrategy());\n        entity.setRefResource(rule.getRefResource());\n        entity.setControlBehavior(rule.getControlBehavior());\n        entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec());\n        entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs());\n        entity.setClusterMode(rule.isClusterMode());\n        entity.setClusterConfig(rule.getClusterConfig());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getLimitApp() {\n        return limitApp;\n    }\n\n    public void setLimitApp(String limitApp) {\n        this.limitApp = limitApp;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getGrade() {\n        return grade;\n    }\n\n    public void setGrade(Integer grade) {\n        this.grade = grade;\n    }\n\n    public Double getCount() {\n        return count;\n    }\n\n    public void setCount(Double count) {\n        this.count = count;\n    }\n\n    public Integer getStrategy() {\n        return strategy;\n    }\n\n    public void setStrategy(Integer strategy) {\n        this.strategy = strategy;\n    }\n\n    public String getRefResource() {\n        return refResource;\n    }\n\n    public void setRefResource(String refResource) {\n        this.refResource = refResource;\n    }\n\n    public Integer getControlBehavior() {\n        return controlBehavior;\n    }\n\n    public void setControlBehavior(Integer controlBehavior) {\n        this.controlBehavior = controlBehavior;\n    }\n\n    public Integer getWarmUpPeriodSec() {\n        return warmUpPeriodSec;\n    }\n\n    public void setWarmUpPeriodSec(Integer warmUpPeriodSec) {\n        this.warmUpPeriodSec = warmUpPeriodSec;\n    }\n\n    public Integer getMaxQueueingTimeMs() {\n        return maxQueueingTimeMs;\n    }\n\n    public void setMaxQueueingTimeMs(Integer maxQueueingTimeMs) {\n        this.maxQueueingTimeMs = maxQueueingTimeMs;\n    }\n\n    public boolean isClusterMode() {\n        return clusterMode;\n    }\n\n    public FlowRuleEntity setClusterMode(boolean clusterMode) {\n        this.clusterMode = clusterMode;\n        return this;\n    }\n\n    public ClusterFlowConfig getClusterConfig() {\n        return clusterConfig;\n    }\n\n    public FlowRuleEntity setClusterConfig(ClusterFlowConfig clusterConfig) {\n        this.clusterConfig = clusterConfig;\n        return this;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public FlowRule toRule() {\n        FlowRule flowRule = new FlowRule();\n        flowRule.setCount(this.count);\n        flowRule.setGrade(this.grade);\n        flowRule.setResource(this.resource);\n        flowRule.setLimitApp(this.limitApp);\n        flowRule.setRefResource(this.refResource);\n        flowRule.setStrategy(this.strategy);\n        if (this.controlBehavior != null) {\n            flowRule.setControlBehavior(controlBehavior);\n        }\n        if (this.warmUpPeriodSec != null) {\n            flowRule.setWarmUpPeriodSec(warmUpPeriodSec);\n        }\n        if (this.maxQueueingTimeMs != null) {\n            flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs);\n        }\n        flowRule.setClusterMode(clusterMode);\n        flowRule.setClusterConfig(clusterConfig);\n        return flowRule;\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {\n\n    public ParamFlowRuleEntity() {}\n\n    public ParamFlowRuleEntity(ParamFlowRule rule) {\n        AssertUtil.notNull(rule, \"Authority rule should not be null\");\n        this.rule = rule;\n    }\n\n    public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) {\n        ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule);\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        return entity;\n    }\n\n    @JsonIgnore\n    public String getLimitApp() {\n        return rule.getLimitApp();\n    }\n\n    @JsonIgnore\n    public String getResource() {\n        return rule.getResource();\n    }\n\n    @JsonIgnore\n    public int getGrade() {\n        return rule.getGrade();\n    }\n\n    @JsonIgnore\n    public Integer getParamIdx() {\n        return rule.getParamIdx();\n    }\n\n    @JsonIgnore\n    public double getCount() {\n        return rule.getCount();\n    }\n\n    @JsonIgnore\n    public List<ParamFlowItem> getParamFlowItemList() {\n        return rule.getParamFlowItemList();\n    }\n\n    @JsonIgnore\n    public boolean isClusterMode() {\n        return rule.isClusterMode();\n    }\n\n    @JsonIgnore\n    public ParamFlowClusterConfig getClusterConfig() {\n        return rule.getClusterConfig();\n    }\n    \n    @Override\n    public Rule toRule() {\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/RuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\n\n/**\n * @author leyou\n */\npublic interface RuleEntity {\n\n    Long getId();\n\n    void setId(Long id);\n\n    String getApp();\n\n    String getIp();\n\n    Integer getPort();\n\n    Date getGmtCreate();\n    \n    Rule toRule();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.system.SystemRule;\n\n/**\n * @author leyou\n */\npublic class SystemRuleEntity implements RuleEntity {\n\n    private Long id;\n\n    private String app;\n    private String ip;\n    private Integer port;\n    private Double avgLoad;\n    private Long avgRt;\n    private Long maxThread;\n    private Double qps;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static SystemRuleEntity fromSystemRule(String app, String ip, Integer port, SystemRule rule) {\n        SystemRuleEntity entity = new SystemRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setAvgLoad(rule.getHighestSystemLoad());\n        entity.setAvgRt(rule.getAvgRt());\n        entity.setMaxThread(rule.getMaxThread());\n        entity.setQps(rule.getQps());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Double getAvgLoad() {\n        return avgLoad;\n    }\n\n    public void setAvgLoad(Double avgLoad) {\n        this.avgLoad = avgLoad;\n    }\n\n    public Long getAvgRt() {\n        return avgRt;\n    }\n\n    public void setAvgRt(Long avgRt) {\n        this.avgRt = avgRt;\n    }\n\n    public Long getMaxThread() {\n        return maxThread;\n    }\n\n    public void setMaxThread(Long maxThread) {\n        this.maxThread = maxThread;\n    }\n\n    public Double getQps() {\n        return qps;\n    }\n\n    public void setQps(Double qps) {\n        this.qps = qps;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public SystemRule toRule() {\n        SystemRule rule = new SystemRule();\n        rule.setHighestSystemLoad(avgLoad);\n        rule.setAvgRt(avgRt);\n        rule.setMaxThread(maxThread);\n        rule.setQps(qps);\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\npublic class AppInfo {\n\n    private String app = \"\";\n\n    private Set<MachineInfo> machines = ConcurrentHashMap.newKeySet();\n\n    public AppInfo() {}\n\n    public AppInfo(String app) {\n        this.app = app;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    /**\n     * Get the current machines.\n     *\n     * @return a new copy of the current machines.\n     */\n    public Set<MachineInfo> getMachines() {\n        return new HashSet<>(machines);\n    }\n\n    @Override\n    public String toString() {\n        return \"AppInfo{\" + \"app='\" + app + \", machines=\" + machines + '}';\n    }\n\n    public boolean addMachine(MachineInfo machineInfo) {\n        machines.remove(machineInfo);\n        return machines.add(machineInfo);\n    }\n\n    public synchronized boolean removeMachine(String ip, int port) {\n        Iterator<MachineInfo> it = machines.iterator();\n        while (it.hasNext()) {\n            MachineInfo machine = it.next();\n            if (machine.getIp().equals(ip) && machine.getPort() == port) {\n                it.remove();\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public Optional<MachineInfo> getMachine(String ip, int port) {\n        return machines.stream()\n            .filter(e -> e.getIp().equals(ip) && e.getPort().equals(port))\n            .findFirst();\n    }\n\n    private boolean heartbeatJudge(final int threshold) {\n        if (machines.size() == 0) {\n            return false;\n        }\n        if (threshold > 0) {\n            long healthyCount = machines.stream()\n                .filter(MachineInfo::isHealthy)\n                .count();\n            if (healthyCount == 0) {\n                // No healthy machines.\n                return machines.stream()\n                    .max(Comparator.comparingLong(MachineInfo::getLastHeartbeat))\n                    .map(e -> System.currentTimeMillis() - e.getLastHeartbeat() < threshold)\n                    .orElse(false);\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Check whether current application has no healthy machines and should not be displayed.\n     *\n     * @return true if the application should be displayed in the sidebar, otherwise false\n     */\n    public boolean isShown() {\n        return heartbeatJudge(DashboardConfig.getHideAppNoMachineMillis());\n    }\n\n    /**\n     * Check whether current application has no healthy machines and should be removed.\n     *\n     * @return true if the application is dead and should be removed, otherwise false\n     */\n    public boolean isDead() {\n        return !heartbeatJudge(DashboardConfig.getRemoveAppNoMachineMillis());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.annotation.PostConstruct;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AppManagement implements MachineDiscovery {\n\n    @Autowired\n    private ApplicationContext context;\n\n    private MachineDiscovery machineDiscovery;\n\n    @PostConstruct\n    public void init() {\n        machineDiscovery = context.getBean(SimpleMachineDiscovery.class);\n    }\n\n    @Override\n    public Set<AppInfo> getBriefApps() {\n        return machineDiscovery.getBriefApps();\n    }\n\n    @Override\n    public long addMachine(MachineInfo machineInfo) {\n        return machineDiscovery.addMachine(machineInfo);\n    }\n    \n    @Override\n    public boolean removeMachine(String app, String ip, int port) {\n        return machineDiscovery.removeMachine(app, ip, port);\n    }\n\n    @Override\n    public List<String> getAppNames() {\n        return machineDiscovery.getAppNames();\n    }\n\n    @Override\n    public AppInfo getDetailApp(String app) {\n        return machineDiscovery.getDetailApp(app);\n    }\n    \n    @Override\n    public void removeApp(String app) {\n        machineDiscovery.removeApp(app);\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic interface MachineDiscovery {\n\n    String UNKNOWN_APP_NAME = \"CLUSTER_NOT_STARTED\";\n\n    List<String> getAppNames();\n\n    Set<AppInfo> getBriefApps();\n\n    AppInfo getDetailApp(String app);\n\n    /**\n     * Remove the given app from the application registry.\n     *\n     * @param app application name\n     * @since 1.5.0\n     */\n    void removeApp(String app);\n\n    long addMachine(MachineInfo machineInfo);\n\n    /**\n     * Remove the given machine instance from the application registry.\n     *\n     * @param app the application name of the machine\n     * @param ip machine IP\n     * @param port machine port\n     * @return true if removed, otherwise false\n     * @since 1.5.0\n     */\n    boolean removeMachine(String app, String ip, int port);\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.Objects;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\npublic class MachineInfo implements Comparable<MachineInfo> {\n\n    private String app = \"\";\n    private String hostname = \"\";\n    private String ip = \"\";\n    private Integer port = -1;\n    private long lastHeartbeat;\n    private long heartbeatVersion;\n\n    /**\n     * Indicates the version of Sentinel client (since 0.2.0).\n     */\n    private String version;\n\n    public static MachineInfo of(String app, String ip, Integer port) {\n        MachineInfo machineInfo = new MachineInfo();\n        machineInfo.setApp(app);\n        machineInfo.setIp(ip);\n        machineInfo.setPort(port);\n        return machineInfo;\n    }\n\n    public String toHostPort() {\n        return ip + \":\" + port;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public long getHeartbeatVersion() {\n        return heartbeatVersion;\n    }\n    \n    public void setHeartbeatVersion(long heartbeatVersion) {\n        this.heartbeatVersion = heartbeatVersion;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public MachineInfo setVersion(String version) {\n        this.version = version;\n        return this;\n    }\n    \n    public boolean isHealthy() {\n        long delta = System.currentTimeMillis() - lastHeartbeat;\n        return delta < DashboardConfig.getUnhealthyMachineMillis();\n    }\n    \n    /**\n     * whether dead should be removed\n     * \n     * @return\n     */\n    public boolean isDead() {\n        if (DashboardConfig.getAutoRemoveMachineMillis() > 0) {\n            long delta = System.currentTimeMillis() - lastHeartbeat;\n            return delta > DashboardConfig.getAutoRemoveMachineMillis();\n        }\n        return false;\n    }\n    \n    public long getLastHeartbeat() {\n        return lastHeartbeat;\n    }\n    \n    public void setLastHeartbeat(long lastHeartbeat) {\n        this.lastHeartbeat = lastHeartbeat;\n    }\n\n    @Override\n    public int compareTo(MachineInfo o) {\n        if (this == o) {\n            return 0;\n        }\n        if (!port.equals(o.getPort())) {\n            return port.compareTo(o.getPort());\n        }\n        if (!StringUtil.equals(app, o.getApp())) {\n            return app.compareToIgnoreCase(o.getApp());\n        }\n        return ip.compareToIgnoreCase(o.getIp());\n    }\n\n    @Override\n    public String toString() {\n        return new StringBuilder(\"MachineInfo {\")\n            .append(\"app='\").append(app).append('\\'')\n            .append(\", hostname='\").append(hostname).append('\\'')\n            .append(\", ip='\").append(ip).append('\\'')\n            .append(\", port=\").append(port)\n            .append(\", heartbeatVersion=\").append(heartbeatVersion)\n            .append(\", lastHeartbeat=\").append(lastHeartbeat)\n            .append(\", version='\").append(version).append('\\'')\n            .append(\", healthy=\").append(isHealthy())\n            .append('}').toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) { return true; }\n        if (!(o instanceof MachineInfo)) { return false; }\n        MachineInfo that = (MachineInfo)o;\n        return Objects.equals(app, that.app) &&\n            Objects.equals(ip, that.ip) &&\n            Objects.equals(port, that.port);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(app, ip, port);\n    }\n\n    /**\n     * Information for log\n     *\n     * @return\n     */\n    public String toLogString() {\n        return app + \"|\" + ip + \"|\" + port + \"|\" + version;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class SimpleMachineDiscovery implements MachineDiscovery {\n\n    private final ConcurrentMap<String, AppInfo> apps = new ConcurrentHashMap<>();\n\n    @Override\n    public long addMachine(MachineInfo machineInfo) {\n        AssertUtil.notNull(machineInfo, \"machineInfo cannot be null\");\n        AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), AppInfo::new);\n        appInfo.addMachine(machineInfo);\n        return 1;\n    }\n\n    @Override\n    public boolean removeMachine(String app, String ip, int port) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        AppInfo appInfo = apps.get(app);\n        if (appInfo != null) {\n            return appInfo.removeMachine(ip, port);\n        }\n        return false;\n    }\n\n    @Override\n    public List<String> getAppNames() {\n        return new ArrayList<>(apps.keySet());\n    }\n\n    @Override\n    public AppInfo getDetailApp(String app) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        return apps.get(app);\n    }\n\n    @Override\n    public Set<AppInfo> getBriefApps() {\n        return new HashSet<>(apps.values());\n    }\n\n    @Override\n    public void removeApp(String app) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        apps.remove(app);\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/ResourceTreeNode.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\n/**\n * @author leyou\n */\npublic class ResourceTreeNode {\n    private String id;\n    private String parentId;\n    private String resource;\n\n    private Integer threadNum;\n    private Long passQps;\n    private Long blockQps;\n    private Long totalQps;\n    private Long averageRt;\n    private Long successQps;\n    private Long exceptionQps;\n    private Long oneMinutePass;\n    private Long oneMinuteBlock;\n    private Long oneMinuteException;\n    private Long oneMinuteTotal;\n\n    private boolean visible = true;\n\n    private List<ResourceTreeNode> children = new ArrayList<>();\n\n    public static ResourceTreeNode fromNodeVoList(List<NodeVo> nodeVos) {\n        if (nodeVos == null || nodeVos.isEmpty()) {\n            return null;\n        }\n        ResourceTreeNode root = null;\n        Map<String, ResourceTreeNode> map = new HashMap<>();\n        for (NodeVo vo : nodeVos) {\n            ResourceTreeNode node = fromNodeVo(vo);\n            map.put(node.id, node);\n            // real root\n            if (node.parentId == null) {\n                root = node;\n            } else if (map.containsKey(node.parentId)) {\n                map.get(node.parentId).children.add(node);\n            } else {\n                // impossible\n            }\n        }\n        return root;\n    }\n\n    public static ResourceTreeNode fromNodeVo(NodeVo vo) {\n        ResourceTreeNode node = new ResourceTreeNode();\n        node.id = vo.getId();\n        node.parentId = vo.getParentId();\n        node.resource = vo.getResource();\n        node.threadNum = vo.getThreadNum();\n        node.passQps = vo.getPassQps();\n        node.blockQps = vo.getBlockQps();\n        node.totalQps = vo.getTotalQps();\n        node.averageRt = vo.getAverageRt();\n        node.successQps = vo.getSuccessQps();\n        node.exceptionQps = vo.getExceptionQps();\n        node.oneMinutePass = vo.getOneMinutePass();\n        node.oneMinuteBlock = vo.getOneMinuteBlock();\n        node.oneMinuteException = vo.getOneMinuteException();\n        node.oneMinuteTotal = vo.getOneMinuteTotal();\n        return node;\n    }\n\n    public void searchIgnoreCase(String searchKey) {\n        search(this, searchKey);\n    }\n\n    /**\n     * This node is visible only when searchKey matches this.resource or at least\n     * one of this's children is visible\n     */\n    private boolean search(ResourceTreeNode node, String searchKey) {\n        // empty matches all\n        if (searchKey == null || searchKey.isEmpty() ||\n            node.resource.toLowerCase().contains(searchKey.toLowerCase())) {\n            node.visible = true;\n        } else {\n            node.visible = false;\n        }\n\n        boolean found = false;\n        for (ResourceTreeNode c : node.children) {\n            found |= search(c, searchKey);\n        }\n        node.visible |= found;\n        return node.visible;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getParentId() {\n        return parentId;\n    }\n\n    public void setParentId(String parentId) {\n        this.parentId = parentId;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getThreadNum() {\n        return threadNum;\n    }\n\n    public void setThreadNum(Integer threadNum) {\n        this.threadNum = threadNum;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getTotalQps() {\n        return totalQps;\n    }\n\n    public void setTotalQps(Long totalQps) {\n        this.totalQps = totalQps;\n    }\n\n    public Long getAverageRt() {\n        return averageRt;\n    }\n\n    public void setAverageRt(Long averageRt) {\n        this.averageRt = averageRt;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Long getOneMinutePass() {\n        return oneMinutePass;\n    }\n\n    public void setOneMinutePass(Long oneMinutePass) {\n        this.oneMinutePass = oneMinutePass;\n    }\n\n    public Long getOneMinuteBlock() {\n        return oneMinuteBlock;\n    }\n\n    public void setOneMinuteBlock(Long oneMinuteBlock) {\n        this.oneMinuteBlock = oneMinuteBlock;\n    }\n\n    public Long getOneMinuteException() {\n        return oneMinuteException;\n    }\n\n    public void setOneMinuteException(Long oneMinuteException) {\n        this.oneMinuteException = oneMinuteException;\n    }\n\n    public Long getOneMinuteTotal() {\n        return oneMinuteTotal;\n    }\n\n    public void setOneMinuteTotal(Long oneMinuteTotal) {\n        this.oneMinuteTotal = oneMinuteTotal;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public void setVisible(boolean visible) {\n        this.visible = visible;\n    }\n\n    public List<ResourceTreeNode> getChildren() {\n        return children;\n    }\n\n    public void setChildren(List<ResourceTreeNode> children) {\n        this.children = children;\n    }\n}\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/Result.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain;\n\n/**\n * @author leyou\n * @author Eric Zhao\n */\npublic class Result<R> {\n\n    private boolean success;\n    private int code;\n    private String msg;\n    private R data;\n\n    public static <R> Result<R> ofSuccess(R data) {\n        return new Result<R>()\n            .setSuccess(true)\n            .setMsg(\"success\")\n            .setData(data);\n    }\n\n    public static <R> Result<R> ofSuccessMsg(String msg) {\n        return new Result<R>()\n            .setSuccess(true)\n            .setMsg(msg);\n    }\n\n    public static <R> Result<R> ofFail(int code, String msg) {\n        Result<R> result = new Result<>();\n        result.setSuccess(false);\n        result.setCode(code);\n        result.setMsg(msg);\n        return result;\n    }\n\n    public static <R> Result<R> ofThrowable(int code, Throwable throwable) {\n        Result<R> result = new Result<>();\n        result.setSuccess(false);\n        result.setCode(code);\n        result.setMsg(throwable.getClass().getName() + \", \" + throwable.getMessage());\n        return result;\n    }\n\n    public boolean isSuccess() {\n        return success;\n    }\n\n    public Result<R> setSuccess(boolean success) {\n        this.success = success;\n        return this;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public Result<R> setCode(int code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMsg() {\n        return msg;\n    }\n\n    public Result<R> setMsg(String msg) {\n        this.msg = msg;\n        return this;\n    }\n\n    public R getData() {\n        return data;\n    }\n\n    public Result<R> setData(R data) {\n        this.data = data;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"Result{\" +\n            \"success=\" + success +\n            \", code=\" + code +\n            \", msg='\" + msg + '\\'' +\n            \", data=\" + data +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppAssignResultVO {\n\n    private Set<String> failedServerSet;\n    private Set<String> failedClientSet;\n\n    private Integer totalCount;\n\n    public Set<String> getFailedServerSet() {\n        return failedServerSet;\n    }\n\n    public ClusterAppAssignResultVO setFailedServerSet(Set<String> failedServerSet) {\n        this.failedServerSet = failedServerSet;\n        return this;\n    }\n\n    public Set<String> getFailedClientSet() {\n        return failedClientSet;\n    }\n\n    public ClusterAppAssignResultVO setFailedClientSet(Set<String> failedClientSet) {\n        this.failedClientSet = failedClientSet;\n        return this;\n    }\n\n    public Integer getTotalCount() {\n        return totalCount;\n    }\n\n    public ClusterAppAssignResultVO setTotalCount(Integer totalCount) {\n        this.totalCount = totalCount;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppAssignResultVO{\" +\n            \"failedServerSet=\" + failedServerSet +\n            \", failedClientSet=\" + failedClientSet +\n            \", totalCount=\" + totalCount +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppFullAssignRequest {\n\n    private List<ClusterAppAssignMap> clusterMap;\n    private Set<String> remainingList;\n\n    public List<ClusterAppAssignMap> getClusterMap() {\n        return clusterMap;\n    }\n\n    public ClusterAppFullAssignRequest setClusterMap(\n        List<ClusterAppAssignMap> clusterMap) {\n        this.clusterMap = clusterMap;\n        return this;\n    }\n\n    public Set<String> getRemainingList() {\n        return remainingList;\n    }\n\n    public ClusterAppFullAssignRequest setRemainingList(Set<String> remainingList) {\n        this.remainingList = remainingList;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppFullAssignRequest{\" +\n            \"clusterMap=\" + clusterMap +\n            \", remainingList=\" + remainingList +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppSingleServerAssignRequest {\n\n    private ClusterAppAssignMap clusterMap;\n    private Set<String> remainingList;\n\n    public ClusterAppAssignMap getClusterMap() {\n        return clusterMap;\n    }\n\n    public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) {\n        this.clusterMap = clusterMap;\n        return this;\n    }\n\n    public Set<String> getRemainingList() {\n        return remainingList;\n    }\n\n    public ClusterAppSingleServerAssignRequest setRemainingList(Set<String> remainingList) {\n        this.remainingList = remainingList;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppSingleServerAssignRequest{\" +\n            \"clusterMap=\" + clusterMap +\n            \", remainingList=\" + remainingList +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterClientInfoVO {\n\n    private String serverHost;\n    private Integer serverPort;\n\n    private Integer clientState;\n\n    private Integer requestTimeout;\n\n    public String getServerHost() {\n        return serverHost;\n    }\n\n    public ClusterClientInfoVO setServerHost(String serverHost) {\n        this.serverHost = serverHost;\n        return this;\n    }\n\n    public Integer getServerPort() {\n        return serverPort;\n    }\n\n    public ClusterClientInfoVO setServerPort(Integer serverPort) {\n        this.serverPort = serverPort;\n        return this;\n    }\n\n    public Integer getClientState() {\n        return clientState;\n    }\n\n    public ClusterClientInfoVO setClientState(Integer clientState) {\n        this.clientState = clientState;\n        return this;\n    }\n\n    public Integer getRequestTimeout() {\n        return requestTimeout;\n    }\n\n    public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) {\n        this.requestTimeout = requestTimeout;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientInfoVO{\" +\n            \"serverHost='\" + serverHost + '\\'' +\n            \", serverPort=\" + serverPort +\n            \", clientState=\" + clientState +\n            \", requestTimeout=\" + requestTimeout +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterGroupEntity {\n\n    private String machineId;\n\n    private String ip;\n    private Integer port;\n\n    private Set<String> clientSet = new HashSet<>();\n\n    private Boolean belongToApp;\n\n    public String getMachineId() {\n        return machineId;\n    }\n\n    public ClusterGroupEntity setMachineId(String machineId) {\n        this.machineId = machineId;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterGroupEntity setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterGroupEntity setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Set<String> getClientSet() {\n        return clientSet;\n    }\n\n    public ClusterGroupEntity setClientSet(Set<String> clientSet) {\n        this.clientSet = clientSet;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public ClusterGroupEntity setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterGroupEntity{\" +\n            \"machineId='\" + machineId + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", clientSet=\" + clientSet +\n            \", belongToApp=\" + belongToApp +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterStateSingleVO {\n\n    private String address;\n    private Integer mode;\n    private String target;\n\n    public String getAddress() {\n        return address;\n    }\n\n    public ClusterStateSingleVO setAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterStateSingleVO setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public String getTarget() {\n        return target;\n    }\n\n    public ClusterStateSingleVO setTarget(String target) {\n        this.target = target;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterStateSingleVO{\" +\n            \"address='\" + address + '\\'' +\n            \", mode=\" + mode +\n            \", target='\" + target + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ConnectionDescriptorVO {\n\n    private String address;\n    private String host;\n\n    public String getAddress() {\n        return address;\n    }\n\n    public ConnectionDescriptorVO setAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public String getHost() {\n        return host;\n    }\n\n    public ConnectionDescriptorVO setHost(String host) {\n        this.host = host;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConnectionDescriptorVO{\" +\n            \"address='\" + address + '\\'' +\n            \", host='\" + host + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.List;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ConnectionGroupVO {\n\n    private String namespace;\n    private List<ConnectionDescriptorVO> connectionSet;\n    private Integer connectedCount;\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public ConnectionGroupVO setNamespace(String namespace) {\n        this.namespace = namespace;\n        return this;\n    }\n\n    public List<ConnectionDescriptorVO> getConnectionSet() {\n        return connectionSet;\n    }\n\n    public ConnectionGroupVO setConnectionSet(\n        List<ConnectionDescriptorVO> connectionSet) {\n        this.connectionSet = connectionSet;\n        return this;\n    }\n\n    public Integer getConnectedCount() {\n        return connectedCount;\n    }\n\n    public ConnectionGroupVO setConnectedCount(Integer connectedCount) {\n        this.connectedCount = connectedCount;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConnectionGroupVO{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", connectionSet=\" + connectionSet +\n            \", connectedCount=\" + connectedCount +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientConfig {\n\n    private String serverHost;\n    private Integer serverPort;\n\n    private Integer requestTimeout;\n    private Integer connectTimeout;\n\n    public String getServerHost() {\n        return serverHost;\n    }\n\n    public ClusterClientConfig setServerHost(String serverHost) {\n        this.serverHost = serverHost;\n        return this;\n    }\n\n    public Integer getServerPort() {\n        return serverPort;\n    }\n\n    public ClusterClientConfig setServerPort(Integer serverPort) {\n        this.serverPort = serverPort;\n        return this;\n    }\n\n    public Integer getRequestTimeout() {\n        return requestTimeout;\n    }\n\n    public ClusterClientConfig setRequestTimeout(Integer requestTimeout) {\n        this.requestTimeout = requestTimeout;\n        return this;\n    }\n\n    public Integer getConnectTimeout() {\n        return connectTimeout;\n    }\n\n    public ClusterClientConfig setConnectTimeout(Integer connectTimeout) {\n        this.connectTimeout = connectTimeout;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientConfig{\" +\n            \"serverHost='\" + serverHost + '\\'' +\n            \", serverPort=\" + serverPort +\n            \", requestTimeout=\" + requestTimeout +\n            \", connectTimeout=\" + connectTimeout +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ServerFlowConfig {\n\n    public static final double DEFAULT_EXCEED_COUNT = 1.0d;\n    public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d;\n\n    public static final int DEFAULT_INTERVAL_MS = 1000;\n    public static final int DEFAULT_SAMPLE_COUNT= 10;\n    public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;\n\n    private final String namespace;\n\n    private Double exceedCount = DEFAULT_EXCEED_COUNT;\n    private Double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO;\n    private Integer intervalMs = DEFAULT_INTERVAL_MS;\n    private Integer sampleCount = DEFAULT_SAMPLE_COUNT;\n\n    private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;\n\n    public ServerFlowConfig() {\n        this(\"default\");\n    }\n\n    public ServerFlowConfig(String namespace) {\n        this.namespace = namespace;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public Double getExceedCount() {\n        return exceedCount;\n    }\n\n    public ServerFlowConfig setExceedCount(Double exceedCount) {\n        this.exceedCount = exceedCount;\n        return this;\n    }\n\n    public Double getMaxOccupyRatio() {\n        return maxOccupyRatio;\n    }\n\n    public ServerFlowConfig setMaxOccupyRatio(Double maxOccupyRatio) {\n        this.maxOccupyRatio = maxOccupyRatio;\n        return this;\n    }\n\n    public Integer getIntervalMs() {\n        return intervalMs;\n    }\n\n    public ServerFlowConfig setIntervalMs(Integer intervalMs) {\n        this.intervalMs = intervalMs;\n        return this;\n    }\n\n    public Integer getSampleCount() {\n        return sampleCount;\n    }\n\n    public ServerFlowConfig setSampleCount(Integer sampleCount) {\n        this.sampleCount = sampleCount;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ServerFlowConfig{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", exceedCount=\" + exceedCount +\n            \", maxOccupyRatio=\" + maxOccupyRatio +\n            \", intervalMs=\" + intervalMs +\n            \", sampleCount=\" + sampleCount +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ServerTransportConfig {\n\n    public static final int DEFAULT_PORT = 18730;\n    public static final int DEFAULT_IDLE_SECONDS = 600;\n\n    private Integer port;\n    private Integer idleSeconds;\n\n    public ServerTransportConfig() {\n        this(DEFAULT_PORT, DEFAULT_IDLE_SECONDS);\n    }\n\n    public ServerTransportConfig(Integer port, Integer idleSeconds) {\n        this.port = port;\n        this.idleSeconds = idleSeconds;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ServerTransportConfig setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Integer getIdleSeconds() {\n        return idleSeconds;\n    }\n\n    public ServerTransportConfig setIdleSeconds(Integer idleSeconds) {\n        this.idleSeconds = idleSeconds;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ServerTransportConfig{\" +\n            \"port=\" + port +\n            \", idleSeconds=\" + idleSeconds +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppAssignMap {\n\n    private String machineId;\n    private String ip;\n    private Integer port;\n\n    private Boolean belongToApp;\n\n    private Set<String> clientSet;\n\n    private Set<String> namespaceSet;\n    private Double maxAllowedQps;\n\n    public String getMachineId() {\n        return machineId;\n    }\n\n    public ClusterAppAssignMap setMachineId(String machineId) {\n        this.machineId = machineId;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterAppAssignMap setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterAppAssignMap setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Set<String> getClientSet() {\n        return clientSet;\n    }\n\n    public ClusterAppAssignMap setClientSet(Set<String> clientSet) {\n        this.clientSet = clientSet;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterAppAssignMap setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppAssignMap{\" +\n            \"machineId='\" + machineId + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", belongToApp=\" + belongToApp +\n            \", clientSet=\" + clientSet +\n            \", namespaceSet=\" + namespaceSet +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientModifyRequest implements ClusterModifyRequest {\n\n    private String app;\n    private String ip;\n    private Integer port;\n\n    private Integer mode;\n    private ClusterClientConfig clientConfig;\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public ClusterClientModifyRequest setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterClientModifyRequest setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterClientModifyRequest setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    @Override\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterClientModifyRequest setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public ClusterClientConfig getClientConfig() {\n        return clientConfig;\n    }\n\n    public ClusterClientModifyRequest setClientConfig(\n        ClusterClientConfig clientConfig) {\n        this.clientConfig = clientConfig;\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface ClusterModifyRequest {\n\n    String getApp();\n\n    String getIp();\n\n    Integer getPort();\n\n    Integer getMode();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterServerModifyRequest implements ClusterModifyRequest {\n\n    private String app;\n    private String ip;\n    private Integer port;\n\n    private Integer mode;\n    private ServerFlowConfig flowConfig;\n    private ServerTransportConfig transportConfig;\n    private Set<String> namespaceSet;\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public ClusterServerModifyRequest setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterServerModifyRequest setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterServerModifyRequest setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    @Override\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterServerModifyRequest setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public ServerFlowConfig getFlowConfig() {\n        return flowConfig;\n    }\n\n    public ClusterServerModifyRequest setFlowConfig(\n        ServerFlowConfig flowConfig) {\n        this.flowConfig = flowConfig;\n        return this;\n    }\n\n    public ServerTransportConfig getTransportConfig() {\n        return transportConfig;\n    }\n\n    public ClusterServerModifyRequest setTransportConfig(\n        ServerTransportConfig transportConfig) {\n        this.transportConfig = transportConfig;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterServerModifyRequest setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterServerModifyRequest{\" +\n            \"app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", mode=\" + mode +\n            \", flowConfig=\" + flowConfig +\n            \", transportConfig=\" + transportConfig +\n            \", namespaceSet=\" + namespaceSet +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class AppClusterClientStateWrapVO {\n\n    /**\n     * {ip}@{transport_command_port}.\n     */\n    private String id;\n\n    private Integer commandPort;\n    private String ip;\n\n    private ClusterClientStateVO state;\n\n    public String getId() {\n        return id;\n    }\n\n    public AppClusterClientStateWrapVO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public AppClusterClientStateWrapVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public ClusterClientStateVO getState() {\n        return state;\n    }\n\n    public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    public Integer getCommandPort() {\n        return commandPort;\n    }\n\n    public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) {\n        this.commandPort = commandPort;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AppClusterClientStateWrapVO{\" +\n            \"id='\" + id + '\\'' +\n            \", commandPort=\" + commandPort +\n            \", ip='\" + ip + '\\'' +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class AppClusterServerStateWrapVO {\n\n    /**\n     * {ip}@{transport_command_port}.\n     */\n    private String id;\n\n    private String ip;\n    private Integer port;\n\n    private Integer connectedCount;\n\n    private Boolean belongToApp;\n\n    private ClusterServerStateVO state;\n\n    public String getId() {\n        return id;\n    }\n\n    public AppClusterServerStateWrapVO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public AppClusterServerStateWrapVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public AppClusterServerStateWrapVO setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    public Integer getConnectedCount() {\n        return connectedCount;\n    }\n\n    public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) {\n        this.connectedCount = connectedCount;\n        return this;\n    }\n\n    public ClusterServerStateVO getState() {\n        return state;\n    }\n\n    public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AppClusterServerStateWrapVO{\" +\n            \"id='\" + id + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port='\" + port + '\\'' +\n            \", belongToApp=\" + belongToApp +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientStateVO {\n\n    /**\n     * Cluster token client state.\n     */\n    private ClusterClientInfoVO clientConfig;\n\n    public ClusterClientInfoVO getClientConfig() {\n        return clientConfig;\n    }\n\n    public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) {\n        this.clientConfig = clientConfig;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientStateVO{\" +\n            \"clientConfig=\" + clientConfig +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterRequestLimitVO {\n\n    private String namespace;\n    private Double currentQps;\n    private Double maxAllowedQps;\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public ClusterRequestLimitVO setNamespace(String namespace) {\n        this.namespace = namespace;\n        return this;\n    }\n\n    public Double getCurrentQps() {\n        return currentQps;\n    }\n\n    public ClusterRequestLimitVO setCurrentQps(Double currentQps) {\n        this.currentQps = currentQps;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterRequestLimitVO{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", currentQps=\" + currentQps +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterServerStateVO {\n\n    private String appName;\n\n    private ServerTransportConfig transport;\n    private ServerFlowConfig flow;\n    private Set<String> namespaceSet;\n\n    private Integer port;\n\n    private List<ConnectionGroupVO> connection;\n    private List<ClusterRequestLimitVO> requestLimitData;\n\n    private Boolean embedded;\n\n    public String getAppName() {\n        return appName;\n    }\n\n    public ClusterServerStateVO setAppName(String appName) {\n        this.appName = appName;\n        return this;\n    }\n\n    public ServerTransportConfig getTransport() {\n        return transport;\n    }\n\n    public ClusterServerStateVO setTransport(ServerTransportConfig transport) {\n        this.transport = transport;\n        return this;\n    }\n\n    public ServerFlowConfig getFlow() {\n        return flow;\n    }\n\n    public ClusterServerStateVO setFlow(ServerFlowConfig flow) {\n        this.flow = flow;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterServerStateVO setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterServerStateVO setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public List<ConnectionGroupVO> getConnection() {\n        return connection;\n    }\n\n    public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) {\n        this.connection = connection;\n        return this;\n    }\n\n    public List<ClusterRequestLimitVO> getRequestLimitData() {\n        return requestLimitData;\n    }\n\n    public ClusterServerStateVO setRequestLimitData(List<ClusterRequestLimitVO> requestLimitData) {\n        this.requestLimitData = requestLimitData;\n        return this;\n    }\n\n    public Boolean getEmbedded() {\n        return embedded;\n    }\n\n    public ClusterServerStateVO setEmbedded(Boolean embedded) {\n        this.embedded = embedded;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterServerStateVO{\" +\n            \"appName='\" + appName + '\\'' +\n            \", transport=\" + transport +\n            \", flow=\" + flow +\n            \", namespaceSet=\" + namespaceSet +\n            \", port=\" + port +\n            \", connection=\" + connection +\n            \", requestLimitData=\" + requestLimitData +\n            \", embedded=\" + embedded +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterStateSimpleEntity {\n\n    private Integer mode;\n    private Long lastModified;\n    private Boolean clientAvailable;\n    private Boolean serverAvailable;\n\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterStateSimpleEntity setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public Long getLastModified() {\n        return lastModified;\n    }\n\n    public ClusterStateSimpleEntity setLastModified(Long lastModified) {\n        this.lastModified = lastModified;\n        return this;\n    }\n\n    public Boolean getClientAvailable() {\n        return clientAvailable;\n    }\n\n    public ClusterStateSimpleEntity setClientAvailable(Boolean clientAvailable) {\n        this.clientAvailable = clientAvailable;\n        return this;\n    }\n\n    public Boolean getServerAvailable() {\n        return serverAvailable;\n    }\n\n    public ClusterStateSimpleEntity setServerAvailable(Boolean serverAvailable) {\n        this.serverAvailable = serverAvailable;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterStateSimpleEntity{\" +\n            \"mode=\" + mode +\n            \", lastModified=\" + lastModified +\n            \", clientAvailable=\" + clientAvailable +\n            \", serverAvailable=\" + serverAvailable +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterUniversalStatePairVO {\n\n    private String ip;\n    private Integer commandPort;\n\n    private ClusterUniversalStateVO state;\n\n    public ClusterUniversalStatePairVO() {}\n\n    public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) {\n        this.ip = ip;\n        this.commandPort = commandPort;\n        this.state = state;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterUniversalStatePairVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getCommandPort() {\n        return commandPort;\n    }\n\n    public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) {\n        this.commandPort = commandPort;\n        return this;\n    }\n\n    public ClusterUniversalStateVO getState() {\n        return state;\n    }\n\n    public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterUniversalStatePairVO{\" +\n            \"ip='\" + ip + '\\'' +\n            \", commandPort=\" + commandPort +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterUniversalStateVO {\n\n    private ClusterStateSimpleEntity stateInfo;\n    private ClusterClientStateVO client;\n    private ClusterServerStateVO server;\n\n    public ClusterClientStateVO getClient() {\n        return client;\n    }\n\n    public ClusterUniversalStateVO setClient(ClusterClientStateVO client) {\n        this.client = client;\n        return this;\n    }\n\n    public ClusterServerStateVO getServer() {\n        return server;\n    }\n\n    public ClusterUniversalStateVO setServer(ClusterServerStateVO server) {\n        this.server = server;\n        return this;\n    }\n\n    public ClusterStateSimpleEntity getStateInfo() {\n        return stateInfo;\n    }\n\n    public ClusterUniversalStateVO setStateInfo(\n        ClusterStateSimpleEntity stateInfo) {\n        this.stateInfo = stateInfo;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterUniversalStateVO{\" +\n            \"stateInfo=\" + stateInfo +\n            \", client=\" + client +\n            \", server=\" + server +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * @author leyou\n */\npublic class MachineInfoVo {\n\n    private String app;\n    private String hostname;\n    private String ip;\n    private int port;\n    private long heartbeatVersion;\n    private long lastHeartbeat;\n    private boolean healthy;\n\n    private String version;\n\n    public static List<MachineInfoVo> fromMachineInfoList(List<MachineInfo> machines) {\n        List<MachineInfoVo> list = new ArrayList<>();\n        for (MachineInfo machine : machines) {\n            list.add(fromMachineInfo(machine));\n        }\n        return list;\n    }\n\n    public static MachineInfoVo fromMachineInfo(MachineInfo machine) {\n        MachineInfoVo vo = new MachineInfoVo();\n        vo.setApp(machine.getApp());\n        vo.setHostname(machine.getHostname());\n        vo.setIp(machine.getIp());\n        vo.setPort(machine.getPort());\n        vo.setLastHeartbeat(machine.getLastHeartbeat());\n        vo.setHeartbeatVersion(machine.getHeartbeatVersion());\n        vo.setVersion(machine.getVersion());\n        vo.setHealthy(machine.isHealthy());\n        return vo;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public long getLastHeartbeat() {\n        return lastHeartbeat;\n    }\n    \n    public void setLastHeartbeat(long lastHeartbeat) {\n        this.lastHeartbeat = lastHeartbeat;\n    }\n    \n    public void setHeartbeatVersion(long heartbeatVersion) {\n        this.heartbeatVersion = heartbeatVersion;\n    }\n    \n    public long getHeartbeatVersion() {\n        return heartbeatVersion;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public MachineInfoVo setVersion(String version) {\n        this.version = version;\n        return this;\n    }\n\n    public boolean isHealthy() {\n        return healthy;\n    }\n\n    public void setHealthy(boolean healthy) {\n        this.healthy = healthy;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MetricVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\n\n/**\n * @author leyou\n */\npublic class MetricVo implements Comparable<MetricVo> {\n    private Long id;\n    private String app;\n    private Long timestamp;\n    private Long gmtCreate = System.currentTimeMillis();\n    private String resource;\n    private Long passQps;\n    private Long blockQps;\n    private Long successQps;\n    private Long exceptionQps;\n    /**\n     * average rt\n     */\n    private Double rt;\n    private Integer count;\n\n    public MetricVo() {\n    }\n\n    public static List<MetricVo> fromMetricEntities(Collection<MetricEntity> entities) {\n        List<MetricVo> list = new ArrayList<>();\n        if (entities != null) {\n            for (MetricEntity entity : entities) {\n                list.add(fromMetricEntity(entity));\n            }\n        }\n        return list;\n    }\n\n    /**\n     * 保留资源名为identity的结果。\n     *\n     * @param entities 通过hashCode查找到的MetricEntities\n     * @param identity 真正需要查找的资源名\n     * @return\n     */\n    public static List<MetricVo> fromMetricEntities(Collection<MetricEntity> entities, String identity) {\n        List<MetricVo> list = new ArrayList<>();\n        if (entities != null) {\n            for (MetricEntity entity : entities) {\n                if (entity.getResource().equals(identity)) {\n                    list.add(fromMetricEntity(entity));\n                }\n            }\n        }\n        return list;\n    }\n\n    public static MetricVo fromMetricEntity(MetricEntity entity) {\n        MetricVo vo = new MetricVo();\n        vo.id = entity.getId();\n        vo.app = entity.getApp();\n        vo.timestamp = entity.getTimestamp().getTime();\n        vo.gmtCreate = entity.getGmtCreate().getTime();\n        vo.resource = entity.getResource();\n        vo.passQps = entity.getPassQps();\n        vo.blockQps = entity.getBlockQps();\n        vo.successQps = entity.getSuccessQps();\n        vo.exceptionQps = entity.getExceptionQps();\n        if (entity.getSuccessQps() != 0) {\n            vo.rt = entity.getRt() / entity.getSuccessQps();\n        } else {\n            vo.rt = 0D;\n        }\n        vo.count = entity.getCount();\n        return vo;\n    }\n\n    public static MetricVo parse(String line) {\n        String[] strs = line.split(\"\\\\|\");\n        long timestamp = Long.parseLong(strs[0]);\n        String identity = strs[1];\n        long passQps = Long.parseLong(strs[2]);\n        long blockQps = Long.parseLong(strs[3]);\n        long exception = Long.parseLong(strs[4]);\n        double rt = Double.parseDouble(strs[5]);\n        long successQps = Long.parseLong(strs[6]);\n        MetricVo vo = new MetricVo();\n        vo.timestamp = timestamp;\n        vo.resource = identity;\n        vo.passQps = passQps;\n        vo.blockQps = blockQps;\n        vo.successQps = successQps;\n        vo.exceptionQps = exception;\n        vo.rt = rt;\n        vo.count = 1;\n        return vo;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Long getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Long timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public Long getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Long gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Double getRt() {\n        return rt;\n    }\n\n    public void setRt(Double rt) {\n        this.rt = rt;\n    }\n\n    public Integer getCount() {\n        return count;\n    }\n\n    public void setCount(Integer count) {\n        this.count = count;\n    }\n\n    @Override\n    public int compareTo(MetricVo o) {\n        return Long.compare(this.timestamp, o.timestamp);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/ResourceVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode;\n\n/**\n * @author leyou\n */\npublic class ResourceVo {\n    private String parentTtId;\n    private String ttId;\n    private String resource;\n\n    private Integer threadNum;\n    private Long passQps;\n    private Long blockQps;\n    private Long totalQps;\n    private Long averageRt;\n    private Long passRequestQps;\n    private Long exceptionQps;\n    private Long oneMinutePass;\n    private Long oneMinuteBlock;\n    private Long oneMinuteException;\n    private Long oneMinuteTotal;\n\n    private boolean visible = true;\n\n    public ResourceVo() {\n    }\n\n    public static List<ResourceVo> fromNodeVoList(List<NodeVo> nodeVos) {\n        if (nodeVos == null) {\n            return null;\n        }\n        List<ResourceVo> list = new ArrayList<>();\n        for (NodeVo nodeVo : nodeVos) {\n            ResourceVo vo = new ResourceVo();\n            vo.parentTtId = nodeVo.getParentId();\n            vo.ttId = nodeVo.getId();\n            vo.resource = nodeVo.getResource();\n            vo.threadNum = nodeVo.getThreadNum();\n            vo.passQps = nodeVo.getPassQps();\n            vo.blockQps = nodeVo.getBlockQps();\n            vo.totalQps = nodeVo.getTotalQps();\n            vo.averageRt = nodeVo.getAverageRt();\n            vo.exceptionQps = nodeVo.getExceptionQps();\n            vo.oneMinutePass = nodeVo.getOneMinutePass();\n            vo.oneMinuteBlock = nodeVo.getOneMinuteBlock();\n            vo.oneMinuteException = nodeVo.getOneMinuteException();\n            vo.oneMinuteTotal = nodeVo.getOneMinuteTotal();\n            list.add(vo);\n        }\n        return list;\n    }\n\n    public static List<ResourceVo> fromResourceTreeNode(ResourceTreeNode root) {\n        if (root == null) {\n            return null;\n        }\n        List<ResourceVo> list = new ArrayList<>();\n        visit(root, list, false, true);\n        //if(!list.isEmpty()){\n        //    list.remove(0);\n        //}\n        return list;\n    }\n\n    /**\n     * This node is visible when this.visible==true or one of this's parents is visible,\n     * root node is always invisible.\n     */\n    private static void visit(ResourceTreeNode node, List<ResourceVo> list, boolean parentVisible, boolean isRoot) {\n        boolean visible = !isRoot && (node.isVisible() || parentVisible);\n        //boolean visible = node.isVisible();\n        if (visible) {\n            ResourceVo vo = new ResourceVo();\n            vo.parentTtId = node.getParentId();\n            vo.ttId = node.getId();\n            vo.resource = node.getResource();\n            vo.threadNum = node.getThreadNum();\n            vo.passQps = node.getPassQps();\n            vo.blockQps = node.getBlockQps();\n            vo.totalQps = node.getTotalQps();\n            vo.averageRt = node.getAverageRt();\n            vo.exceptionQps = node.getExceptionQps();\n            vo.oneMinutePass = node.getOneMinutePass();\n            vo.oneMinuteBlock = node.getOneMinuteBlock();\n            vo.oneMinuteException = node.getOneMinuteException();\n            vo.oneMinuteTotal = node.getOneMinuteTotal();\n            vo.visible = node.isVisible();\n            list.add(vo);\n        }\n        for (ResourceTreeNode c : node.getChildren()) {\n            visit(c, list, visible, false);\n        }\n    }\n\n    public String getParentTtId() {\n        return parentTtId;\n    }\n\n    public void setParentTtId(String parentTtId) {\n        this.parentTtId = parentTtId;\n    }\n\n    public String getTtId() {\n        return ttId;\n    }\n\n    public void setTtId(String ttId) {\n        this.ttId = ttId;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getThreadNum() {\n        return threadNum;\n    }\n\n    public void setThreadNum(Integer threadNum) {\n        this.threadNum = threadNum;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getTotalQps() {\n        return totalQps;\n    }\n\n    public void setTotalQps(Long totalQps) {\n        this.totalQps = totalQps;\n    }\n\n    public Long getAverageRt() {\n        return averageRt;\n    }\n\n    public void setAverageRt(Long averageRt) {\n        this.averageRt = averageRt;\n    }\n\n    public Long getPassRequestQps() {\n        return passRequestQps;\n    }\n\n    public void setPassRequestQps(Long passRequestQps) {\n        this.passRequestQps = passRequestQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Long getOneMinuteException() {\n        return oneMinuteException;\n    }\n\n    public void setOneMinuteException(Long oneMinuteException) {\n        this.oneMinuteException = oneMinuteException;\n    }\n\n    public Long getOneMinutePass() {\n        return oneMinutePass;\n    }\n\n    public void setOneMinutePass(Long oneMinutePass) {\n        this.oneMinutePass = oneMinutePass;\n    }\n\n    public Long getOneMinuteBlock() {\n        return oneMinuteBlock;\n    }\n\n    public void setOneMinuteBlock(Long oneMinuteBlock) {\n        this.oneMinuteBlock = oneMinuteBlock;\n    }\n\n    public Long getOneMinuteTotal() {\n        return oneMinuteTotal;\n    }\n\n    public void setOneMinuteTotal(Long oneMinuteTotal) {\n        this.oneMinuteTotal = oneMinuteTotal;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public void setVisible(boolean visible) {\n        this.visible = visible;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.filter;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * Servlet Filter that authenticate requests.\n *\n * Note:\n * Some urls are excluded as they needn't auth, such as:\n *\n * Index url: /\n * Authentication request url: /login,logout\n * Used for client: /registry/machine\n * Static resources: htm,html,js and so on.\n *\n * The excluded urls and urlSuffixes are configured in application.properties\n *\n * @author cdfive\n * @since 1.6.0\n */\n@Component\npublic class AuthFilter implements Filter {\n\n    private static final String URL_SUFFIX_DOT = \".\";\n\n    /**Some urls which needn't auth, such as /auth/login,/registry/machine and so on*/\n    @Value(\"#{'${auth.filter.exclude-urls}'.split(',')}\")\n    private List<String> authFilterExcludeUrls;\n\n    /**Some urls with suffixes which needn't auth, such as htm,html,js and so on*/\n    @Value(\"#{'${auth.filter.exclude-url-suffixes}'.split(',')}\")\n    private List<String> authFilterExcludeUrlSuffixes;\n\n    /**Authentication using AuthService interface*/\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n\n    }\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n        HttpServletRequest httpRequest = (HttpServletRequest) request;\n\n        String requestURI = httpRequest.getRequestURI();\n\n        // Exclude the urls which needn't auth\n        if (authFilterExcludeUrls.contains(requestURI)) {\n            chain.doFilter(request, response);\n            return;\n        }\n\n        // Exclude the urls with suffixes which needn't auth\n        for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {\n            if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {\n                continue;\n            }\n\n            // Add . for url suffix so that we needn't add . in property file\n            if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {\n                authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;\n            }\n\n            if (requestURI.endsWith(authFilterExcludeUrlSuffix)) {\n                chain.doFilter(request, response);\n                return;\n            }\n        }\n\n        AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);\n\n        HttpServletResponse httpResponse = (HttpServletResponse) response;\n        if (authUser == null) {\n            // If auth fail, set response status code to 401\n            httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n        } else {\n            chain.doFilter(request, response);\n        }\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.metric;\n\nimport java.net.ConnectException;\nimport java.net.SocketTimeoutException;\nimport java.nio.charset.Charset;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.node.metric.MetricNode;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.concurrent.FutureCallback;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.client.DefaultRedirectStrategy;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.apache.http.impl.nio.reactor.IOReactorConfig;\nimport org.apache.http.protocol.HTTP;\nimport org.apache.http.util.EntityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * Fetch metric of machines.\n *\n * @author leyou\n */\n@Component\npublic class MetricFetcher {\n\n    public static final String NO_METRICS = \"No metrics\";\n    private static final int HTTP_OK = 200;\n    private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;\n    private static final long FETCH_INTERVAL_SECOND = 6;\n    private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());\n    private final static String METRIC_URL_PATH = \"metric\";\n    private static Logger logger = LoggerFactory.getLogger(MetricFetcher.class);\n    private final long intervalSecond = 1;\n\n    private Map<String, AtomicLong> appLastFetchTime = new ConcurrentHashMap<>();\n\n    @Autowired\n    private MetricsRepository<MetricEntity> metricStore;\n    @Autowired\n    private AppManagement appManagement;\n\n    private CloseableHttpAsyncClient httpclient;\n\n    @SuppressWarnings(\"PMD.ThreadPoolCreationRule\")\n    private ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool(1,\n        new NamedThreadFactory(\"sentinel-dashboard-metrics-fetch-task\"));\n    private ExecutorService fetchService;\n    private ExecutorService fetchWorker;\n\n    public MetricFetcher() {\n        int cores = Runtime.getRuntime().availableProcessors() * 2;\n        long keepAliveTime = 0;\n        int queueSize = 2048;\n        RejectedExecutionHandler handler = new DiscardPolicy();\n        fetchService = new ThreadPoolExecutor(cores, cores,\n            keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),\n            new NamedThreadFactory(\"sentinel-dashboard-metrics-fetchService\"), handler);\n        fetchWorker = new ThreadPoolExecutor(cores, cores,\n            keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),\n            new NamedThreadFactory(\"sentinel-dashboard-metrics-fetchWorker\"), handler);\n        IOReactorConfig ioConfig = IOReactorConfig.custom()\n            .setConnectTimeout(3000)\n            .setSoTimeout(3000)\n            .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2)\n            .build();\n\n        httpclient = HttpAsyncClients.custom()\n            .setRedirectStrategy(new DefaultRedirectStrategy() {\n                @Override\n                protected boolean isRedirectable(final String method) {\n                    return false;\n                }\n            }).setMaxConnTotal(4000)\n            .setMaxConnPerRoute(1000)\n            .setDefaultIOReactorConfig(ioConfig)\n            .build();\n        httpclient.start();\n        start();\n    }\n\n    private void start() {\n        fetchScheduleService.scheduleAtFixedRate(() -> {\n            try {\n                fetchAllApp();\n            } catch (Exception e) {\n                logger.info(\"fetchAllApp error:\", e);\n            }\n        }, 10, intervalSecond, TimeUnit.SECONDS);\n    }\n\n    private void writeMetric(Map<String, MetricEntity> map) {\n        if (map.isEmpty()) {\n            return;\n        }\n        Date date = new Date();\n        for (MetricEntity entity : map.values()) {\n            entity.setGmtCreate(date);\n            entity.setGmtModified(date);\n        }\n        metricStore.saveAll(map.values());\n    }\n\n    /**\n     * Traverse each APP, and then pull the metric of all machines for that APP.\n     */\n    private void fetchAllApp() {\n        List<String> apps = appManagement.getAppNames();\n        if (apps == null) {\n            return;\n        }\n        for (final String app : apps) {\n            fetchService.submit(() -> {\n                try {\n                    doFetchAppMetric(app);\n                } catch (Exception e) {\n                    logger.error(\"fetchAppMetric error\", e);\n                }\n            });\n        }\n    }\n\n    /**\n     * fetch metric between [startTime, endTime], both side inclusive\n     */\n    private void fetchOnce(String app, long startTime, long endTime, int maxWaitSeconds) {\n        if (maxWaitSeconds <= 0) {\n            throw new IllegalArgumentException(\"maxWaitSeconds must > 0, but \" + maxWaitSeconds);\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        // auto remove for app\n        if (appInfo.isDead()) {\n            logger.info(\"Dead app removed: {}\", app);\n            appManagement.removeApp(app);\n            return;\n        }\n        Set<MachineInfo> machines = appInfo.getMachines();\n        logger.debug(\"enter fetchOnce(\" + app + \"), machines.size()=\" + machines.size()\n            + \", time intervalMs [\" + startTime + \", \" + endTime + \"]\");\n        if (machines.isEmpty()) {\n            return;\n        }\n        final String msg = \"fetch\";\n        AtomicLong unhealthy = new AtomicLong();\n        final AtomicLong success = new AtomicLong();\n        final AtomicLong fail = new AtomicLong();\n\n        long start = System.currentTimeMillis();\n        /** app_resource_timeSecond -> metric */\n        final Map<String, MetricEntity> metricMap = new ConcurrentHashMap<>(16);\n        final CountDownLatch latch = new CountDownLatch(machines.size());\n        for (final MachineInfo machine : machines) {\n            // auto remove\n            if (machine.isDead()) {\n                latch.countDown();\n                appManagement.getDetailApp(app).removeMachine(machine.getIp(), machine.getPort());\n                logger.info(\"Dead machine removed: {}:{} of {}\", machine.getIp(), machine.getPort(), app);\n                continue;\n            }\n            if (!machine.isHealthy()) {\n                latch.countDown();\n                unhealthy.incrementAndGet();\n                continue;\n            }\n            final String url = \"http://\" + machine.getIp() + \":\" + machine.getPort() + \"/\" + METRIC_URL_PATH\n                + \"?startTime=\" + startTime + \"&endTime=\" + endTime + \"&refetch=\" + false;\n            final HttpGet httpGet = new HttpGet(url);\n            httpGet.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);\n            httpclient.execute(httpGet, new FutureCallback<HttpResponse>() {\n                @Override\n                public void completed(final HttpResponse response) {\n                    try {\n                        handleResponse(response, machine, metricMap);\n                        success.incrementAndGet();\n                    } catch (Exception e) {\n                        logger.error(msg + \" metric \" + url + \" error:\", e);\n                    } finally {\n                        latch.countDown();\n                    }\n                }\n\n                @Override\n                public void failed(final Exception ex) {\n                    latch.countDown();\n                    fail.incrementAndGet();\n                    httpGet.abort();\n                    if (ex instanceof SocketTimeoutException) {\n                        logger.error(\"Failed to fetch metric from <{}>: socket timeout\", url);\n                    } else if (ex instanceof ConnectException) {\n                        logger.error(\"Failed to fetch metric from <{}> (ConnectionException: {})\", url, ex.getMessage());\n                    } else {\n                        logger.error(msg + \" metric \" + url + \" error\", ex);\n                    }\n                }\n\n                @Override\n                public void cancelled() {\n                    latch.countDown();\n                    fail.incrementAndGet();\n                    httpGet.abort();\n                }\n            });\n        }\n        try {\n            latch.await(maxWaitSeconds, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            logger.info(msg + \" metric, wait http client error:\", e);\n        }\n        long cost = System.currentTimeMillis() - start;\n        //logger.info(\"finished \" + msg + \" metric for \" + app + \", time intervalMs [\" + startTime + \", \" + endTime\n        //    + \"], total machines=\" + machines.size() + \", dead=\" + dead + \", fetch success=\"\n        //    + success + \", fetch fail=\" + fail + \", time cost=\" + cost + \" ms\");\n        writeMetric(metricMap);\n    }\n\n    private void doFetchAppMetric(final String app) {\n        long now = System.currentTimeMillis();\n        long lastFetchMs = now - MAX_LAST_FETCH_INTERVAL_MS;\n        if (appLastFetchTime.containsKey(app)) {\n            lastFetchMs = Math.max(lastFetchMs, appLastFetchTime.get(app).get() + 1000);\n        }\n        // trim milliseconds\n        lastFetchMs = lastFetchMs / 1000 * 1000;\n        long endTime = lastFetchMs + FETCH_INTERVAL_SECOND * 1000;\n        if (endTime > now - 1000 * 2) {\n            // to near\n            return;\n        }\n        // update last_fetch in advance.\n        appLastFetchTime.computeIfAbsent(app, a -> new AtomicLong()).set(endTime);\n        final long finalLastFetchMs = lastFetchMs;\n        final long finalEndTime = endTime;\n        try {\n            // do real fetch async\n            fetchWorker.submit(() -> {\n                try {\n                    fetchOnce(app, finalLastFetchMs, finalEndTime, 5);\n                } catch (Exception e) {\n                    logger.info(\"fetchOnce(\" + app + \") error\", e);\n                }\n            });\n        } catch (Exception e) {\n            logger.info(\"submit fetchOnce(\" + app + \") fail, intervalMs [\" + lastFetchMs + \", \" + endTime + \"]\", e);\n        }\n    }\n\n    private void handleResponse(final HttpResponse response, MachineInfo machine,\n                                Map<String, MetricEntity> metricMap) throws Exception {\n        int code = response.getStatusLine().getStatusCode();\n        if (code != HTTP_OK) {\n            return;\n        }\n        Charset charset = null;\n        try {\n            String contentTypeStr = response.getFirstHeader(\"Content-type\").getValue();\n            if (StringUtil.isNotEmpty(contentTypeStr)) {\n                ContentType contentType = ContentType.parse(contentTypeStr);\n                charset = contentType.getCharset();\n            }\n        } catch (Exception ignore) {\n        }\n        String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);\n        if (StringUtil.isEmpty(body) || body.startsWith(NO_METRICS)) {\n            //logger.info(machine.getApp() + \":\" + machine.getIp() + \":\" + machine.getPort() + \", bodyStr is empty\");\n            return;\n        }\n        String[] lines = body.split(\"\\n\");\n        //logger.info(machine.getApp() + \":\" + machine.getIp() + \":\" + machine.getPort() +\n        //    \", bodyStr.length()=\" + body.length() + \", lines=\" + lines.length);\n        handleBody(lines, machine, metricMap);\n    }\n\n    private void handleBody(String[] lines, MachineInfo machine, Map<String, MetricEntity> map) {\n        //logger.info(\"handleBody() lines=\" + lines.length + \", machine=\" + machine);\n        if (lines.length < 1) {\n            return;\n        }\n\n        for (String line : lines) {\n            try {\n                MetricNode node = MetricNode.fromThinString(line);\n                /**\n                 * aggregation metrics by app_resource_timeSecond, ignore ip and port.\n                 */\n                String key = buildMetricKey(machine.getApp(), node.getResource(), node.getTimestamp());\n                MetricEntity entity = map.get(key);\n                if (entity != null) {\n                    entity.addPassQps(node.getPassQps());\n                    entity.addBlockQps(node.getBlockQps());\n                    entity.addRtAndSuccessQps(node.getRt(), node.getSuccessQps());\n                    entity.addExceptionQps(node.getExceptionQps());\n                    entity.addCount(1);\n                } else {\n                    entity = new MetricEntity();\n                    entity.setApp(machine.getApp());\n                    entity.setTimestamp(new Date(node.getTimestamp()));\n                    entity.setPassQps(node.getPassQps());\n                    entity.setBlockQps(node.getBlockQps());\n                    entity.setRtAndSuccessQps(node.getRt(), node.getSuccessQps());\n                    entity.setExceptionQps(node.getExceptionQps());\n                    entity.setCount(1);\n                    entity.setResource(node.getResource());\n                    map.put(key, entity);\n                }\n            } catch (Exception e) {\n                logger.warn(\"handleBody line exception, machine: {}, line: {}\", machine.toLogString(), line);\n            }\n        }\n    }\n\n    private String buildMetricKey(String app, String resource, long timestamp) {\n        return app + \"__\" + resource + \"__\" + (timestamp / 1000);\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;\nimport org.springframework.stereotype.Component;\n\n/**\n * Caches metrics data in a period of time in memory.\n *\n * @author Carpenter Lee\n * @author Eric Zhao\n */\n@Component\npublic class InMemoryMetricsRepository implements MetricsRepository<MetricEntity> {\n\n    private static final long MAX_METRIC_LIVE_TIME_MS = 1000 * 60 * 5;\n\n    /**\n     * {@code app -> resource -> timestamp -> metric}\n     */\n    private Map<String, Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>>> allMetrics = new ConcurrentHashMap<>();\n\n\n\n    @Override\n    public synchronized void save(MetricEntity entity) {\n        if (entity == null || StringUtil.isBlank(entity.getApp())) {\n            return;\n        }\n        allMetrics.computeIfAbsent(entity.getApp(), e -> new ConcurrentHashMap<>(16))\n            .computeIfAbsent(entity.getResource(), e -> new ConcurrentLinkedHashMap.Builder<Long, MetricEntity>()\n                .maximumWeightedCapacity(MAX_METRIC_LIVE_TIME_MS).weigher((key, value) -> {\n                    // Metric older than {@link #MAX_METRIC_LIVE_TIME_MS} will be removed.\n                    int weight = (int)(System.currentTimeMillis() - key);\n                    // weight must be a number greater than or equal to one\n                    return Math.max(weight, 1);\n                }).build()).put(entity.getTimestamp().getTime(), entity);\n    }\n\n    @Override\n    public synchronized void saveAll(Iterable<MetricEntity> metrics) {\n        if (metrics == null) {\n            return;\n        }\n        metrics.forEach(this::save);\n    }\n\n    @Override\n    public synchronized List<MetricEntity> queryByAppAndResourceBetween(String app, String resource,\n                                                                        long startTime, long endTime) {\n        List<MetricEntity> results = new ArrayList<>();\n        if (StringUtil.isBlank(app)) {\n            return results;\n        }\n        Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMap = allMetrics.get(app);\n        if (resourceMap == null) {\n            return results;\n        }\n        ConcurrentLinkedHashMap<Long, MetricEntity> metricsMap = resourceMap.get(resource);\n        if (metricsMap == null) {\n            return results;\n        }\n        for (Entry<Long, MetricEntity> entry : metricsMap.entrySet()) {\n            if (entry.getKey() >= startTime && entry.getKey() <= endTime) {\n                results.add(entry.getValue());\n            }\n        }\n        return results;\n    }\n\n    @Override\n    public List<String> listResourcesOfApp(String app) {\n        List<String> results = new ArrayList<>();\n        if (StringUtil.isBlank(app)) {\n            return results;\n        }\n        // resource -> timestamp -> metric\n        Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMap = allMetrics.get(app);\n        if (resourceMap == null) {\n            return results;\n        }\n        final long minTimeMs = System.currentTimeMillis() - 1000 * 60;\n        Map<String, MetricEntity> resourceCount = new ConcurrentHashMap<>(32);\n\n        for (Entry<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMetrics : resourceMap.entrySet()) {\n            for (Entry<Long, MetricEntity> metrics : resourceMetrics.getValue().entrySet()) {\n                if (metrics.getKey() < minTimeMs) {\n                    continue;\n                }\n                MetricEntity newEntity = metrics.getValue();\n                if (resourceCount.containsKey(resourceMetrics.getKey())) {\n                    MetricEntity oldEntity = resourceCount.get(resourceMetrics.getKey());\n                    oldEntity.addPassQps(newEntity.getPassQps());\n                    oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps());\n                    oldEntity.addBlockQps(newEntity.getBlockQps());\n                    oldEntity.addExceptionQps(newEntity.getExceptionQps());\n                    oldEntity.addCount(1);\n                } else {\n                    resourceCount.put(resourceMetrics.getKey(), MetricEntity.copyOf(newEntity));\n                }\n            }\n        }\n        // Order by last minute b_qps DESC.\n        return resourceCount.entrySet()\n            .stream()\n            .sorted((o1, o2) -> {\n                MetricEntity e1 = o1.getValue();\n                MetricEntity e2 = o2.getValue();\n                int t = e2.getBlockQps().compareTo(e1.getBlockQps());\n                if (t != 0) {\n                    return t;\n                }\n                return e2.getPassQps().compareTo(e1.getPassQps());\n            })\n            .map(Entry::getKey)\n            .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/MetricsRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport java.util.List;\n\n/**\n * Repository interface for aggregated metrics data.\n *\n * @param <T> type of metrics\n * @author Eric Zhao\n */\npublic interface MetricsRepository<T> {\n\n    /**\n     * Save the metric to the storage repository.\n     *\n     * @param metric metric data to save\n     */\n    void save(T metric);\n\n    /**\n     * Save all metrics to the storage repository.\n     *\n     * @param metrics metrics to save\n     */\n    void saveAll(Iterable<T> metrics);\n\n    /**\n     * Get all metrics by {@code appName} and {@code resourceName} between a period of time.\n     *\n     * @param app       application name for Sentinel\n     * @param resource  resource name\n     * @param startTime start timestamp\n     * @param endTime   end timestamp\n     * @return all metrics in query conditions\n     */\n    List<T> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime);\n\n    /**\n     * List resource name of provided application name.\n     *\n     * @param app application name\n     * @return list of resources\n     */\n    List<String> listResourcesOfApp(String app);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemAuthorityRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * In-memory storage for authority rules.\n *\n * @author Eric Zhao\n * @since 0.2.1\n */\n@Component\npublic class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter<AuthorityRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemDegradeRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class InMemDegradeRuleStore extends InMemoryRuleRepositoryAdapter<DegradeRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * Store {@link FlowRuleEntity} in memory.\n *\n * @author leyou\n */\n@Component\npublic class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter<FlowRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n\n    @Override\n    protected FlowRuleEntity preProcess(FlowRuleEntity entity) {\n        if (entity != null && entity.isClusterMode()) {\n            ClusterFlowConfig config = entity.getClusterConfig();\n            if (config == null) {\n                config = new ClusterFlowConfig();\n                entity.setClusterConfig(config);\n            }\n            // Set cluster rule id.\n            config.setFlowId(entity.getId());\n        }\n        return entity;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemParamFlowRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@Component\npublic class InMemParamFlowRuleStore extends InMemoryRuleRepositoryAdapter<ParamFlowRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n\n    @Override\n    protected ParamFlowRuleEntity preProcess(ParamFlowRuleEntity entity) {\n        if (entity != null && entity.isClusterMode()) {\n            ParamFlowClusterConfig config = entity.getClusterConfig();\n            if (config == null) {\n                config = new ParamFlowClusterConfig();\n            }\n            // Set cluster rule id.\n            config.setFlowId(entity.getId());\n        }\n        return entity;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemSystemRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class InMemSystemRuleStore extends InMemoryRuleRepositoryAdapter<SystemRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemoryRuleRepositoryAdapter.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\n/**\n * @author leyou\n */\npublic abstract class InMemoryRuleRepositoryAdapter<T extends RuleEntity> implements RuleRepository<T, Long> {\n\n    /**\n     * {@code <machine, <id, rule>>}\n     */\n    private Map<MachineInfo, Map<Long, T>> machineRules = new ConcurrentHashMap<>(16);\n    private Map<Long, T> allRules = new ConcurrentHashMap<>(16);\n\n    private Map<String, Map<Long, T>> appRules = new ConcurrentHashMap<>(16);\n\n    private static final int MAX_RULES_SIZE = 10000;\n\n    @Override\n    public T save(T entity) {\n        if (entity.getId() == null) {\n            entity.setId(nextId());\n        }\n        T processedEntity = preProcess(entity);\n        if (processedEntity != null) {\n            allRules.put(processedEntity.getId(), processedEntity);\n            machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(),\n                processedEntity.getPort()), e -> new ConcurrentHashMap<>(32))\n                .put(processedEntity.getId(), processedEntity);\n            appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32))\n                .put(processedEntity.getId(), processedEntity);\n        }\n\n        return processedEntity;\n    }\n\n    @Override\n    public List<T> saveAll(List<T> rules) {\n        // TODO: check here.\n        allRules.clear();\n        machineRules.clear();\n        appRules.clear();\n\n        if (rules == null) {\n            return null;\n        }\n        List<T> savedRules = new ArrayList<>(rules.size());\n        for (T rule : rules) {\n            savedRules.add(save(rule));\n        }\n        return savedRules;\n    }\n\n    @Override\n    public T delete(Long id) {\n        T entity = allRules.remove(id);\n        if (entity != null) {\n            if (appRules.get(entity.getApp()) != null) {\n                appRules.get(entity.getApp()).remove(id);\n            }\n            machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id);\n        }\n        return entity;\n    }\n\n    @Override\n    public T findById(Long id) {\n        return allRules.get(id);\n    }\n\n    @Override\n    public List<T> findAllByMachine(MachineInfo machineInfo) {\n        Map<Long, T> entities = machineRules.get(machineInfo);\n        if (entities == null) {\n            return new ArrayList<>();\n        }\n        return new ArrayList<>(entities.values());\n    }\n\n    @Override\n    public List<T> findAllByApp(String appName) {\n        AssertUtil.notEmpty(appName, \"appName cannot be empty\");\n        Map<Long, T> entities = appRules.get(appName);\n        if (entities == null) {\n            return new ArrayList<>();\n        }\n        return new ArrayList<>(entities.values());\n    }\n\n    protected T preProcess(T entity) {\n        return entity;\n    }\n\n    /**\n     * Get next unused id.\n     *\n     * @return next unused id\n     */\n    abstract protected long nextId();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/RuleRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * Interface to store and find rules.\n *\n * @author leyou\n */\npublic interface RuleRepository<T, ID> {\n\n    /**\n     * Save one.\n     *\n     * @param entity\n     * @return\n     */\n    T save(T entity);\n\n    /**\n     * Save all.\n     *\n     * @param rules\n     * @return rules saved.\n     */\n    List<T> saveAll(List<T> rules);\n\n    /**\n     * Delete by id\n     *\n     * @param id\n     * @return entity deleted\n     */\n    T delete(ID id);\n\n    /**\n     * Find by id.\n     *\n     * @param id\n     * @return\n     */\n    T findById(ID id);\n\n    /**\n     * Find all by machine.\n     *\n     * @param machineInfo\n     * @return\n     */\n    List<T> findAllByMachine(MachineInfo machineInfo);\n\n    /**\n     * Find all by application.\n     *\n     * @param appName valid app name\n     * @return all rules of the application\n     * @since 1.4.0\n     */\n    List<T> findAllByApp(String appName);\n\n    ///**\n    // * Find all by app and enable switch.\n    // * @param app\n    // * @param enable\n    // * @return\n    // */\n    //List<T> findAllByAppAndEnable(String app, boolean enable);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRuleProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface DynamicRuleProvider<T> {\n\n    T getRules(String appName) throws Exception;\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRulePublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface DynamicRulePublisher<T> {\n\n    /**\n     * Publish rules to remote rule configuration center for given application name.\n     *\n     * @param app app name\n     * @param rules list of rules to push\n     * @throws Exception if some error occurs\n     */\n    void publish(String app, T rules) throws Exception;\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n */\n@Component(\"flowRuleDefaultProvider\")\npublic class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        if (StringUtil.isBlank(appName)) {\n            return new ArrayList<>();\n        }\n        List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()\n            .stream()\n            .filter(MachineInfo::isHealthy)\n            .sorted((e1, e2) -> Long.compare(e2.getLastHeartbeat(), e1.getLastHeartbeat())).collect(Collectors.toList());\n        if (list.isEmpty()) {\n            return new ArrayList<>();\n        } else {\n            MachineInfo machine = list.get(0);\n            return sentinelApiClient.fetchFlowRuleOfMachine(machine.getApp(), machine.getIp(), machine.getPort());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleDefaultPublisher\")\npublic class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        if (StringUtil.isBlank(app)) {\n            return;\n        }\n        if (rules == null) {\n            return;\n        }\n        Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();\n\n        for (MachineInfo machine : set) {\n            if (!machine.isHealthy()) {\n                continue;\n            }\n            // TODO: parse the results\n            sentinelApiClient.setFlowRuleOfMachine(app, machine.getIp(), machine.getPort(), rules);\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Configuration\npublic class ApolloConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ApolloOpenApiClient apolloOpenApiClient() {\n        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()\n            .withPortalUrl(\"https://apollo.xxx.com\")    // TODO 根据实际情况修改\n            .withToken(\"open api token\")                // TODO 根据实际情况修改\n            .build();\n        return client;\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloProvider\")\npublic class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    @Value(\"${env:DEV}\")\n    private String env;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String flowDataId = \"sentinel.flowRules\";\n        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appName, env, \"default\", \"application\");\n        String rules = openNamespaceDTO\n            .getItems()\n            .stream()\n            .filter(p -> p.getKey().equals(flowDataId))\n            .map(OpenItemDTO::getValue)\n            .findFirst()\n            .orElse(\"\");\n\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloPublisher\")\npublic class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    @Value(\"${env:DEV}\")\n    private String env;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        String flowDataId = \"sentinel.flowRules\";\n\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n\n        // TODO 处理不兼容的字段，spring cloud alibaba 0.2.2版本中实现JSON转换的时候，不会忽略不存在的字段，会导致客户端出现出现异常\n        // 可以通过下面这段代码将这些字段不存入Apollo，以避免客户端加载的错误\n        for (FlowRuleEntity ruleEntity : rules) {\n            ruleEntity.setId(null);\n            ruleEntity.setApp(null);\n            ruleEntity.setGmtModified(null);\n            ruleEntity.setGmtCreate(null);\n            ruleEntity.setIp(null);\n            ruleEntity.setPort(null);\n        }\n        // 请视情况使用\n\n        OpenItemDTO openItemDTO = new OpenItemDTO();\n        openItemDTO.setKey(flowDataId);\n        openItemDTO.setValue(converter.convert(rules));\n        openItemDTO.setComment(\"modify by sentinel-dashboard\");\n        openItemDTO.setDataChangeCreatedBy(\"apollo\");\n        apolloOpenApiClient.createOrUpdateItem(app, env, \"default\", \"application\", openItemDTO);\n\n        // Release configuration\n        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();\n        namespaceReleaseDTO.setEmergencyPublish(true);\n        namespaceReleaseDTO.setReleaseComment(\"release by sentinel-dashboard\");\n        namespaceReleaseDTO.setReleasedBy(\"apollo\");\n        namespaceReleaseDTO.setReleaseTitle(\"release by sentinel-dashboard\");\n        apolloOpenApiClient.publishNamespace(app, env, \"default\", \"application\", namespaceReleaseDTO);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic interface ClusterAssignService {\n\n    /**\n     * Unbind a specific cluster server and its clients.\n     *\n     * @param app app name\n     * @param machineId valid machine ID ({@code host@commandPort})\n     * @return assign result\n     */\n    ClusterAppAssignResultVO unbindClusterServer(String app, String machineId);\n\n    /**\n     * Unbind a set of cluster servers and its clients.\n     *\n     * @param app app name\n     * @param machineIdSet set of valid machine ID ({@code host@commandPort})\n     * @return assign result\n     */\n    ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet);\n\n    /**\n     * Apply cluster server and client assignment for provided app.\n     *\n     * @param app app name\n     * @param clusterMap cluster assign map (server -> clients)\n     * @param remainingSet unassigned set of machine ID\n     * @return assign result\n     */\n    ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,\n                                              Set<String> remainingSet);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.csp.sentinel.util.function.Tuple2;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\nimport com.alibaba.csp.sentinel.dashboard.util.MachineUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\n@Service\npublic class ClusterAssignServiceImpl implements ClusterAssignService {\n\n    private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private ClusterConfigService clusterConfigService;\n\n    private boolean isMachineInApp(/*@NonEmpty*/ String machineId) {\n        return machineId.contains(\":\");\n    }\n\n    private ClusterAppAssignResultVO handleUnbindClusterServerNotInApp(String app, String machineId) {\n        Set<String> failedSet = new HashSet<>();\n        try {\n            List<ClusterUniversalStatePairVO> list = clusterConfigService.getClusterUniversalState(app)\n                .get(10, TimeUnit.SECONDS);\n            Set<String> toModifySet = list.stream()\n                .filter(e -> e.getState().getStateInfo().getMode() == ClusterStateManager.CLUSTER_CLIENT)\n                .filter(e -> machineId.equals(e.getState().getClient().getClientConfig().getServerHost() + ':' +\n                    e.getState().getClient().getClientConfig().getServerPort()))\n                .map(e -> e.getIp() + '@' + e.getCommandPort())\n                .collect(Collectors.toSet());\n            // Modify mode to NOT-STARTED for all associated token clients.\n            modifyToNonStarted(toModifySet, failedSet);\n        } catch (Exception ex) {\n            Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;\n            LOGGER.error(\"Failed to unbind machine <{}>\", machineId, e);\n            failedSet.add(machineId);\n        }\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedSet)\n            .setFailedServerSet(new HashSet<>());\n    }\n\n    private void modifyToNonStarted(Set<String> toModifySet, Set<String> failedSet) {\n        toModifySet.parallelStream()\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(e -> {\n                CompletableFuture<Void> f = modifyMode(e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED);\n                return Tuple2.of(e.r1 + '@' + e.r2, f);\n            })\n            .forEach(f -> handleFutureSync(f, failedSet));\n    }\n\n    @Override\n    public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.assertNotBlank(machineId, \"machineId cannot be blank\");\n\n        if (isMachineInApp(machineId)) {\n            return handleUnbindClusterServerNotInApp(app, machineId);\n        }\n        Set<String> failedSet = new HashSet<>();\n        try {\n            ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId)\n                .get(10, TimeUnit.SECONDS);\n            Set<String> toModifySet = new HashSet<>();\n            toModifySet.add(machineId);\n            if (entity.getClientSet() != null) {\n                toModifySet.addAll(entity.getClientSet());\n            }\n            // Modify mode to NOT-STARTED for all chosen token servers and associated token clients.\n            modifyToNonStarted(toModifySet, failedSet);\n        } catch (Exception ex) {\n            Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;\n            LOGGER.error(\"Failed to unbind machine <{}>\", machineId, e);\n            failedSet.add(machineId);\n        }\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedSet)\n            .setFailedServerSet(new HashSet<>());\n    }\n\n    @Override\n    public ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), \"machineIdSet cannot be empty\");\n        ClusterAppAssignResultVO result = new ClusterAppAssignResultVO()\n            .setFailedClientSet(new HashSet<>())\n            .setFailedServerSet(new HashSet<>());\n        for (String machineId : machineIdSet) {\n            ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId);\n            result.getFailedClientSet().addAll(resultVO.getFailedClientSet());\n            result.getFailedServerSet().addAll(resultVO.getFailedServerSet());\n        }\n        return result;\n    }\n\n    @Override\n    public ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,\n                                                     Set<String> remainingSet) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.notNull(clusterMap, \"clusterMap cannot be null\");\n        Set<String> failedServerSet = new HashSet<>();\n        Set<String> failedClientSet = new HashSet<>();\n\n        // Assign server and apply config.\n        clusterMap.stream()\n            .filter(Objects::nonNull)\n            .filter(ClusterAppAssignMap::getBelongToApp)\n            .map(e -> {\n                String ip = e.getIp();\n                int commandPort = parsePort(e);\n                CompletableFuture<Void> f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_SERVER)\n                    .thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e));\n                return Tuple2.of(e.getMachineId(), f);\n            })\n            .forEach(t -> handleFutureSync(t, failedServerSet));\n\n        // Assign client of servers and apply config.\n        clusterMap.parallelStream()\n            .filter(Objects::nonNull)\n            .forEach(e -> applyAllClientConfigChange(app, e, failedClientSet));\n\n        // Unbind remaining (unassigned) machines.\n        applyAllRemainingMachineSet(app, remainingSet, failedClientSet);\n\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedClientSet)\n            .setFailedServerSet(failedServerSet);\n    }\n\n    private void applyAllRemainingMachineSet(String app, Set<String> remainingSet, Set<String> failedSet) {\n        if (remainingSet == null || remainingSet.isEmpty()) {\n            return;\n        }\n        remainingSet.parallelStream()\n            .filter(Objects::nonNull)\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(ipPort -> {\n                String ip = ipPort.r1;\n                int commandPort = ipPort.r2;\n                CompletableFuture<Void> f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED);\n                return Tuple2.of(ip + '@' + commandPort, f);\n            })\n            .forEach(t -> handleFutureSync(t, failedSet));\n    }\n\n    private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap,\n                                            Set<String> failedSet) {\n        Set<String> clientSet = assignMap.getClientSet();\n        if (clientSet == null || clientSet.isEmpty()) {\n            return;\n        }\n        final String serverIp = assignMap.getIp();\n        final int serverPort = assignMap.getPort();\n        clientSet.stream()\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(ipPort -> {\n                CompletableFuture<Void> f = sentinelApiClient\n                    .modifyClusterMode(ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT)\n                    .thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2,\n                        new ClusterClientConfig().setRequestTimeout(20)\n                            .setServerHost(serverIp)\n                            .setServerPort(serverPort)\n                    ));\n                return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f);\n            })\n            .forEach(t -> handleFutureSync(t, failedSet));\n    }\n\n    private void handleFutureSync(Tuple2<String, CompletableFuture<Void>> t, Set<String> failedSet) {\n        try {\n            t.r2.get(10, TimeUnit.SECONDS);\n        } catch (Exception ex) {\n            if (ex instanceof ExecutionException) {\n                LOGGER.error(\"Request for <{}> failed\", t.r1, ex.getCause());\n            } else {\n                LOGGER.error(\"Request for <{}> failed\", t.r1, ex);\n            }\n            failedSet.add(t.r1);\n        }\n    }\n\n    private CompletableFuture<Void> applyServerConfigChange(String app, String ip, int commandPort,\n                                                            ClusterAppAssignMap assignMap) {\n        ServerTransportConfig transportConfig = new ServerTransportConfig()\n            .setPort(assignMap.getPort())\n            .setIdleSeconds(600);\n        return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig)\n            .thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap))\n            .thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap));\n    }\n\n    private CompletableFuture<Void> applyServerFlowConfigChange(String app, String ip, int commandPort,\n                                                                ClusterAppAssignMap assignMap) {\n        Double maxAllowedQps = assignMap.getMaxAllowedQps();\n        if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) {\n            return CompletableFuture.completedFuture(null);\n        }\n        return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort,\n            new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps));\n    }\n\n    private CompletableFuture<Void> applyServerNamespaceSetConfig(String app, String ip, int commandPort,\n                                                                  ClusterAppAssignMap assignMap) {\n        Set<String> namespaceSet = assignMap.getNamespaceSet();\n        if (namespaceSet == null || namespaceSet.isEmpty()) {\n            return CompletableFuture.completedFuture(null);\n        }\n        return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet);\n    }\n\n    private CompletableFuture<Void> modifyMode(String ip, int port, int mode) {\n        return sentinelApiClient.modifyClusterMode(ip, port, mode);\n    }\n\n    private int parsePort(ClusterAppAssignMap assignMap) {\n        return MachineUtils.parseCommandPort(assignMap.getMachineId())\n            .orElse(ServerTransportConfig.DEFAULT_PORT);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.util.AsyncUtils;\nimport com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Service\npublic class ClusterConfigService {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {\n        if (notClientRequestValid(request)) {\n            throw new IllegalArgumentException(\"Invalid request\");\n        }\n        String app = request.getApp();\n        String ip = request.getIp();\n        int port = request.getPort();\n        return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())\n            .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT));\n    }\n\n    private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {\n        ClusterClientConfig config = request.getClientConfig();\n        return config == null || StringUtil.isEmpty(config.getServerHost())\n            || config.getServerPort() == null || config.getServerPort() <= 0\n            || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0;\n    }\n\n    public CompletableFuture<Void> modifyClusterServerConfig(ClusterServerModifyRequest request) {\n        ServerTransportConfig transportConfig = request.getTransportConfig();\n        ServerFlowConfig flowConfig = request.getFlowConfig();\n        Set<String> namespaceSet = request.getNamespaceSet();\n        if (invalidTransportConfig(transportConfig)) {\n            throw new IllegalArgumentException(\"Invalid transport config in request\");\n        }\n        if (invalidFlowConfig(flowConfig)) {\n            throw new IllegalArgumentException(\"Invalid flow config in request\");\n        }\n        if (namespaceSet == null) {\n            throw new IllegalArgumentException(\"namespace set cannot be null\");\n        }\n        String app = request.getApp();\n        String ip = request.getIp();\n        int port = request.getPort();\n        return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet)\n            .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig))\n            .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig))\n            .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_SERVER));\n    }\n\n    /**\n     * Get cluster state list of all available machines of provided application.\n     *\n     * @param app application name\n     * @return cluster state list of all available machines of the application\n     * @since 1.4.1\n     */\n    public CompletableFuture<List<ClusterUniversalStatePairVO>> getClusterUniversalState(String app) {\n        if (StringUtil.isBlank(app)) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app cannot be empty\"));\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null || appInfo.getMachines() == null) {\n            return CompletableFuture.completedFuture(new ArrayList<>());\n        }\n\n        List<CompletableFuture<ClusterUniversalStatePairVO>> futures = appInfo.getMachines().stream()\n            .filter(e -> e.isHealthy())\n            .map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())\n                .thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))\n            .collect(Collectors.toList());\n\n        return AsyncUtils.sequenceSuccessFuture(futures);\n    }\n\n    public CompletableFuture<ClusterGroupEntity> getClusterUniversalStateForAppMachine(String app, String machineId) {\n        if (StringUtil.isBlank(app)) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app cannot be empty\"));\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null || appInfo.getMachines() == null) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app does not have machines\"));\n        }\n\n        boolean machineOk = appInfo.getMachines().stream()\n            .filter(e -> e.isHealthy())\n            .map(e -> e.getIp() + '@' + e.getPort())\n            .anyMatch(e -> e.equals(machineId));\n        if (!machineOk) {\n            return AsyncUtils.newFailedFuture(new IllegalStateException(\"machine does not exist or disconnected\"));\n        }\n\n        return getClusterUniversalState(app)\n            .thenApply(ClusterEntityUtils::wrapToClusterGroup)\n            .thenCompose(e -> e.stream()\n                .filter(e1 -> e1.getMachineId().equals(machineId))\n                .findAny()\n                .map(CompletableFuture::completedFuture)\n                .orElse(AsyncUtils.newFailedFuture(new IllegalStateException(\"not a server: \" + machineId)))\n            );\n    }\n\n    public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {\n        return sentinelApiClient.fetchClusterMode(ip, port)\n            .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))\n            .thenCompose(vo -> {\n                if (vo.getStateInfo().getClientAvailable()) {\n                    return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port)\n                        .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));\n                } else {\n                    return CompletableFuture.completedFuture(vo);\n                }\n            }).thenCompose(vo -> {\n                if (vo.getStateInfo().getServerAvailable()) {\n                    return sentinelApiClient.fetchClusterServerBasicInfo(ip, port)\n                        .thenApply(vo::setServer);\n                } else {\n                    return CompletableFuture.completedFuture(vo);\n                }\n            });\n    }\n\n    private boolean invalidTransportConfig(ServerTransportConfig transportConfig) {\n        return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0\n            || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0;\n    }\n\n    private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {\n        return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0\n            || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0\n            || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0\n            || flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/AsyncUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic final class AsyncUtils {\n\n    private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class);\n\n    public static <R> CompletableFuture<R> newFailedFuture(Throwable ex) {\n        CompletableFuture<R> future = new CompletableFuture<>();\n        future.completeExceptionally(ex);\n        return future;\n    }\n\n    public static <R> CompletableFuture<List<R>> sequenceFuture(List<CompletableFuture<R>> futures) {\n        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))\n            .thenApply(v -> futures.stream()\n                .map(AsyncUtils::getValue)\n                .filter(Objects::nonNull)\n                .collect(Collectors.toList())\n            );\n    }\n\n    public static <R> CompletableFuture<List<R>> sequenceSuccessFuture(List<CompletableFuture<R>> futures) {\n        return CompletableFuture.supplyAsync(() -> futures.parallelStream()\n            .map(AsyncUtils::getValue)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList())\n        );\n    }\n\n    public static <T> T getValue(CompletableFuture<T> future) {\n        try {\n            return future.get(10, TimeUnit.SECONDS);\n        } catch (Exception ex) {\n            LOG.error(\"getValue for async result failed\", ex);\n        }\n        return null;\n    }\n\n    public static boolean isSuccessFuture(CompletableFuture future) {\n        return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled();\n    }\n\n    private AsyncUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/ClusterEntityUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic final class ClusterEntityUtils {\n\n    public static List<AppClusterServerStateWrapVO> wrapToAppClusterServerState(\n        List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, AppClusterServerStateWrapVO> map = new HashMap<>();\n        Set<String> tokenServerSet = new HashSet<>();\n        // Handle token servers that belong to current app.\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_SERVER) {\n                String ip = stateVO.getIp();\n                String serverId = ip + '@' + stateVO.getCommandPort();\n                ClusterServerStateVO serverStateVO = stateVO.getState().getServer();\n                map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()\n                    .setId(serverId)\n                    .setIp(ip)\n                    .setPort(serverStateVO.getPort())\n                    .setState(serverStateVO)\n                    .setBelongToApp(true)\n                    .setConnectedCount(serverStateVO.getConnection().stream()\n                        .mapToInt(ConnectionGroupVO::getConnectedCount)\n                        .sum()\n                    )\n                );\n                tokenServerSet.add(ip + \":\" + serverStateVO.getPort());\n            }\n        }\n        // Handle token servers from other app.\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                ClusterClientStateVO clientState = stateVO.getState().getClient();\n                if (clientState == null) {\n                    continue;\n                }\n                String serverIp = clientState.getClientConfig().getServerHost();\n                int serverPort = clientState.getClientConfig().getServerPort();\n                if (tokenServerSet.contains(serverIp + \":\" + serverPort)) {\n                    continue;\n                }\n                // We are not able to get the commandPort of foreign token server directly.\n                String serverId = String.format(\"%s:%d\", serverIp, serverPort);\n                map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()\n                    .setId(serverId)\n                    .setIp(serverIp)\n                    .setPort(serverPort)\n                    .setBelongToApp(false)\n                );\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    public static List<AppClusterClientStateWrapVO> wrapToAppClusterClientState(\n        List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, AppClusterClientStateWrapVO> map = new HashMap<>();\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                String ip = stateVO.getIp();\n                String clientId = ip + '@' + stateVO.getCommandPort();\n                ClusterClientStateVO clientStateVO = stateVO.getState().getClient();\n                map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO()\n                    .setId(clientId)\n                    .setIp(ip)\n                    .setState(clientStateVO)\n                    .setCommandPort(stateVO.getCommandPort())\n                );\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    public static List<ClusterGroupEntity> wrapToClusterGroup(List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, ClusterGroupEntity> map = new HashMap<>();\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n            String ip = stateVO.getIp();\n            if (mode == ClusterStateManager.CLUSTER_SERVER) {\n                String serverAddress = getIp(ip);\n                int port = stateVO.getState().getServer().getPort();\n                map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity()\n                    .setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort())\n                    .setIp(ip).setPort(port)\n                );\n            }\n        }\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n            String ip = stateVO.getIp();\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost();\n                Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort();\n                if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) {\n                    continue;\n                }\n\n                ClusterGroupEntity group = map.computeIfAbsent(targetServer,\n                    v -> new ClusterGroupEntity()\n                        .setBelongToApp(true).setMachineId(targetServer)\n                        .setIp(targetServer).setPort(targetPort)\n                );\n                group.getClientSet().add(ip + '@' + stateVO.getCommandPort());\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    private static String getIp(String str) {\n        if (str.contains(\":\")) {\n            return str.split(\":\")[0];\n        }\n        return str;\n    }\n\n    private ClusterEntityUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.Optional;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.csp.sentinel.util.function.Tuple2;\n\n/**\n * @author Eric Zhao\n */\npublic final class MachineUtils {\n\n    public static Optional<Integer> parseCommandPort(String machineIp) {\n        try {\n            if (!machineIp.contains(\"@\")) {\n                return Optional.empty();\n            }\n            String[] str = machineIp.split(\"@\");\n            if (str.length <= 1) {\n                return Optional.empty();\n            }\n            return Optional.of(Integer.parseInt(str[1]));\n        } catch (Exception ex) {\n            return Optional.empty();\n        }\n    }\n\n    public static Optional<Tuple2<String, Integer>> parseCommandIpAndPort(String machineIp) {\n        try {\n            if (StringUtil.isEmpty(machineIp) || !machineIp.contains(\"@\")) {\n                return Optional.empty();\n            }\n            String[] str = machineIp.split(\"@\");\n            if (str.length <= 1) {\n                return Optional.empty();\n            }\n            return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1])));\n        } catch (Exception ex) {\n            return Optional.empty();\n        }\n    }\n\n    private MachineUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.Optional;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\n\n/**\n * Util class for parsing version.\n *\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic final class VersionUtils {\n\n    /**\n     * Parse version of Sentinel from raw string.\n     *\n     * @param versionFull version string\n     * @return parsed {@link SentinelVersion} if the version is valid; empty if\n     * there is something wrong with the format\n     */\n    public static Optional<SentinelVersion> parseVersion(String s) {\n        if (StringUtil.isBlank(s)) {\n            return Optional.empty();\n        }\n        try {\n            String versionFull = s;\n            SentinelVersion version = new SentinelVersion();\n            \n            // postfix\n            int index = versionFull.indexOf(\"-\");\n            if (index == 0) {\n                // Start with \"-\"\n                return Optional.empty();\n            }\n            if (index == versionFull.length() - 1) {\n                // End with \"-\"\n            } else if (index > 0) {\n                version.setPostfix(versionFull.substring(index + 1));\n            }\n            \n            if (index >= 0) {\n                versionFull = versionFull.substring(0, index);\n            }\n            \n            // x.x.x\n            int segment = 0;\n            int[] ver = new int[3];\n            while (segment < ver.length) {\n                index = versionFull.indexOf('.');\n                if (index < 0) {\n                    if (versionFull.length() > 0) {\n                        ver[segment] = Integer.valueOf(versionFull);\n                    }\n                    break;\n                }\n                ver[segment] = Integer.valueOf(versionFull.substring(0, index));\n                versionFull = versionFull.substring(index + 1);\n                segment ++;\n            }\n            \n            if (ver[0] < 1) {\n                // Wrong format, return empty.\n                return Optional.empty();\n            } else {\n                return Optional.of(version\n                        .setMajorVersion(ver[0])\n                        .setMinorVersion(ver[1])\n                        .setFixVersion(ver[2]));\n            }\n        } catch (Exception ex) {\n            // Parse fail, return empty.\n            return Optional.empty();\n        }\n    }\n\n    private VersionUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/resources/application.properties",
    "content": "#spring settings\nspring.http.encoding.force=true\nspring.http.encoding.charset=UTF-8\nspring.http.encoding.enabled=true\n\n#logging settings\nlogging.level.org.springframework.web=INFO\nlogging.file=${user.home}/logs/csp/sentinel-dashboard.log\nlogging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\n#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\n\n#auth settings\nauth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine\nauth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png\nauth.username=sentinel\nauth.password=sentinel\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/.gitignore",
    "content": "node_modules/\ntmp/"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/.jshintrc",
    "content": "{\n  /*\n   * ENVIRONMENTS\n   * =================\n   */\n\n  // Define globals exposed by modern browsers.\n  \"browser\": true,\n\n  // Define globals exposed by jQuery.\n  \"jquery\": true,\n\n  // Define globals exposed by Node.js.\n  \"node\": true,\n\n  // Allow ES6.\n  \"esversion\": 6,\n\n  /*\n   * ENFORCING OPTIONS\n   * =================\n   */\n\n  // Force all variable names to use either camelCase style or UPPER_CASE\n  // with underscores.\n  \"camelcase\": true,\n\n  // Prohibit use of == and != in favor of === and !==.\n  \"eqeqeq\": true,\n\n  // Enforce tab width of 2 spaces.\n  \"indent\": 2,\n\n  // Prohibit use of a variable before it is defined.\n  \"latedef\": true,\n\n  // Enforce line length to 100 characters\n  \"maxlen\": 100,\n\n  // Require capitalized names for constructor functions.\n  \"newcap\": true,\n\n  // Enforce use of single quotation marks for strings.\n  \"quotmark\": \"single\",\n\n  // Enforce placing 'use strict' at the top function scope\n  // 前端项目中外层使用 strict 即可，覆盖此条规则\n  \"strict\": false,\n\n  // Prohibit use of explicitly undeclared variables.\n  \"undef\": true,\n\n  // Warn when variables are defined but never used.\n  \"unused\": true,\n\n  /*\n   * RELAXING OPTIONS\n   * =================\n   */\n\n  // Suppress warnings about == null comparisons.\n  \"eqnull\": true,\n  \"globals\": {\n    \"$\": false,\n    \"angular\": false\n  }\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/README.md",
    "content": "# Sentinel Dashboard Frontend\n\n## Env Requirement\n\n- Node.js > 6.x\n\n## Code Guide\n\n- [Code Style Guide for HTML/CSS](https://codeguide.bootcss.com/)\n- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5)\n\n## Install Packages\n\n```\nnpm install\n```\n\n## Start Development\n\n```\nnpm start\n```\n\n## Build for production\n\n```\nnpm run build\n```\n\n## Credit\n\n- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/README_zh.md",
    "content": "# Sentinel Dashboard Frontend\n\n## 环境要求\n\n- Node.js > 6.x\n\n## 编码规范\n\n- HTML/CSS 遵循 [Bootstrap 编码规范](https://codeguide.bootcss.com/)\n- JavaScript 遵循 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) 以及最新的 ES 6 标准\n\n## 安装依赖\n\n```\nnpm i\n```\n\n## 开始本地开发\n\n```\nnpm start\n```\n\n## 构建前端资源\n\n```\nnpm run build\n```\n\n## Credit\n\n- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/app.js",
    "content": "'use strict';\n\n/**\n * @ngdoc overview\n * @name sentinelDashboardApp\n * @description\n * # sentinelDashboardApp\n *\n * Main module of the application.\n */\n\nangular\n  .module('sentinelDashboardApp', [\n    'oc.lazyLoad',\n    'ui.router',\n    'ui.bootstrap',\n    'angular-loading-bar',\n    'ngDialog',\n    'ui.bootstrap.datetimepicker',\n    'ui-notification',\n    'rzTable',\n    'angular-clipboard',\n    'selectize',\n    'angularUtils.directives.dirPagination'\n  ])\n  .factory('AuthInterceptor', ['$window', '$state', function ($window, $state) {\n    var authInterceptor = {\n      'responseError' : function(response) {\n        if (response.status == 401) {\n          // If not auth, clear session in localStorage and jump to the login page\n          $window.localStorage.removeItem(\"session_sentinel_admin\");\n          $state.go('login');\n        }\n\n        return response;\n      },\n      'response' : function(response) {\n        return response;\n      },\n      'request' : function(config) {\n        return config;\n      },\n      'requestError' : function(config){\n        return config;\n      }\n    };\n    return authInterceptor;\n  }])\n  .config(['$stateProvider', '$urlRouterProvider', '$ocLazyLoadProvider', '$httpProvider',\n    function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, $httpProvider) {\n      $httpProvider.interceptors.push('AuthInterceptor');\n\n      $ocLazyLoadProvider.config({\n        debug: false,\n        events: true,\n      });\n\n      $urlRouterProvider.otherwise('/dashboard/home');\n\n      $stateProvider\n        .state('login', {\n            url: '/login',\n            templateUrl: 'app/views/login.html',\n            controller: 'LoginCtl',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/login.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard', {\n        url: '/dashboard',\n        templateUrl: 'app/views/dashboard/main.html',\n        resolve: {\n          loadMyDirectives: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load(\n              {\n                name: 'sentinelDashboardApp',\n                files: [\n                  'app/scripts/directives/header/header.js',\n                  'app/scripts/directives/sidebar/sidebar.js',\n                  'app/scripts/directives/sidebar/sidebar-search/sidebar-search.js',\n                ]\n              });\n          }]\n        }\n      })\n\n      .state('dashboard.home', {\n        url: '/home',\n        templateUrl: 'app/views/dashboard/home.html',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/main.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.flowV1', {\n        templateUrl: 'app/views/flow_v1.html',\n        url: '/flow/:app',\n        controller: 'FlowControllerV1',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/flow_v1.js',\n              ]\n            });\n          }]\n        }\n      })\n\n        .state('dashboard.flow', {\n            templateUrl: 'app/views/flow_v2.html',\n            url: '/v2/flow/:app',\n            controller: 'FlowControllerV2',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/flow_v2.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard.paramFlow', {\n        templateUrl: 'app/views/param_flow.html',\n        url: '/paramFlow/:app',\n        controller: 'ParamFlowController',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/param_flow.js',\n              ]\n            });\n          }]\n        }\n      })\n\n        .state('dashboard.clusterAppAssignManage', {\n            templateUrl: 'app/views/cluster_app_assign_manage.html',\n            url: '/cluster/assign_manage/:app',\n            controller: 'SentinelClusterAppAssignManageController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_assign_manage.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterAppServerList', {\n            templateUrl: 'app/views/cluster_app_server_list.html',\n            url: '/cluster/server/:app',\n            controller: 'SentinelClusterAppServerListController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_server_list.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterAppClientList', {\n            templateUrl: 'app/views/cluster_app_client_list.html',\n            url: '/cluster/client/:app',\n            controller: 'SentinelClusterAppTokenClientListController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_token_client_list.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterSingle', {\n            templateUrl: 'app/views/cluster_single_config.html',\n            url: '/cluster/single/:app',\n            controller: 'SentinelClusterSingleController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_single.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard.authority', {\n            templateUrl: 'app/views/authority.html',\n            url: '/authority/:app',\n            controller: 'AuthorityRuleController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/authority.js',\n                        ]\n                    });\n                }]\n            }\n       })\n\n      .state('dashboard.degrade', {\n        templateUrl: 'app/views/degrade.html',\n        url: '/degrade/:app',\n        controller: 'DegradeCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/degrade.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.system', {\n        templateUrl: 'app/views/system.html',\n        url: '/system/:app',\n        controller: 'SystemCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/system.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.machine', {\n        templateUrl: 'app/views/machine.html',\n        url: '/app/:app',\n        controller: 'MachineCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/machine.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.identity', {\n        templateUrl: 'app/views/identity.html',\n        url: '/identity/:app',\n        controller: 'IdentityCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/identity.js',\n              ]\n            });\n          }]\n        }\n      })\n      .state('dashboard.metric', {\n        templateUrl: 'app/views/metric.html',\n        url: '/metric/:app',\n        controller: 'MetricCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/metric.js',\n              ]\n            });\n          }]\n        }\n      });\n  }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/authority.js",
    "content": "/**\n * Authority rule controller.\n */\nangular.module('sentinelDashboardApp').controller('AuthorityRuleController', ['$scope', '$stateParams', 'AuthorityRuleService', 'ngDialog',\n    'MachineService',\n    function ($scope, $stateParams, AuthorityRuleService, ngDialog,\n              MachineService) {\n        $scope.app = $stateParams.app;\n\n        $scope.rulesPageConfig = {\n            pageSize: 10,\n            currentPageIndex: 1,\n            totalPage: 1,\n            totalCount: 0,\n        };\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n\n        function getMachineRules() {\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            AuthorityRuleService.queryMachineRules($scope.app, mac[0], mac[1])\n                .success(function (data) {\n                    if (data.code === 0 && data.data) {\n                        $scope.loadError = undefined;\n                        $scope.rules = data.data;\n                        $scope.rulesPageConfig.totalCount = $scope.rules.length;\n                    } else {\n                        $scope.rules = [];\n                        $scope.rulesPageConfig.totalCount = 0;\n                        $scope.loadError = {message: data.msg};\n                    }\n                })\n                .error((data, header, config, status) => {\n                    $scope.loadError = {message: \"未知错误\"};\n                });\n        };\n        $scope.getMachineRules = getMachineRules;\n        getMachineRules();\n\n        var authorityRuleDialog;\n\n        $scope.editRule = function (rule) {\n            $scope.currentRule = angular.copy(rule);\n            $scope.authorityRuleDialog = {\n                title: '编辑授权规则',\n                type: 'edit',\n                confirmBtnText: '保存',\n            };\n            authorityRuleDialog = ngDialog.open({\n                template: '/app/views/dialog/authority-rule-dialog.html',\n                width: 680,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.addNewRule = function () {\n            var mac = $scope.macInputModel.split(':');\n            $scope.currentRule = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n                rule: {\n                    strategy: 0,\n                    limitApp: '',\n                }\n            };\n            $scope.authorityRuleDialog = {\n                title: '新增授权规则',\n                type: 'add',\n                confirmBtnText: '新增',\n                showAdvanceButton: true,\n            };\n            authorityRuleDialog = ngDialog.open({\n                template: '/app/views/dialog/authority-rule-dialog.html',\n                width: 680,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.saveRule = function () {\n            if (!AuthorityRuleService.checkRuleValid($scope.currentRule.rule)) {\n                return;\n            }\n            if ($scope.authorityRuleDialog.type === 'add') {\n                addNewRuleAndPush($scope.currentRule);\n            } else if ($scope.authorityRuleDialog.type === 'edit') {\n                saveRuleAndPush($scope.currentRule, true);\n            }\n        };\n\n        function addNewRuleAndPush(rule) {\n            AuthorityRuleService.addNewRule(rule).success((data) => {\n                if (data.success) {\n                    getMachineRules();\n                    authorityRuleDialog.close();\n                } else {\n                    alert('添加规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('添加规则失败：' + data.msg);\n                } else {\n                    alert(\"添加规则失败：未知错误\");\n                }\n            });\n        }\n\n        function saveRuleAndPush(rule, edit) {\n            AuthorityRuleService.saveRule(rule).success(function (data) {\n                if (data.success) {\n                    alert(\"修改规则成功\");\n                    getMachineRules();\n                    if (edit) {\n                        authorityRuleDialog.close();\n                    } else {\n                        confirmDialog.close();\n                    }\n                } else {\n                    alert('修改规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('修改规则失败：' + data.msg);\n                } else {\n                    alert(\"修改规则失败：未知错误\");\n                }\n            });\n        }\n\n        function deleteRuleAndPush(entity) {\n            if (entity.id === undefined || isNaN(entity.id)) {\n                alert('规则 ID 不合法！');\n                return;\n            }\n            AuthorityRuleService.deleteRule(entity).success((data) => {\n                if (data.code == 0) {\n                    getMachineRules();\n                    confirmDialog.close();\n                } else {\n                    alert('删除规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('删除规则失败：' + data.msg);\n                } else {\n                    alert(\"删除规则失败：未知错误\");\n                }\n            });\n        };\n\n        var confirmDialog;\n        $scope.deleteRule = function (ruleEntity) {\n            $scope.currentRule = ruleEntity;\n            $scope.confirmDialog = {\n                title: '删除授权规则',\n                type: 'delete_rule',\n                attentionTitle: '请确认是否删除如下授权限流规则',\n                attention: '资源名: ' + ruleEntity.rule.resource + ', 流控应用: ' + ruleEntity.rule.limitApp +\n                    ', 类型: ' + (ruleEntity.rule.strategy === 0 ? '白名单' : '黑名单'),\n                confirmBtnText: '删除',\n            };\n            confirmDialog = ngDialog.open({\n                template: '/app/views/dialog/confirm-dialog.html',\n                scope: $scope,\n                overlay: true\n            });\n        };\n\n        $scope.confirm = function () {\n            if ($scope.confirmDialog.type === 'delete_rule') {\n                deleteRuleAndPush($scope.currentRule);\n            } else {\n                console.error('error');\n            }\n        };\n\n        queryAppMachines();\n\n        function queryAppMachines() {\n            MachineService.getAppMachines($scope.app).success(\n                function (data) {\n                    if (data.code == 0) {\n                        // $scope.machines = data.data;\n                        if (data.data) {\n                            $scope.machines = [];\n                            $scope.macsInputOptions = [];\n                            data.data.forEach(function (item) {\n                                if (item.healthy) {\n                                    $scope.macsInputOptions.push({\n                                        text: item.ip + ':' + item.port,\n                                        value: item.ip + ':' + item.port\n                                    });\n                                }\n                            });\n                        }\n                        if ($scope.macsInputOptions.length > 0) {\n                            $scope.macInputModel = $scope.macsInputOptions[0].value;\n                        }\n                    } else {\n                        $scope.macsInputOptions = [];\n                    }\n                }\n            );\n        };\n        $scope.$watch('macInputModel', function () {\n            if ($scope.macInputModel) {\n                getMachineRules();\n            }\n        });\n    }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_assign_manage.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingClientAddressList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(ip)) {\n                    tmpMap.set(ip, group);\n                }\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                    return;\n                }\n\n                if (!tmpMap.has(targetServer)) {\n                    let group = {\n                        ip: targetServer,\n                        machineId: targetServer,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(targetServer, group);\n                } else {\n                    let g = tmpMap.get(targetServer);\n                    g.clientSet.push(machineId);\n                }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.onCurrentServerChange = () => {\n            resetChosen();\n        };\n\n        $scope.remainingClientAddressList = [];\n\n        $scope.moveToServerGroup = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                chosenServer.clientSet.push(e);\n                removeFromArr($scope.remainingClientAddressList, e);\n            });\n            resetChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingClientAddressList.push(e);\n                removeFromArr($scope.tmp.curChosenServer.clientSet, e);\n            });\n            resetChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        $scope.addToServerList = () => {\n            let group;\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                group = {\n                    machineId: e,\n                    ip: parseIpFromMachineId(e),\n                    port: DEFAULT_CLUSTER_SERVER_PORT,\n                    clientSet: [],\n                    namespaceSetStr: 'default,' + $scope.app,\n                    belongToApp: true,\n                };\n                $scope.clusterMap.push(group);\n                removeFromArr($scope.remainingClientAddressList, e);\n                $scope.tmp.curChosenServer = group;\n            });\n            resetChosen();\n        };\n\n        $scope.removeFromServerList = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            chosenServer.clientSet.forEach((e) => {\n                if (e !== undefined) {\n                    $scope.remainingClientAddressList.push(e);\n                }\n            });\n\n            if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {\n                $scope.remainingClientAddressList.push(chosenServer.machineId);\n            } else {\n                alert('提示：非本应用内机器将不会置回空闲列表中');\n            }\n\n            removeFromArr($scope.clusterMap, chosenServer);\n\n            resetChosen();\n\n            if ($scope.clusterMap.length > 0) {\n                $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                $scope.onCurrentServerChange();\n            } else {\n                $scope.tmp.curChosenServer = {};\n            }\n        };\n\n        function retrieveClusterAppInfo() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    if ($scope.clusterMap.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                        $scope.onCurrentServerChange();\n                    }\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterAppInfo();\n\n        $scope.saveAndApplyAssign = () => {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let cm = $scope.clusterMap;\n            if (!cm) {\n                cm = [];\n            }\n            cm.forEach((e) => {\n                e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);\n            });\n            cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);\n            let request = {\n                clusterMap: cm,\n                remainingList: $scope.remainingClientAddressList,\n            };\n            ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        alert('推送完毕。token server 失败列表：' + JSON.stringify(failedServerSet) +\n                            '; token client 失败列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    retrieveClusterAppInfo();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_list.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n        const DEFAULT_NAMESPACE = 'default';\n        const DEFAULT_MAX_ALLOWED_QPS = 20000;\n\n        // tmp for dialog temporary data.\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        $scope.remainingMachineList = [];\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) {\n                return DEFAULT_NAMESPACE;\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                let ns = set[i];\n                if (ns !== DEFAULT_NAMESPACE) {\n                    s = s + ns;\n                    if (i < set.length - 1) {\n                        s = s + ',';\n                    }\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function removeFromArrIf(arr, f) {\n            for (let i = 0; i < arr.length; i++) {\n                if (f(arr[i]) === true) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetAssignDialogChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            let serverCommandPortMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingMachineList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingMachineList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    maxAllowedQps: e.state.server.flow.maxAllowedQps,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(machineId)) {\n                    tmpMap.set(machineId, group);\n                }\n                serverCommandPortMap.set(ip + ':' + e.state.server.port, e.commandPort);\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingMachineList.push(generateMachineId(e));\n                    return;\n                }\n\n                let serverHostPort = targetServer + ':' + targetPort;\n\n                if (serverCommandPortMap.has(serverHostPort)) {\n                    let serverCommandPort = serverCommandPortMap.get(serverHostPort);\n                    let g;\n                    if (serverCommandPort < 0) {\n                        // Not belong to this app.\n                        g = tmpMap.get(serverHostPort);\n                    } else {\n                        // Belong to this app.\n                        g = tmpMap.get(targetServer + '@' + serverCommandPort);\n                    }\n                    g.clientSet.push(machineId);\n                } else {\n                    let group = {\n                        ip: targetServer,\n                        machineId: serverHostPort,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(serverHostPort, group);\n                    // Indicates that it's not belonging to current app.\n                    serverCommandPortMap.set(serverHostPort, -1);\n                }\n\n                // if (!tmpMap.has(serverHostPort)) {\n                //     let group = {\n                //         ip: targetServer,\n                //         machineId: targetServer,\n                //         port: targetPort,\n                //         clientSet: [machineId],\n                //         belongToApp: false,\n                //     };\n                //     tmpMap.set(targetServer, group);\n                // } else {\n                //     let g = tmpMap.get(targetServer);\n                //     g.clientSet.push(machineId);\n                // }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.notChosenServer = (id) => {\n            return id !== $scope.serverAssignDialogData.serverData.currentServer;\n        };\n\n        $scope.onCurrentServerChange = () => {\n            resetAssignDialogChosen();\n        };\n\n        $scope.moveToServerGroup = () => {\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                $scope.serverAssignDialogData.serverData.clientSet.push(e);\n                removeFromArr($scope.remainingMachineList, e);\n            });\n            resetAssignDialogChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingMachineList.push(e);\n                removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e);\n            });\n            resetAssignDialogChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf(':') !== -1) {\n                return machineId.split(':')[0];\n            }\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        function retrieveClusterAssignInfoOfApp() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n\n        $scope.newServerDialog = () => {\n            retrieveClusterAssignInfoOfApp();\n            $scope.serverAssignDialogData = {\n                title: '新增 Token Server',\n                type: 'add',\n                confirmBtnText: '保存',\n                serverData: {\n                    serverType: 0,\n                    clientSet: [],\n                    serverPort: DEFAULT_CLUSTER_SERVER_PORT,\n                    maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS,\n                }\n            };\n            $scope.serverAssignDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',\n                width: 1000,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.modifyServerAssignConfig = (serverVO) => {\n            let id = serverVO.id;\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    let clusterMap = $scope.clusterMap;\n                    let d;\n                    for (let i = 0; i < clusterMap.length; i++) {\n                        if (clusterMap[i].machineId === id) {\n                            d = clusterMap[i];\n                        }\n                    }\n                    if (!d) {\n                        alert('状态错误');\n                        return;\n                    }\n                    $scope.serverAssignDialogData = {\n                        title: 'Token Server 分配编辑',\n                        type: 'edit',\n                        confirmBtnText: '保存',\n                        serverData: {\n                            currentServer: d.machineId,\n                            belongToApp: serverVO.belongToApp,\n                            serverPort: d.port,\n                            clientSet: d.clientSet,\n                        }\n                    };\n                    if (d.maxAllowedQps !== undefined) {\n                        $scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps;\n                    }\n                    $scope.serverAssignDialog = ngDialog.open({\n                        template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',\n                        width: 1000,\n                        overlay: true,\n                        scope: $scope\n                    });\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        };\n\n        function getRemainingMachineList() {\n            return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e));\n        }\n\n        function doApplyNewSingleServerAssign() {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let serverData = $scope.serverAssignDialogData.serverData;\n            let belongToApp = serverData.serverType == 0; // don't modify here!\n            let machineId = serverData.currentServer;\n            let request = {\n                clusterMap: {\n                    machineId: machineId,\n                    ip: parseIpFromMachineId(machineId),\n                    port: serverData.serverPort,\n                    clientSet: serverData.clientSet,\n                    belongToApp: belongToApp,\n                    maxAllowedQps: serverData.maxAllowedQps,\n                },\n                remainingList: getRemainingMachineList(),\n            };\n            ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        let failedSet = [];\n                        if (failedServerSet) {\n                            failedServerSet.forEach((e) => {\n                                failedSet.push(e);\n                            });\n                        }\n                        if (failedClientSet) {\n                            failedClientSet.forEach((e) => {\n                                failedSet.push(e);\n                            });\n                        }\n\n                        alert('推送完毕。失败机器列表：' + JSON.stringify(failedSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        }\n\n        function doApplySingleServerAssignEdit() {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let serverData = $scope.serverAssignDialogData.serverData;\n            let machineId = serverData.currentServer;\n            let request = {\n                clusterMap: {\n                    machineId: machineId,\n                    ip: parseIpFromMachineId(machineId),\n                    port: serverData.serverPort,\n                    clientSet: serverData.clientSet,\n                    belongToApp: serverData.belongToApp,\n                },\n                remainingList: $scope.remainingMachineList,\n            };\n            if (serverData.maxAllowedQps !== undefined) {\n                request.clusterMap.maxAllowedQps = serverData.maxAllowedQps;\n            }\n            ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        let failedSet = [];\n                        failedServerSet.forEach(failedSet.push);\n                        failedClientSet.forEach(failedSet.push);\n                        alert('推送完毕。失败机器列表：' + JSON.stringify(failedSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        }\n\n        $scope.saveAssignForDialog = () => {\n            if (!checkAssignDialogValid()) {\n                return;\n            }\n            if ($scope.serverAssignDialogData.type === 'add') {\n                doApplyNewSingleServerAssign();\n            } else if ($scope.serverAssignDialogData.type === 'edit') {\n                doApplySingleServerAssignEdit();\n            } else {\n                alert('未知的操作');\n            }\n        };\n\n        function checkAssignDialogValid() {\n            let serverData = $scope.serverAssignDialogData.serverData;\n            if (serverData.currentServer === undefined || serverData.currentServer === '') {\n                alert('请指定有效的 Token Server');\n                return false;\n            }\n            if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) {\n                alert('请输入合法的端口值');\n                return false;\n            }\n            if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) {\n                alert('请输入合法的最大允许 QPS');\n                return false;\n            }\n            return true;\n        }\n\n        $scope.viewConnectionDetail = (serverVO) => {\n            $scope.connectionDetailDialogData = {\n                serverData: serverVO\n            };\n            $scope.connectionDetailDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html',\n                width: 700,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        function generateRequestLimitDataStr(limitData) {\n            if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) {\n                 return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps;\n            }\n            for (let i = 0; i < limitData.length; i++) {\n                let crl = limitData[i];\n                if (crl.namespace === $scope.app) {\n                    return '' + crl.currentQps + ' / ' + crl.maxAllowedQps;\n                }\n            }\n            return '0';\n        }\n\n        function processServerListData(serverVO) {\n            if (serverVO.state && serverVO.state.namespaceSet) {\n                serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);\n            }\n            if (serverVO.state && serverVO.state.requestLimitData) {\n                serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData);\n            }\n        }\n\n        $scope.generateConnectionSet = (data) => {\n            let connectionSet = data;\n            let s = '';\n            if (connectionSet) {\n                s = s + '[';\n                for (let i = 0; i < connectionSet.length; i++) {\n                    s = s + connectionSet[i].address;\n                    if (i < connectionSet.length - 1) {\n                        s = s + ', ';\n                    }\n                }\n                s = s + ']';\n            } else {\n                s = '[]';\n            }\n            return s;\n        };\n\n        function retrieveClusterServerInfo() {\n            ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.serverVOList = data.data;\n                    $scope.serverVOList.forEach(processServerListData);\n                } else {\n                    $scope.serverVOList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterServerInfo();\n\n        let confirmUnbindServerDialog;\n        $scope.unbindServer = (id) => {\n            $scope.pendingUnbindIds = [id];\n            $scope.confirmDialog = {\n                title: '移除 Token Server',\n                type: 'unbind_token_server',\n                attentionTitle: '请确认是否移除以下 Token Server（该 server 下的 client 也会解除分配）',\n                attention: id + '',\n                confirmBtnText: '移除',\n            };\n            confirmUnbindServerDialog = ngDialog.open({\n                template: '/app/views/dialog/confirm-dialog.html',\n                scope: $scope,\n                overlay: true\n            });\n        };\n\n        function apiUnbindServerAssign(ids) {\n            ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('成功');\n                    } else {\n                        alert('操作推送完毕，部分失败机器列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n            // confirmUnbindServerDialog.close();\n        }\n\n        // Confirm function for confirm dialog.\n        $scope.confirm = () => {\n            if ($scope.confirmDialog.type === 'unbind_token_server') {\n                apiUnbindServerAssign($scope.pendingUnbindIds);\n            } else {\n                console.error('Error dialog when unbinding token server');\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_manage.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingClientAddressList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(ip)) {\n                    tmpMap.set(ip, group);\n                }\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                    return;\n                }\n\n                if (!tmpMap.has(targetServer)) {\n                    let group = {\n                        ip: targetServer,\n                        machineId: targetServer,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(targetServer, group);\n                } else {\n                    let g = tmpMap.get(targetServer);\n                    g.clientSet.push(machineId);\n                }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.onCurrentServerChange = () => {\n            resetChosen();\n        };\n\n        $scope.remainingClientAddressList = [];\n\n        $scope.moveToServerGroup = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                chosenServer.clientSet.push(e);\n                removeFromArr($scope.remainingClientAddressList, e);\n            });\n            resetChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingClientAddressList.push(e);\n                removeFromArr($scope.tmp.curChosenServer.clientSet, e);\n            });\n            resetChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        $scope.addToServerList = () => {\n            let group;\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                group = {\n                    machineId: e,\n                    ip: parseIpFromMachineId(e),\n                    port: DEFAULT_CLUSTER_SERVER_PORT,\n                    clientSet: [],\n                    namespaceSetStr: 'default,' + $scope.app,\n                    belongToApp: true,\n                };\n                $scope.clusterMap.push(group);\n                removeFromArr($scope.remainingClientAddressList, e);\n                $scope.tmp.curChosenServer = group;\n            });\n            resetChosen();\n        };\n\n        $scope.removeFromServerList = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            chosenServer.clientSet.forEach((e) => {\n                if (e !== undefined) {\n                    $scope.remainingClientAddressList.push(e);\n                }\n            });\n\n            if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {\n                $scope.remainingClientAddressList.push(chosenServer.machineId);\n            } else {\n                alert('提示：非本应用内机器将不会置回空闲列表中');\n            }\n\n            removeFromArr($scope.clusterMap, chosenServer);\n\n            resetChosen();\n\n            if ($scope.clusterMap.length > 0) {\n                $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                $scope.onCurrentServerChange();\n            } else {\n                $scope.tmp.curChosenServer = {};\n            }\n        };\n\n        function retrieveClusterAppInfo() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    if ($scope.clusterMap.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                        $scope.onCurrentServerChange();\n                    }\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterAppInfo();\n\n        $scope.saveAndApplyAssign = () => {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let cm = $scope.clusterMap;\n            if (!cm) {\n                cm = [];\n            }\n            cm.forEach((e) => {\n                e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);\n            });\n            cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);\n            let request = {\n                clusterMap: cm,\n                remainingList: $scope.remainingClientAddressList,\n            };\n            ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        alert('推送完毕。token server 失败列表：' + JSON.stringify(failedServerSet) +\n                            '; token client 失败列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    retrieveClusterAppInfo();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_monitor.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_SERVER = 1;\n\n        $scope.tmp = {\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function processServerData(serverVO) {\n            if (serverVO.state && serverVO.state.namespaceSet) {\n                serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);\n            }\n        }\n\n        $scope.generateConnectionSet = (data) => {\n            let connectionSet = data;\n            let s = '';\n            if (connectionSet) {\n                s = s + '[';\n                for (let i = 0; i < connectionSet.length; i++) {\n                    s = s + connectionSet[i].address;\n                    if (i < connectionSet.length - 1) {\n                        s = s + ', ';\n                    }\n                }\n                s = s + ']';\n            } else {\n                s = '[]';\n            }\n            return s;\n        };\n\n        $scope.onChosenServerChange = () => {\n\n        };\n\n        function retrieveClusterServerInfo() {\n            ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.serverVOList = data.data;\n                    $scope.serverVOList.forEach(processServerData);\n\n                    if ($scope.serverVOList.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.serverVOList[0];\n                        $scope.onChosenServerChange();\n                    }\n                } else {\n                    $scope.serverVOList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterServerInfo();\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_token_client_list.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n\n        const UNSUPPORTED_CODE = 4041;\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n\n        function processClientData(clientVO) {\n\n        }\n\n        $scope.modifyClientConfigDialog = (clientVO) => {\n            if (!clientVO) {\n                return;\n            }\n            $scope.ccDialogData = {\n                ip: clientVO.ip,\n                commandPort: clientVO.commandPort,\n                clientId: clientVO.id,\n                serverHost: clientVO.state.clientConfig.serverHost,\n                serverPort: clientVO.state.clientConfig.serverPort,\n                requestTimeout: clientVO.state.clientConfig.requestTimeout,\n            };\n            $scope.ccDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-client-config-dialog.html',\n                width: 700,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        function checkValidClientConfig(config) {\n            if (!config.serverHost || config.serverHost.trim() == '') {\n                alert('请输入有效的 Token Server IP');\n                return false;\n            }\n            if (config.serverPort === undefined || config.serverPort <= 0 ||  config.serverPort > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            if (config.requestTimeout === undefined || config.requestTimeout <= 0) {\n                alert('请输入有效的请求超时时长');\n                return false;\n            }\n            return true;\n        }\n\n        $scope.doModifyClientConfig = () => {\n            if (!checkValidClientConfig($scope.ccDialogData)) {\n                return;\n            }\n            let id = $scope.ccDialogData.id;\n            let request = {\n                app: $scope.app,\n                ip: $scope.ccDialogData.ip,\n                port: $scope.ccDialogData.commandPort,\n                mode: CLUSTER_MODE_CLIENT,\n                clientConfig: {\n                    serverHost: $scope.ccDialogData.serverHost,\n                    serverPort: $scope.ccDialogData.serverPort,\n                    requestTimeout: $scope.ccDialogData.requestTimeout,\n                }\n            };\n            ClusterStateService.modifyClusterConfig(request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    alert('修改 Token Client 配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        };\n\n        function retrieveClusterTokenClientInfo() {\n            ClusterStateService.fetchClusterClientStateOfApp($scope.app)\n                .success((data) => {\n                    if (data.code === 0 && data.data) {\n                        $scope.loadError = undefined;\n                        $scope.clientVOList = data.data;\n                        $scope.clientVOList.forEach(processClientData);\n                    } else {\n                        $scope.clientVOList = [];\n                        if (data.code === UNSUPPORTED_CODE) {\n                            $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                        } else {\n                            $scope.loadError = {message: data.msg};\n                        }\n                    }\n                })\n                .error(() => {\n                    $scope.loadError = {message: '未知错误'};\n                });\n        }\n\n        retrieveClusterTokenClientInfo();\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_single.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function fetchMachineClusterState() {\n            if (!$scope.macInputModel || $scope.macInputModel === '') {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.stateVO = data.data;\n                    $scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode;\n                    if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) {\n                        $scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet);\n                    }\n                } else {\n                    $scope.stateVO = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error((data, header, config, status) => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        fetchMachineClusterState();\n\n        function checkValidClientConfig(stateVO) {\n            if (!stateVO.client || !stateVO.client.clientConfig) {\n                alert('不合法的配置');\n                return false;\n            }\n            let config = stateVO.client.clientConfig;\n            if (!config.serverHost || config.serverHost.trim() == '') {\n                alert('请输入有效的 Token Server IP');\n                return false;\n            }\n            if (config.serverPort === undefined || config.serverPort <= 0 ||  config.serverPort > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            if (config.requestTimeout === undefined || config.requestTimeout <= 0) {\n                alert('请输入有效的请求超时时长');\n                return false;\n            }\n            return true;\n        }\n\n        function sendClusterClientRequest(stateVO) {\n            if (!checkValidClientConfig(stateVO)) {\n                return;\n            }\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            let request = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n            };\n            request.mode = CLUSTER_MODE_CLIENT;\n            request.clientConfig = stateVO.client.clientConfig;\n            ClusterStateService.modifyClusterConfig(request).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    alert('修改集群限流客户端配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        }\n\n        function checkValidServerConfig(stateVO) {\n            if (!stateVO.server || !stateVO.server.transport) {\n                alert('不合法的配置');\n                return false;\n            }\n            if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') {\n                alert('请输入有效的命名空间集合（多个 namespace 以 , 分隔）');\n                return false;\n            }\n            let transportConfig = stateVO.server.transport;\n            if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            let flowConfig = stateVO.server.flow;\n            if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) {\n                alert('请输入有效的最大允许 QPS');\n                return false;\n            }\n            // if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) {\n            //     alert('请输入有效的连接清理时长 (idleSeconds)');\n            //     return false;\n            // }\n            return true;\n        }\n\n        function sendClusterServerRequest(stateVO) {\n            if (!checkValidServerConfig(stateVO)) {\n                return;\n            }\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            let request = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n            };\n            request.mode = CLUSTER_MODE_SERVER;\n            request.flowConfig = stateVO.server.flow;\n            request.transportConfig = stateVO.server.transport;\n            request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr);\n            ClusterStateService.modifyClusterConfig(request).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    alert('修改集群限流服务端配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        }\n\n\n        $scope.saveConfig = () => {\n            let ok = confirm('是否确定修改集群限流配置？');\n            if (!ok) {\n                return;\n            }\n            let mode = $scope.stateVO.stateInfo.mode;\n            if (mode != 1 && mode != 0) {\n                alert('未知的集群限流模式');\n                return;\n            }\n            if (mode == 0) {\n                sendClusterClientRequest($scope.stateVO);\n            } else {\n                sendClusterServerRequest($scope.stateVO);\n            }\n        };\n\n        function queryAppMachines() {\n            MachineService.getAppMachines($scope.app).success(\n                function (data) {\n                    if (data.code === 0) {\n                        // $scope.machines = data.data;\n                        if (data.data) {\n                            $scope.machines = [];\n                            $scope.macsInputOptionsOrigin = [];\n                            $scope.macsInputOptions = [];\n                            data.data.forEach(function (item) {\n                                if (item.healthy) {\n                                    $scope.macsInputOptionsOrigin.push({\n                                        text: item.ip + ':' + item.port,\n                                        value: item.ip + ':' + item.port\n                                    });\n                                }\n                            });\n                            $scope.macsInputOptions = $scope.macsInputOptionsOrigin;\n                        }\n                        if ($scope.macsInputOptions.length > 0) {\n                            $scope.macInputModel = $scope.macsInputOptions[0].value;\n                        }\n                    } else {\n                        $scope.macsInputOptions = [];\n                    }\n                }\n            );\n        }\n        queryAppMachines();\n\n        $scope.$watch('searchKey', function () {\n            if (!$scope.macsInputOptions) {\n                return;\n            }\n            if ($scope.searchKey) {\n                $scope.macsInputOptions = $scope.macsInputOptionsOrigin\n                    .filter((e) => e.value.indexOf($scope.searchKey) !== -1);\n            } else {\n                $scope.macsInputOptions = $scope.macsInputOptionsOrigin;\n            }\n            if ($scope.macsInputOptions.length > 0) {\n                $scope.macInputModel = $scope.macsInputOptions[0].value;\n            } else {\n                $scope.macInputModel = '';\n            }\n        });\n\n        $scope.$watch('macInputModel', function () {\n            if ($scope.macInputModel) {\n                fetchMachineClusterState();\n            }\n        });\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/degrade.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService',\n  function ($scope, $stateParams, DegradeService, ngDialog, MachineService) {\n    //初始化\n    $scope.app = $stateParams.app;\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var degradeRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.degradeRuleDialog = {\n        title: '编辑降级规则',\n        type: 'edit',\n        confirmBtnText: '保存'\n      };\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default'\n      };\n      $scope.degradeRuleDialog = {\n        title: '新增降级规则',\n        type: 'add',\n        confirmBtnText: '新增'\n      };\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!DegradeService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.degradeRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.degradeRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    function parseDegradeMode(grade) {\n        switch (grade) {\n            case 0:\n              return 'RT';\n            case 1:\n              return '异常比例';\n            case 2:\n              return '异常数';\n            default:\n              return '未知';\n        }\n    }\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除降级规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下降级规则',\n        attention: '资源名: ' + rule.resource +\n            ', 降级模式: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type == 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      DegradeService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      DegradeService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          degradeRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function saveRule(rule, edit) {\n      DegradeService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            degradeRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/flow_v1.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, FlowService, ngDialog,\n    MachineService) {\n    $scope.app = $stateParams.app;\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    $scope.generateThresholdTypeShow = (rule) => {\n      if (!rule.clusterMode) {\n        return '单机';\n      }\n      if (rule.clusterConfig.thresholdType === 0) {\n        return '集群均摊';\n      } else if (rule.clusterConfig.thresholdType === 1) {\n        return '集群总体';\n      } else {\n        return '集群';\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var flowRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.flowRuleDialog = {\n        title: '编辑流控规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 1,\n        strategy: 0,\n        controlBehavior: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n          thresholdType: 0\n        }\n      };\n      $scope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!FlowService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.flowRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.flowRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除流控规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下流控规则',\n        attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp\n          + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      FlowService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      FlowService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    $scope.onOpenAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = false;\n    };\n    $scope.onCloseAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = true;\n    };\n\n    function saveRule(rule, edit) {\n      FlowService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            flowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/flow_v2.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, FlowService, ngDialog,\n    MachineService) {\n    $scope.app = $stateParams.app;\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    $scope.generateThresholdTypeShow = (rule) => {\n      if (!rule.clusterMode) {\n        return '单机';\n      }\n      if (rule.clusterConfig.thresholdType === 0) {\n        return '集群均摊';\n      } else if (rule.clusterConfig.thresholdType === 1) {\n        return '集群总体';\n      } else {\n        return '集群';\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var flowRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.flowRuleDialog = {\n        title: '编辑流控规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 1,\n        strategy: 0,\n        controlBehavior: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n          thresholdType: 0,\n          fallbackToLocalWhenFail: true\n        }\n      };\n      $scope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!FlowService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.flowRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.flowRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除流控规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下流控规则',\n        attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp\n          + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      FlowService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      FlowService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    $scope.onOpenAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = false;\n    };\n    $scope.onCloseAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = true;\n    };\n\n    function saveRule(rule, edit) {\n      FlowService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            flowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/home.js",
    "content": "/**\n * @ngdoc function\n * @name sentinelDashboardApp.controller:MainCtrl\n * @description\n * # MainCtrl\n * Controller of the sentinelDashboardApp\n */\nangular.module('sentinelDashboardApp')\n  .controller('HomeCtrl', ['$scope', '$position', function ($scope, $position) {\n    // do noting\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/identity.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',\n  'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService',\n  '$interval', '$location', '$timeout',\n  function ($scope, $stateParams, IdentityService, ngDialog,\n    FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) {\n\n    $scope.app = $stateParams.app;\n\n    $scope.currentPage = 1;\n    $scope.pageSize = 16;\n    $scope.totalPage = 1;\n    $scope.totalCount = 0;\n    $scope.identities = [];\n    // 数据自动刷新频率, 默认10s\n    var DATA_REFRESH_INTERVAL = 30;\n\n    $scope.isExpand = true;\n    $scope.searchKey = '';\n    $scope.firstExpandAll = false;\n    $scope.isTreeView = true;\n\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n    $scope.table = null;\n\n    var flowRuleDialog;\n    var flowRuleDialogScope;\n    $scope.addNewFlowRule = function (resource) {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      flowRuleDialogScope = $scope.$new(true);\n      flowRuleDialogScope.currentRule = {\n        enable: false,\n        strategy: 0,\n        grade: 1,\n        controlBehavior: 0,\n        resource: resource,\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n            thresholdType: 0\n        },\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1]\n      };\n\n      flowRuleDialogScope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        saveAndContinueBtnText: '新增并继续添加',\n        showAdvanceButton: true\n      };\n      // $scope.flowRuleDialog = {\n      //     showAdvanceButton : true\n      // };\n      flowRuleDialogScope.saveRule = saveFlowRule;\n      flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue;\n      flowRuleDialogScope.onOpenAdvanceClick = function () {\n        flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false;\n      };\n      flowRuleDialogScope.onCloseAdvanceClick = function () {\n        flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true;\n      };\n\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: flowRuleDialogScope\n      });\n    };\n\n    function saveFlowRule() {\n      if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {\n        return;\n      }\n      FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {\n        if (data.code === 0) {\n          flowRuleDialog.close();\n          let url = '/dashboard/flow/' + $scope.app;\n          $location.path(url);\n        } else {\n          alert('失败!');\n        }\n      }).error((data, header, config, status) => {\n          alert('未知错误');\n      });\n    }\n\n    function saveFlowRuleAndContinue() {\n        if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {\n            return;\n        }\n      FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n    var degradeRuleDialog;\n    var degradeRuleDialogScope;\n    $scope.addNewDegradeRule = function (resource) {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      degradeRuleDialogScope = $scope.$new(true);\n      degradeRuleDialogScope.currentRule = {\n        enable: false,\n        grade: 0,\n        strategy: 0,\n        resource: resource,\n        limitApp: 'default',\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1]\n      };\n\n      degradeRuleDialogScope.degradeRuleDialog = {\n        title: '新增降级规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        saveAndContinueBtnText: '新增并继续添加'\n      };\n      degradeRuleDialogScope.saveRule = saveDegradeRule;\n      degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue;\n\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: degradeRuleDialogScope\n      });\n    };\n\n    function saveDegradeRule() {\n        if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {\n            return;\n        }\n      DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          degradeRuleDialog.close();\n          var url = '/dashboard/degrade/' + $scope.app;\n          $location.path(url);\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n    function saveDegradeRuleAndContinue() {\n        if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {\n            return;\n        }\n      DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          degradeRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n      let authorityRuleDialog;\n      let authorityRuleDialogScope;\n\n      function saveAuthorityRule() {\n          let ruleEntity = authorityRuleDialogScope.currentRule;\n          if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          AuthorityRuleService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  authorityRuleDialog.close();\n                  let url = '/dashboard/authority/' + $scope.app;\n                  $location.path(url);\n              } else {\n                  alert('添加规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加规则失败：' + data.msg);\n              } else {\n                  alert(\"添加规则失败：未知错误\");\n              }\n          });\n      }\n\n      function saveAuthorityRuleAndContinue() {\n          let ruleEntity = authorityRuleDialogScope.currentRule;\n          if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          AuthorityRuleService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  authorityRuleDialog.close();\n              } else {\n                  alert('添加规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加规则失败：' + data.msg);\n              } else {\n                  alert(\"添加规则失败：未知错误\");\n              }\n          });\n      }\n\n      $scope.addNewAuthorityRule = function (resource) {\n          if (!$scope.macInputModel) {\n              return;\n          }\n          let mac = $scope.macInputModel.split(':');\n          authorityRuleDialogScope = $scope.$new(true);\n          authorityRuleDialogScope.currentRule = {\n              app: $scope.app,\n              ip: mac[0],\n              port: mac[1],\n              rule: {\n                  resource: resource,\n                  strategy: 0,\n                  limitApp: '',\n              }\n          };\n\n          authorityRuleDialogScope.authorityRuleDialog = {\n              title: '新增授权规则',\n              type: 'add',\n              confirmBtnText: '新增',\n              saveAndContinueBtnText: '新增并继续添加'\n          };\n          authorityRuleDialogScope.saveRule = saveAuthorityRule;\n          authorityRuleDialogScope.saveRuleAndContinue = saveAuthorityRuleAndContinue;\n\n          authorityRuleDialog = ngDialog.open({\n              template: '/app/views/dialog/authority-rule-dialog.html',\n              width: 680,\n              overlay: true,\n              scope: authorityRuleDialogScope\n          });\n      };\n\n      let paramFlowRuleDialog;\n      let paramFlowRuleDialogScope;\n\n      function saveParamFlowRule() {\n          let ruleEntity = paramFlowRuleDialogScope.currentRule;\n          if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          ParamFlowService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  paramFlowRuleDialog.close();\n                  let url = '/dashboard/paramFlow/' + $scope.app;\n                  $location.path(url);\n              } else {\n                  alert('添加热点规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加热点规则失败：' + data.msg);\n              } else {\n                  alert(\"添加热点规则失败：未知错误\");\n              }\n          });\n      }\n\n      function saveParamFlowRuleAndContinue() {\n          let ruleEntity = paramFlowRuleDialogScope.currentRule;\n          if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          ParamFlowService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  paramFlowRuleDialog.close();\n              } else {\n                  alert('添加热点规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加热点规则失败：' + data.msg);\n              } else {\n                  alert(\"添加热点规则失败：未知错误\");\n              }\n          });\n      }\n\n      $scope.addNewParamFlowRule = function (resource) {\n          if (!$scope.macInputModel) {\n              return;\n          }\n          let mac = $scope.macInputModel.split(':');\n          paramFlowRuleDialogScope = $scope.$new(true);\n          paramFlowRuleDialogScope.currentRule = {\n              app: $scope.app,\n              ip: mac[0],\n              port: mac[1],\n              rule: {\n                  resource: resource,\n                  grade: 1,\n                  paramFlowItemList: [],\n                  count: 0,\n                  limitApp: 'default',\n              }\n          };\n\n          paramFlowRuleDialogScope.paramFlowRuleDialog = {\n              title: '新增热点规则',\n              type: 'add',\n              confirmBtnText: '新增',\n              saveAndContinueBtnText: '新增并继续添加',\n              supportAdvanced: false,\n              showAdvanceButton: true\n          };\n          paramFlowRuleDialogScope.saveRule = saveParamFlowRule;\n          paramFlowRuleDialogScope.saveRuleAndContinue = saveParamFlowRuleAndContinue;\n          // paramFlowRuleDialogScope.onOpenAdvanceClick = function () {\n          //     paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = false;\n          // };\n          // paramFlowRuleDialogScope.onCloseAdvanceClick = function () {\n          //     paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = true;\n          // };\n\n          paramFlowRuleDialog = ngDialog.open({\n              template: '/app/views/dialog/param-flow-rule-dialog.html',\n              width: 680,\n              overlay: true,\n              scope: paramFlowRuleDialogScope\n          });\n      };\n\n    var searchHandler;\n    $scope.searchChange = function (searchKey) {\n      $timeout.cancel(searchHandler);\n      searchHandler = $timeout(function () {\n        $scope.searchKey = searchKey;\n        $scope.isExpand = true;\n        $scope.firstExpandAll = true;\n        reInitIdentityDatas();\n        $scope.firstExpandAll = false;\n      }, 600);\n    };\n\n    $scope.initTreeTable = function () {\n      // if (!$scope.table) {\n        com_github_culmat_jsTreeTable.register(window);\n        $scope.table = window.treeTable($('#identities'));\n      // }\n    };\n\n    $scope.expandAll = function () {\n      $scope.isExpand = true;\n    };\n    $scope.collapseAll = function () {\n      $scope.isExpand = false;\n    };\n    $scope.treeView = function () {\n      $scope.isTreeView = true;\n      queryIdentities();\n    };\n    $scope.listView = function () {\n      $scope.isTreeView = false;\n      queryIdentities();\n    };\n\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code === 0) {\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    }\n\n    // Fetch all machines by current app name.\n    queryAppMachines();\n\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        reInitIdentityDatas();\n      }\n    });\n\n    $scope.$on('$destroy', function () {\n      $interval.cancel(intervalId);\n    });\n\n    var intervalId;\n    function reInitIdentityDatas() {\n      // $interval.cancel(intervalId);\n      queryIdentities();\n      // intervalId = $interval(function () {\n      //    queryIdentities();\n      // }, DATA_REFRESH_INTERVAL * 1000);\n    };\n\n    function queryIdentities() {\n      var mac = $scope.macInputModel.split(':');\n      if (mac == null || mac.length < 2) {\n        return;\n      }\n      if ($scope.isTreeView) {\n        IdentityService.fetchIdentityOfMachine(mac[0], mac[1], $scope.searchKey).success(\n          function (data) {\n            if (data.code == 0 && data.data) {\n              $scope.identities = data.data;\n              $scope.totalCount = $scope.identities.length;\n            } else {\n              $scope.identities = [];\n              $scope.totalCount = 0;\n            }\n          }\n        );\n      } else {\n        IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success(\n          function (data) {\n            if (data.code == 0 && data.data) {\n              $scope.identities = data.data;\n              $scope.totalCount = $scope.identities.length;\n            } else {\n              $scope.identities = [];\n              $scope.totalCount = 0;\n            }\n          }\n        );\n      }\n    };\n    $scope.queryIdentities = queryIdentities;\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/login.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService',\n  function ($scope, $state, $window, LoginService) {\n    // If auth, jump to the index page directly\n    if ($window.localStorage.getItem('session_sentinel_admin')) {\n      $state.go('dashboard');\n    }\n\n    $scope.login = function () {\n      if (!$scope.username) {\n        alert('请输入用户名');\n        return;\n      }\n\n      if (!$scope.password) {\n        alert('请输入密码');\n        return;\n      }\n\n      var param = {\"username\": $scope.username, \"password\": $scope.password};\n\n      LoginService.login(param).success(function (data) {\n        if (data.code == 0) {\n          $window.localStorage.setItem('session_sentinel_admin', {\n            username: data.data\n          });\n\n          $state.go('dashboard');\n        } else {\n          alert(data.msg);\n        }\n      });\n    };\n  }]\n);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/machine.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService',\n  function ($scope, $stateParams, MachineService) {\n    $scope.app = $stateParams.app;\n    $scope.propertyName = '';\n    $scope.reverse = false;\n    $scope.currentPage = 1;\n    $scope.machines = [];\n    $scope.machinesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n\n    $scope.sortBy = function (propertyName) {\n      // console.log('machine sortBy ' + propertyName);\n      $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;\n      $scope.propertyName = propertyName;\n    };\n    \n    $scope.reloadMachines = function() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          // console.log('get machines: ' + data.data[0].hostname)\n          if (data.code == 0 && data.data) {\n            $scope.machines = data.data;\n            var healthy = 0;\n            $scope.machines.forEach(function (item) {\n              if (item.healthy) {\n                  healthy++;\n              }\n              if (!item.hostname) {\n                item.hostname = '未知'\n              }\n            })\n            $scope.healthyCount = healthy;\n            $scope.machinesPageConfig.totalCount = $scope.machines.length;\n          } else {\n            $scope.machines = [];\n            $scope.healthyCount = 0;\n          }\n        }\n      );\n    };\n    \n    $scope.removeMachine = function(ip, port) {\n      if (!confirm(\"confirm to remove machine [\" + ip + \":\" + port + \"]?\")) {\n        return;\n      }\n      MachineService.removeAppMachine($scope.app, ip, port).success(\n        function(data) {\n          if (data.code == 0) {\n            $scope.reloadMachines();\n          } else {\n            alert(\"remove failed\");\n          }\n        }\n      );\n    };\n    \n    $scope.reloadMachines();\n    \n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/main.js",
    "content": "/**\n * @ngdoc function\n * @name sentinelDashboardApp.controller:MainCtrl\n * @description\n * # MainCtrl\n * Controller of the sentinelDashboardApp\n */\nangular.module('sentinelDashboardApp')\n  .controller('DashboardCtrl', ['$scope', '$position', function ($scope, $position) {\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/metric.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout',\n  function ($scope, $stateParams, MetricService, $interval, $timeout) {\n\n    $scope.endTime = new Date();\n    $scope.startTime = new Date();\n    $scope.startTime.setMinutes($scope.endTime.getMinutes() - 30);\n    $scope.startTimeFmt = formatDate($scope.startTime);\n    $scope.endTimeFmt = formatDate($scope.endTime);\n    function formatDate(date) {\n      return moment(date).format('YYYY/MM/DD HH:mm:ss');\n    }\n    $scope.changeStartTime = function (startTime) {\n      $scope.startTime = new Date(startTime);\n      $scope.startTimeFmt = formatDate(startTime);\n    };\n    $scope.changeEndTime = function (endTime) {\n      $scope.endTime = new Date(endTime);\n      $scope.endTimeFmt = formatDate(endTime);\n    };\n\n    $scope.app = $stateParams.app;\n    // 数据自动刷新频率\n    var DATA_REFRESH_INTERVAL = 1000 * 10;\n\n    $scope.servicePageConfig = {\n      pageSize: 6,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.servicesChartConfigs = [];\n\n    $scope.pageChanged = function (newPageNumber) {\n      $scope.servicePageConfig.currentPageIndex = newPageNumber;\n      reInitIdentityDatas();\n    };\n\n    var searchT;\n    $scope.searchService = function () {\n      $timeout.cancel(searchT);\n      searchT = $timeout(function () {\n        reInitIdentityDatas();\n      }, 600);\n    }\n\n    var intervalId;\n    reInitIdentityDatas();\n    function reInitIdentityDatas() {\n      $interval.cancel(intervalId);\n      queryIdentityDatas();\n      intervalId = $interval(function () {\n        queryIdentityDatas();\n      }, DATA_REFRESH_INTERVAL);\n    };\n\n    $scope.$on('$destroy', function () {\n      $interval.cancel(intervalId);\n    });\n    $scope.initAllChart = function () {\n      $.each($scope.metrics, function (idx, metric) {\n        if (idx == $scope.metrics.length - 1) {\n          return;\n        }\n        const chart = new G2.Chart({\n          container: 'chart' + idx,\n          forceFit: true,\n          width: 100,\n          height: 250,\n          padding: [10, 30, 70, 50]\n        });\n        var maxQps = 0;\n        for (var i in metric.data) {\n          var item = metric.data[i];\n          if (item.passQps > maxQps) {\n            maxQps = item.passQps;\n          }\n          if (item.blockQps > maxQps) {\n            maxQps = item.blockQps;\n          }\n        }\n        chart.source(metric.data);\n        chart.scale('timestamp', {\n          type: 'time',\n          mask: 'YYYY-MM-DD HH:mm:ss'\n        });\n        chart.scale('passQps', {\n          min: 0,\n          max: maxQps,\n          fine: true,\n          alias: '通过 QPS'\n          // max: 10\n        });\n        chart.scale('blockQps', {\n          min: 0,\n          max: maxQps,\n          fine: true,\n          alias: '拒绝 QPS',\n        });\n        chart.scale('rt', {\n          min: 0,\n          fine: true,\n        });\n        chart.axis('rt', {\n          grid: null,\n          label: null\n        });\n        chart.axis('blockQps', {\n          grid: null,\n          label: null\n        });\n\n        chart.axis('timestamp', {\n          label: {\n            textStyle: {\n              textAlign: 'center', // 文本对齐方向，可取值为： start center end\n              fill: '#404040', // 文本的颜色\n              fontSize: '11', // 文本大小\n              //textBaseline: 'top', // 文本基准线，可取 top middle bottom，默认为middle\n            },\n            autoRotate: false,\n            formatter: function (text, item, index) {\n              return text.substring(11, 11 + 5);\n            }\n          }\n        });\n        chart.legend({\n          custom: true,\n          position: 'bottom',\n          allowAllCanceled: true,\n          itemFormatter: function (val) {\n            if ('passQps' === val) {\n              return '通过 QPS';\n            }\n            if ('blockQps' === val) {\n              return '拒绝 QPS';\n            }\n            return val;\n          },\n          items: [\n            { value: 'passQps', marker: { symbol: 'hyphen', stroke: 'green', radius: 5, lineWidth: 2 } },\n            { value: 'blockQps', marker: { symbol: 'hyphen', stroke: 'blue', radius: 5, lineWidth: 2 } },\n            //{ value: 'rt', marker: {symbol: 'hyphen', stroke: 'gray', radius: 5, lineWidth: 2} },\n          ],\n          onClick: function (ev) {\n            const item = ev.item;\n            const value = item.value;\n            const checked = ev.checked;\n            const geoms = chart.getAllGeoms();\n            for (var i = 0; i < geoms.length; i++) {\n              const geom = geoms[i];\n              if (geom.getYScale().field === value) {\n                if (checked) {\n                  geom.show();\n                } else {\n                  geom.hide();\n                }\n              }\n            }\n          }\n        });\n        chart.line().position('timestamp*passQps').size(1).color('green').shape('smooth');\n        chart.line().position('timestamp*blockQps').size(1).color('blue').shape('smooth');\n        //chart.line().position('timestamp*rt').size(1).color('gray').shape('smooth');\n        G2.track(false);\n        chart.render();\n      });\n    };\n\n    $scope.metrics = [];\n    $scope.emptyObjs = [];\n    function queryIdentityDatas() {\n      var params = {\n        app: $scope.app,\n        pageIndex: $scope.servicePageConfig.currentPageIndex,\n        pageSize: $scope.servicePageConfig.pageSize,\n        desc: $scope.isDescOrder,\n        searchKey: $scope.serviceQuery\n      };\n      MetricService.queryAppSortedIdentities(params).success(function (data) {\n        $scope.metrics = [];\n        $scope.emptyObjs = [];\n        if (data.code === 0 && data.data) {\n          var metricsObj = data.data.metric;\n          var identityNames = Object.keys(metricsObj);\n          if (identityNames.length < 1) {\n            $scope.emptyServices = true;\n          } else {\n            $scope.emptyServices = false;\n          }\n          $scope.servicePageConfig.totalPage = data.data.totalPage;\n          $scope.servicePageConfig.pageSize = data.data.pageSize;\n          var totalCount = data.data.totalCount;\n          $scope.servicePageConfig.totalCount = totalCount;\n          for (i = 0; i < totalCount; i++) {\n            $scope.emptyObjs.push({});\n          }\n          $.each(identityNames, function (idx, identityName) {\n            var identityDatas = metricsObj[identityName];\n            var metrics = {};\n            metrics.resource = identityName;\n            // metrics.data = identityDatas;\n            metrics.data = fillZeros(identityDatas);\n            metrics.shortData = lastOfArray(identityDatas, 6);\n            $scope.metrics.push(metrics);\n          });\n          // push an empty element in the last, for ng-init reasons.\n          $scope.metrics.push([]);\n        } else {\n          $scope.emptyServices = true;\n          console.log(data.msg);\n        }\n      });\n    };\n    function fillZeros(metricData) {\n      if (!metricData || metricData.length == 0) {\n        return [];\n      }\n      var filledData = [];\n      filledData.push(metricData[0]);\n      var lastTime = metricData[0].timestamp / 1000;\n      for (var i = 1; i < metricData.length; i++) {\n        var curTime = metricData[i].timestamp / 1000;\n        if (curTime > lastTime + 1) {\n          for (var j = lastTime + 1; j < curTime; j++) {\n            filledData.push({\n                \"timestamp\": j * 1000,\n                \"passQps\": 0,\n                \"blockQps\": 0,\n                \"successQps\": 0,\n                \"exception\": 0,\n                \"rt\": 0,\n                \"count\": 0\n            })\n          }\n        }\n        filledData.push(metricData[i]);\n        lastTime = curTime;\n      }\n      return filledData;\n    }\n    function lastOfArray(arr, n) {\n      if (!arr.length) {\n        return [];\n      }\n      var rs = [];\n      for (i = 0; i < n && i < arr.length; i++) {\n        rs.push(arr[arr.length - 1 - i]);\n      }\n      return rs;\n    }\n\n    $scope.isDescOrder = true;\n    $scope.setDescOrder = function () {\n      $scope.isDescOrder = true;\n      reInitIdentityDatas();\n    }\n    $scope.setAscOrder = function () {\n      $scope.isDescOrder = false;\n      reInitIdentityDatas();\n    }\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/param_flow.js",
    "content": "/**\n * Parameter flow control controller.\n * \n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, ParamFlowService, ngDialog,\n    MachineService) {\n    const UNSUPPORTED_CODE = 4041;\n    $scope.app = $stateParams.app;\n    $scope.curExItem = {};\n\n    $scope.paramItemClassTypeList = [\n      'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte'\n    ];\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n      function updateSingleParamItem(arr, v, t, c) {\n          for (let i = 0; i < arr.length; i++) {\n              if (arr[i].object === v && arr[i].classType === t) {\n                  arr[i].count = c;\n                  return;\n              }\n          }\n          arr.push({object: v, classType: t, count: c});\n      }\n\n      function removeSingleParamItem(arr, v, t) {\n          for (let i = 0; i < arr.length; i++) {\n              if (arr[i].object === v && arr[i].classType === t) {\n                  arr.splice(i, 1);\n                  break;\n              }\n          }\n      }\n\n      function isNumberClass(classType) {\n        return classType === 'int' || classType === 'double' ||\n            classType === 'float' || classType === 'long' || classType === 'short';\n      }\n\n      function isByteClass(classType) {\n          return classType === 'byte';\n      }\n\n      function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n      }\n\n      function notGoodNumber(num) {\n          return num === undefined || num === '' || isNaN(num);\n      }\n\n      function notGoodNumberBetweenExclusive(num, l ,r) {\n          return num === undefined || num === '' || isNaN(num) || num < l || num > r;\n      }\n\n      $scope.notValidParamItem = (curExItem) => {\n        if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {\n          return true;\n        }\n        if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {\n          return true;\n        }\n        return curExItem.object === undefined || curExItem.classType === undefined ||\n            notNumberAtLeastZero(curExItem.count);\n      };\n\n      $scope.addParamItem = () => {\n          updateSingleParamItem($scope.currentRule.rule.paramFlowItemList,\n              $scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count);\n          let oldItem = $scope.curExItem;\n          $scope.curExItem = {classType: oldItem.classType};\n      };\n\n      $scope.removeParamItem = (v, t) => {\n          removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t);\n      };\n\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      let mac = $scope.macInputModel.split(':');\n      ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1])\n        .success(function (data) {\n          if (data.code === 0 && data.data) {\n            $scope.loadError = undefined;\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n            if (data.code === UNSUPPORTED_CODE) {\n              $scope.loadError = {message: \"机器 \" + mac[0] + \":\" + mac[1] + \" 的 Sentinel 客户端版本不支持热点参数限流功能，请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。\"}\n            } else {\n              $scope.loadError = {message: data.msg}\n            }\n          }\n        })\n        .error((data, header, config, status) => {\n          $scope.loadError = {message: \"未知错误\"}\n        });\n    }\n    $scope.getMachineRules = getMachineRules;\n    getMachineRules();\n\n    var paramFlowRuleDialog;\n\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.paramFlowRuleDialog = {\n        title: '编辑热点规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        supportAdvanced: true,\n        showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0\n      };\n      paramFlowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/param-flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n      $scope.curExItem = {};\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        rule: {\n          grade: 1,\n          paramFlowItemList: [],\n          count: 0,\n          limitApp: 'default',\n          clusterMode: false,\n          clusterConfig: {\n            thresholdType: 0\n          }\n        }\n      };\n      $scope.paramFlowRuleDialog = {\n        title: '新增热点规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      paramFlowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/param-flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n      $scope.curExItem = {};\n    };\n\n      $scope.onOpenAdvanceClick = function () {\n          $scope.paramFlowRuleDialog.showAdvanceButton = false;\n      };\n      $scope.onCloseAdvanceClick = function () {\n          $scope.paramFlowRuleDialog.showAdvanceButton = true;\n      };\n\n    $scope.saveRule = function () {\n      if (!ParamFlowService.checkRuleValid($scope.currentRule.rule)) {\n        return;\n      }\n      if ($scope.paramFlowRuleDialog.type === 'add') {\n        addNewRuleAndPush($scope.currentRule);\n      } else if ($scope.paramFlowRuleDialog.type === 'edit') {\n        saveRuleAndPush($scope.currentRule, true);\n      }\n    };\n\n    function addNewRuleAndPush(rule) {\n      ParamFlowService.addNewRule(rule).success((data) => {\n        if (data.success) {\n          getMachineRules();\n          paramFlowRuleDialog.close();\n        } else {\n          alert('添加规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('添加规则失败：' + data.msg);\n        } else {\n          alert(\"添加规则失败：未知错误\");\n        }\n      });\n    }\n\n    function saveRuleAndPush(rule, edit) {\n      ParamFlowService.saveRule(rule).success(function (data) {\n        if (data.success) {\n          alert(\"修改规则成功\");\n          getMachineRules();\n          if (edit) {\n            paramFlowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('修改规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('修改规则失败：' + data.msg);\n        } else {\n          alert(\"修改规则失败：未知错误\");\n        }\n      });\n    }\n\n    function deleteRuleAndPush(entity) {\n      if (entity.id === undefined || isNaN(entity.id)) {\n        alert('规则 ID 不合法！');\n        return;\n      }\n      ParamFlowService.deleteRule(entity).success((data) => {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('删除规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('删除规则失败：' + data.msg);\n        } else {\n          alert(\"删除规则失败：未知错误\");\n        }\n      });\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (ruleEntity) {\n      $scope.currentRule = ruleEntity;\n      console.log('deleting: ' + ruleEntity);\n      $scope.confirmDialog = {\n        title: '删除热点规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下热点参数限流规则',\n        attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx +\n            ', 限流模式: ' + (ruleEntity.rule.grade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRuleAndPush($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    queryAppMachines();\n\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/system.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SystemCtl', ['$scope', '$stateParams', 'SystemService', 'ngDialog', 'MachineService',\n  function ($scope, $stateParams, SystemService,\n    ngDialog, MachineService) {\n    //初始化\n    $scope.app = $stateParams.app;\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      SystemService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $.each($scope.rules, function (idx, rule) {\n              // rule.orginEnable = rule.enable;\n              if (rule.avgLoad >= 0) {\n                rule.grade = 0;\n              } else if (rule.avgRt >= 0) {\n                rule.grade = 1;\n              } else if (rule.maxThread >= 0) {\n                rule.grade = 2;\n              } else if (rule.qps >= 0) {\n                rule.grade = 3;\n              }\n            });\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n    var systemRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.systemRuleDialog = {\n        title: '编辑系统保护规则',\n        type: 'edit',\n        confirmBtnText: '保存'\n      };\n      systemRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/system-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n      };\n      $scope.systemRuleDialog = {\n        title: '新增系统保护规则',\n        type: 'add',\n        confirmBtnText: '新增'\n      };\n      systemRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/system-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if ($scope.systemRuleDialog.type == 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.systemRuleDialog.type == 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    }\n\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      var ruleTypeDesc = '';\n      var ruleTypeCount = null;\n      if (rule.avgLoad != -1) {\n        ruleTypeDesc = 'LOAD';\n        ruleTypeCount = rule.avgLoad;\n      } else if (rule.avgRt != -1) {\n        ruleTypeDesc = 'RT';\n        ruleTypeCount = rule.avgRt;\n      } else if (rule.maxThread != -1) {\n        ruleTypeDesc = '线程数';\n        ruleTypeCount = rule.maxThread;\n      } else if (rule.qps != -1) {\n        ruleTypeDesc = 'QPS';\n        ruleTypeCount = rule.qps;\n      }\n\n      $scope.confirmDialog = {\n        title: '删除系统保护规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下系统保护规则',\n        attention: '阈值类型: ' + ruleTypeDesc + ', 阈值: ' + ruleTypeCount,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type == 'delete_rule') {\n        deleteRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'enable_rule') {\n        //     $scope.currentRule.enable = true;\n        //     saveRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'disable_rule') {\n        //     $scope.currentRule.enable = false;\n        //     saveRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'enable_all') {\n        //     enableAll($scope.app);\n        // } else if ($scope.confirmDialog.type == 'disable_all') {\n        //     disableAll($scope.app);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      SystemService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      SystemService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          systemRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function saveRule(rule, edit) {\n      SystemService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          // if (rule.enable) {\n          //    rule.orginEnable = true;\n          // } else {\n          //    rule.orginEnable = false;\n          // }\n          getMachineRules();\n          if (edit) {\n            systemRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/header/header.html",
    "content": "<div class=\"header\">\n  <nav class=\"navbar navbar-fixed-top navbar-inverse\" style=\"\" role=\"navigation\">\n    <div class=\"navbar-brand\">\n      <span style=\"color: #fff;font-size: 26px;\">Sentinel 控制台</span>\n    </div>\n    <ul class=\"nav navbar-nav navbar-right\">\n      <li>\n        <a href=\"javascript:void(0);\" ng-click=\"logout()\"\n           style=\"margin: 3px 15px 0 0;\"><span class=\"glyphicon glyphicon-log-out\"></span>&nbsp;注销</a>\n      </li>\n    </ul>\n  </nav>\n  <!-- end nav -->\n  <sidebar></sidebar>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/header/header.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description\n * # adminPosHeader\n */\nangular.module('sentinelDashboardApp')\n  .directive('header', ['AuthService', function () {\n    return {\n      templateUrl: 'app/scripts/directives/header/header.html',\n      restrict: 'E',\n      replace: true,\n      controller: function ($scope, $state, $window, AuthService) {\n        if (!$window.localStorage.getItem('session_sentinel_admin')) {\n          $state.go('login');\n        }\n\n        $scope.logout = function () {\n          AuthService.logout().success(function (data) {\n            if (data.code == 0) {\n              $window.localStorage.removeItem(\"session_sentinel_admin\");\n              $state.go('login');\n            } else {\n              alert('logout error');\n            }\n          });\n        }\n      }\n    }\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.html",
    "content": "<li class=\"sidebar-search\">\n  <div class=\"input-group custom-search-form\">\n    <input type=\"text\" class=\"form-control\" ng-model=\"searchApp\" placeholder=\"Search...\">\n    <span class=\"input-group-btn\">\n      <button class=\"btn btn-default\" type=\"button\">\n        <i class=\"fa fa-search\"></i>\n      </button>\n    </span>\n  </div>\n</li>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description\n * # adminPosHeader\n */\n\nangular.module('sentinelDashboardApp')\n  .directive('sidebarSearch', function () {\n    return {\n      templateUrl: 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.html',\n      restrict: 'E',\n      replace: true,\n      scope: {\n      },\n      controller: function ($scope) {\n        $scope.selectedMenu = 'home';\n      }\n    }\n  });\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html",
    "content": "<div class=\"navbar-default sidebar\" role=\"navigation\" style=\"overflow-y: auto;\">\n  <div class=\"sidebar-nav navbar-collapse\">\n    <ul class=\"nav in\" id=\"side-menu\">\n      <li class=\"sidebar-search\">\n        <div class=\"input-group\" style=\"\">\n          <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"应用名\" ng-model=\"searchApp\">\n          <span class=\"input-group-btn\">\n            <button class=\"btn btn-secondary btn-default-inverse\" type=\"button\">搜索</button>\n          </span>\n        </div>\n      </li>\n      <li ui-sref-active=\"active\">\n        <a ui-sref=\"dashboard.home\" style=\"font-size:16px;\">\n          <span class=\"glyphicon glyphicon-dashboard\"></span>\n          &nbsp;&nbsp;首页</a>\n      </li>\n\n      <li ng-class=\"{active: true}\" ng-repeat=\"entry in apps | filter: { app: searchApp }\">{{dropDown}}\n        <a href=\"javascript:void(0);\" ng-click=\"click($event)\" collapse=\"{{collpaseall == 1}}\" style=\"font-size: 16px;word-break: break-word;\">\n          &nbsp;{{entry.app}}\n          <span class=\"fa arrow\"></span>\n          <span class=\"arrow\">({{entry.heathyCount}}/{{entry.machines.length}})</span>\n        </a>\n\n        <!--<ul class=\"nav nav-second-level\" collapse=\"{{entry.active}}\" style=\"display: none;\">-->\n        <ul class=\"nav nav-second-level\" ng-show=\"entry.active\">\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.metric({app: entry.app})\">\n              <i class=\"fa fa-bar-chart\"></i>&nbsp;&nbsp;实时监控</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.identity({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-list-alt\"></i>&nbsp;&nbsp;簇点链路</a>\n          </li>\n\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.flow({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-filter\"></i>&nbsp;&nbsp;流控规则</a>\n          </li>\n          <!--<li ui-sref-active=\"active\">-->\n            <!--<a ui-sref=\"dashboard.flow({app: entry.app})\">-->\n              <!--<i class=\"glyphicon glyphicon-filter\"></i>&nbsp;&nbsp;流控规则 V1</a>-->\n          <!--</li>-->\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.degrade({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-flash\"></i>&nbsp;&nbsp;降级规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.paramFlow({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-fire\"></i>&nbsp;&nbsp;热点规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.system({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-lock\"></i>&nbsp;&nbsp;系统规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.authority({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-check\"></i>&nbsp;&nbsp;授权规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.clusterAppServerList({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-cloud\"></i>&nbsp;&nbsp;集群流控</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.machine({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-th-list\"></i>&nbsp;&nbsp;机器列表</a>\n          </li>\n\n        </ul>\n        <!-- /.nav-second-level -->\n      </li>\n    </ul>\n  </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description # adminPosHeader\n */\n\nangular.module('sentinelDashboardApp')\n  .directive('sidebar', ['$location', '$stateParams', 'AppService', function () {\n    return {\n      templateUrl: 'app/scripts/directives/sidebar/sidebar.html',\n      restrict: 'E',\n      replace: true,\n      scope: {\n      },\n      controller: function ($scope, $stateParams, $location, AppService) {\n        $scope.app = $stateParams.app;\n        $scope.collapseVar = 0;\n\n        // app\n        AppService.getApps().success(\n          function (data) {\n            if (data.code === 0) {\n              let initHashApp = $location.path().split('/')[3];\n              $scope.apps = data.data;\n              $scope.apps = $scope.apps.map(function (item) {\n                if (item.app === initHashApp) {\n                  item.active = true;\n                }\n                var heathyCount = 0;\n                for (var i in item.machines) {\n                  if (item.machines[i].healthy) {\n                      heathyCount++;\n                  }\n                }\n                item.heathyCount = heathyCount;\n                if (item.shown) {\n                  return item;\n                }\n              });\n            }\n          }\n        );\n\n        // toggle side bar\n        $scope.click = function ($event) {\n          let entry = angular.element($event.target).scope().entry;\n          entry.active = !entry.active;// toggle this clicked app bar\n\n          $scope.apps.forEach(function (item) {// collapse other app bars\n            if (item != entry) {\n              item.active = false;\n            }\n          });\n        };\n\n        /**\n         * @deprecated\n         */\n        $scope.addSearchApp = function () {\n          let findApp = false;\n          for (let i = 0; i < $scope.apps.length; i++) {\n            if ($scope.apps[i].app === $scope.searchApp) {\n              findApp = true;\n              break;\n            }\n          }\n          if (!findApp) {\n            $scope.apps.push({ app: $scope.searchApp });\n          }\n        };\n      }\n    };\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/filters/filters.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.filter('range', [function () {\n  return function (input, length) {\n    if (isNaN(length) || length <= 0) {\n      return [];\n    }\n\n    input = [];\n    for (var index = 1; index <= length; index++) {\n      input.push(index);\n    }\n\n    return input;\n  };\n  \n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/libs/treeTable.js",
    "content": "var com_github_culmat_jsTreeTable =  (function(){\n\n\tfunction depthFirst(tree, func, childrenAttr) {\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\tfunction i_depthFirst(node) {\n\t\t\tif (node[childrenAttr]) {\n\t\t\t\t$.each(node[childrenAttr], function(i, child) {\n\t\t\t\t\ti_depthFirst(child)\n\t\t\t\t})\n\t\t\t}\n\t\t\tfunc(node)\n\t\t}\n\t\t$.each(tree, function(i, root) {\n\t\t\ti_depthFirst(root)\n\t\t})\n\t\treturn tree\n\t}\n\t\n\t/*\n\t * make a deep copy of the object\n\t */\n\tfunction copy(data){\n\t\treturn JSON.parse(JSON.stringify(data))\n\t}\n\t\n\tfunction makeTree (data, idAttr, refAttr, childrenAttr) {\n\t\tvar data_tmp = data\n\t\tidAttr = idAttr || 'id'\n\t\trefAttr = refAttr || 'parent'\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\n\t\tvar byName = []\n\t\t$.each(data_tmp, function(i, entry) {\n\t\t\tbyName[entry[idAttr]] = entry\n\t\t})\n\t\tvar tree = []\n\t\t$.each(data_tmp, function(i, entry) {\n\t\t\tvar parents = entry[refAttr]\n\t\t\tif(!$.isArray(parents)){\n\t\t\t\tparents = [parents]\n\t\t\t}\n\t\t\tif(parents.length == 0){\n\t\t\t\ttree.push(entry)\n\t\t\t} else {\n\t\t\t\tvar inTree = false;\n\t\t\t\t$.each(parents, function(i,parentID){\n\t\t\t\t\tvar parent = byName[parentID]\n\t\t\t\t\tif (parent) {\n\t\t\t\t\t\tif (!parent[childrenAttr]) {\n\t\t\t\t\t\t\tparent[childrenAttr] = []\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif($.inArray(entry, parent[childrenAttr])< 0)\n\t\t\t\t\t\t\tparent[childrenAttr].push(entry)\n\t\t\t\t\t\tinTree = true\n\t\t\t\t\t} \n\t\t\t\t})\n\t\t\t\tif(!inTree){\n\t\t\t\t\ttree.push(entry)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\treturn tree\n\t}\n\t\n\tfunction renderTree(tree, childrenAttr, idAttr, attrs, renderer, tableAttributes) {\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\tidAttr = idAttr || 'id'\n\t\ttableAttributes = tableAttributes || {}\n\t\tvar maxLevel = 0;\n\t\tvar ret = []\n\t\n\t\tvar table = $(\"<table>\")\n\t\t$.each(tableAttributes, function(key, value){\n\t\t\tif(key == 'class' && value != 'jsTT') {\n\t\t\t\ttable.addClass(value)\n\t\t\t} else {\n\t\t\t\ttable.attr(key, value)\t\t\t\n\t\t\t}\n\t\t})\n\t\tvar thead = $(\"<thead>\")\n\t\tvar tr = $(\"<tr>\")\n\t\tvar tbody = $(\"<tbody>\")\n\t\n\t\ttable.append(thead)\n\t\tthead.append(tr)\n\t\ttable.append(tbody)\n\t\tif (attrs) {\n\t\t\t$.each(attrs, function(attr, desc) {\n\t\t\t\t$(tr).append($('<th>' + desc + '</th>'))\n\t\t\t})\n\t\t} else {\n\t\t\t$(tr).append($('<th>' + idAttr + '</th>'))\n\t\t\t$.each(tree[0], function(key, value) {\n\t\t\t\tif (key != childrenAttr && key != idAttr)\n\t\t\t\t\t$(tr).append($('<th>' + key + '</th>'))\n\t\t\t})\n\t\t}\n\t\n\t\tfunction render(node, parent) {\n\t\t\tvar tr = $(\"<tr>\")\n\t\t\t$(tr).attr('data-tt-id', node[idAttr])\n\t\t\t$(tr).attr('data-tt-level', node['data-tt-level'])\n\t\t\tif(!node[childrenAttr] || node[childrenAttr].length == 0)\n\t\t\t\t$(tr).attr('data-tt-isleaf', true)\n\t\t\telse\n\t\t\t\t$(tr).attr('data-tt-isnode', true)\n\t\t\tif (parent) {\n\t\t\t\t$(tr).attr('data-tt-parent-id', parent[idAttr])\n\t\t\t}\n\t\t\tif (renderer) {\n\t\t\t\trenderer($(tr), node)\n\t\t\t}else if (attrs) {\n\t\t\t\t$.each(attrs, function(attr, desc) {\n\t\t\t\t\t$(tr).append($('<td>' + node[attr] + '</td>'))\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\t$(tr).append($('<td>' + node[idAttr] + '</td>'))\n\t\t\t\t$.each(node, function(key, value) {\n\t\t\t\t\tif (key != childrenAttr && key != idAttr && key != 'data-tt-level')\n\t\t\t\t\t\t$(tr).append($('<td>' + value + '</td>'))\n\t\t\t\t})\n\t\t\t}\n\t\t\ttbody.append(tr)\n\t\t}\n\t\n\t\tfunction i_renderTree(subTree, childrenAttr, level, parent) {\n\t\t\tmaxLevel = Math.max(maxLevel, level)\n\t\t\t$.each(subTree, function(i, node) {\n\t\t\t\tnode['data-tt-level'] = level\n\t\t\t\trender(node, parent)\n\t\t\t\tif (node[childrenAttr]) {\n\t\t\t\t\t$.each(node[childrenAttr], function(i, child) {\n\t\t\t\t\t\ti_renderTree([ child ], childrenAttr, level + 1, node)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\ti_renderTree(tree, childrenAttr, 1)\n\t\tif (tree[0])\n\t\t\ttree[0].maxLevel = maxLevel\n\t\treturn table\n\t}\n\t\n\tfunction attr2attr(nodes, attrs){\n\t\t$.each(nodes,  function(i, node) {\n\t\t\t$.each(attrs,  function(j, at) {\n\t\t\t\tnode[at] = $(node).attr(at)\n\t\t\t})\t\n\t\t})\n\t\treturn nodes\n\t}\n\t\n\tfunction treeTable(table){\n\t\ttable.addClass('jsTT')\n\t\ttable.expandLevel = function (n) {\n\t\t\t$(\"tr[data-tt-level]\", table).each(function(index) {\n\t\t\t\tvar level = parseInt($(this).attr('data-tt-level'))\n\t\t\t\tif (level > n-1) {\n\t\t\t\t\tthis.trCollapse(true)\n\t\t\t\t} else if (level == n-1){\n\t\t\t\t\tthis.trExpand(true)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tfunction getLevel(node){\n\t\t\tvar level = node.attr('data-tt-level')\n\t\t\tif(level != undefined ) return parseInt(level)\n\t\t\tvar parentID = node.attr('data-tt-parent-id')\n\t\t\tif( parentID == undefined){\n\t\t\t\treturn 0\n\t\t\t} else {\n\t\t\t\treturn getLevel($('tr[data-tt-id=\"'+parentID+'\"]', table).first()) + 1\n\t\t\t} \n\t\t}\n\t\t$(\"tr[data-tt-id]\", table).each(function(i,node){\n\t\t\tnode = $(node)\n\t\t\tnode.attr('data-tt-level', getLevel(node)) \n\t\t})\n\t\tvar dat = $(\"tr[data-tt-level]\", table).get()\n\t\t$.each(dat,  function(j, d) {\n\t\t\td.trChildrenVisible = true\n\t\t\td.trChildren = []\n\t\t})\t\n\t\tdat  = attr2attr(dat, ['data-tt-id', 'data-tt-parent-id'])\n\t\tdat = makeTree(dat, 'data-tt-id', 'data-tt-parent-id', 'trChildren')\n\t\t\n\t\tvar imgExpand = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=\"\n\t\tvar imgCollapse = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC\"\n\t\t$(\"tr[data-tt-level]\", table).each(function(index, tr) {\n\t\t\tvar level = $(tr).attr('data-tt-level')\n\t\t\tvar td = $(\"td\",tr).first()\n\t\t\tif(tr.trChildren.length>0){\n\t\t\t\ttd.prepend($('<img id=\"state\" style=\"cursor:pointer\" src=\"'+imgCollapse+'\"/>'))\t\t\t\n\t\t\t}  else {\n\t\t\t\ttd.prepend($('<span style=\"padding-left:16px;\" /></span>'))\n\t\t\t}\n\t\t\ttd.prepend($('<span style=\"padding-left:'+(15*parseInt(level-1))+'px;\" /></span>'))\n\t\t\t// td.css('white-space','nowrap')\n\t\t\ttr.trExpand = function(changeState){\n\t\t\t\tif(this.trChildren.length < 1) return\n\t\t\t\tif(changeState) {\n\t\t\t\t\tthis.trChildrenVisible = true\n\t\t\t\t\t$('#state', this).get(0).src= imgCollapse\n\t\t\t\t} \n\t\t\t\tvar doit = changeState || this.trChildrenVisible\n\t\t\t\t$.each(this.trChildren, function(i, ctr) {\n\t\t\t\t\tif(doit) $(ctr).css('display', 'table-row')\n\t\t\t\t\tctr.trExpand()\n\t\t\t\t})\n\t\t\t}\n\t\t\ttr.trCollapse = function(changeState){\n\t\t\t\tif(this.trChildren.length < 1) return\n\t\t\t\tif(changeState) {\n\t\t\t\t\tthis.trChildrenVisible = false\n\t\t\t\t\t$('#state', this).get(0).src= imgExpand\n\t\t\t\t}\n\t\t\t\t$.each(this.trChildren, function(i, ctr) {\n\t\t\t\t\t$(ctr).css('display', 'none')\n\t\t\t\t\tctr.trCollapse()\n\t\t\t\t})\n\t\t\t}\n\t\t\t$(tr).click(function() {\n\t\t\t\tthis.trChildrenVisible ? this.trCollapse(true) : this.trExpand(true)\n\t\t\t})\n\t\t})\n\t\treturn table\n\t}\n\t\n\tfunction appendTreetable(tree, options) {\n\t\tfunction inALine(nodes) {\n\t\t\tvar tr = $('<tr>')\n\t\t\t$.each(nodes, function(i, node){\n\t\t\t\ttr.append($('<td style=\"padding-right: 20px;\">').append(node))\n\t\t\t})\n\t\t\treturn $('<table border=\"0\"/>').append(tr)\n\t\t\t\n\t\t}\n\t\toptions = options || {}\n\t\toptions.idAttr = (options.idAttr || 'id')\n\t\toptions.childrenAttr = (options.childrenAttr || 'children')\n\t\tvar controls = (options.controls || [])\n\t\n\t\tif (!options.mountPoint)\n\t\t\toptions.mountPoint = $('body')\n\t\t\n\t\tif (options.depthFirst)\n\t\t\tdepthFirst(tree, options.depthFirst, options.childrenAttr)\n\t\tvar rendered = renderTree(tree, options.childrenAttr, options.idAttr,\n\t\t\t\toptions.renderedAttr, options.renderer, options.tableAttributes)\n\t\n\t\ttreeTable(rendered)\n\t\tif (options.replaceContent) {\n\t\t\toptions.mountPoint.html('')\n\t\t}\n\t\tvar initialExpandLevel = options.initialExpandLevel ? parseInt(options.initialExpandLevel) : -1\n\t\tinitialExpandLevel = Math.min(initialExpandLevel, tree[0].maxLevel)\n\t\trendered.expandLevel(initialExpandLevel)\n\t\tif(options.slider){\n\t\t\tvar slider = $('<div style=\"margin-right: 15px;\">')\n\t\t\tslider.width('200px')\n\t\t\tslider.slider({\n\t\t\t\tmin : 1,\n\t\t\t\tmax : tree[0].maxLevel,\n\t\t\t\trange : \"min\",\n\t\t\t\tvalue : initialExpandLevel,\n\t\t\t\tslide : function(event, ui) {\n\t\t\t\t\trendered.expandLevel(ui.value)\n\t\t\t\t}\n\t\t\t})\n\t\t\tcontrols = [slider].concat(options.controls)\n\t\t}\n\t\t\n\t    if(controls.length >0){\n\t    \toptions.mountPoint.append(inALine(controls))    \t\n\t    }\n\t\toptions.mountPoint.append(rendered)\n\t\treturn rendered\n\t}\n\t\n\treturn {\n\t\tdepthFirst : depthFirst,\n\t\tmakeTree : makeTree,\n\t\trenderTree : renderTree,\n\t\tattr2attr : attr2attr,\n\t\ttreeTable : treeTable,\n\t\tappendTreetable : appendTreetable,\n\t\tjsTreeTable : '1.0',\n\t\tregister : function(target){\n\t\t\t$.each(this, function(key, value){ if(key != 'register') target[key] = value})\n\t\t}\n\t}\n})();\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/appservice.js",
    "content": "\nvar app = angular.module('sentinelDashboardApp');\n\napp.service('AppService', ['$http', function ($http) {\n  this.getApps = function () {\n    return $http({\n      // url: 'app/mock_infos',\n      url: 'app/briefinfos.json',\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/auth_service.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('AuthService', ['$http', function ($http) {\n  this.login = function (param) {\n    return $http({\n      url: '/auth/login',\n      params: param,\n      method: 'POST'\n    })\n  }\n\n  this.logout = function () {\n    return $http({\n      url: '/auth/logout',\n      method: 'POST'\n    })\n  }\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/authority_service.js",
    "content": "/**\n * Authority rule service.\n */\nangular.module('sentinelDashboardApp').service('AuthorityRuleService', ['$http', function ($http) {\n    this.queryMachineRules = function(app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/authority/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.addNewRule = function(rule) {\n        return $http({\n            url: '/authority/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (entity) {\n        return $http({\n            url: '/authority/rule/' + entity.id,\n            data: entity,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (entity) {\n        return $http({\n            url: '/authority/rule/' + entity.id,\n            method: 'DELETE'\n        });\n    };\n\n    this.checkRuleValid = function checkRuleValid(rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.limitApp === undefined || rule.limitApp === '') {\n            alert('流控针对应用不能为空');\n            return false;\n        }\n        if (rule.strategy === undefined) {\n            alert('必须选择黑白名单模式');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/cluster_state_service.js",
    "content": "/**\n * Cluster state control service.\n *\n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) {\n\n    this.fetchClusterUniversalStateSingle = function(app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/cluster/state_single',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterUniversalStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterServerStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/server_state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterClientStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/client_state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.modifyClusterConfig = function(config) {\n        return $http({\n            url: '/cluster/config/modify_single',\n            data: config,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterFullAssignOfApp = function(app, clusterMap) {\n        return $http({\n            url: '/cluster/assign/all_server/' + app,\n            data: clusterMap,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterSingleServerAssignOfApp = function(app, request) {\n        return $http({\n            url: '/cluster/assign/single_server/' + app,\n            data: request,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterServerBatchUnbind = function(app, machineSet) {\n        return $http({\n            url: '/cluster/assign/unbind_server/' + app,\n            data: machineSet,\n            method: 'POST'\n        });\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/degradeservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('DegradeService', ['$http', function ($http) {\n  this.queryMachineRules = function (app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: 'degrade/rules.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.newRule = function (rule) {\n    var param = {\n      id: rule.id,\n      resource: rule.resource,\n      limitApp: rule.limitApp,\n      count: rule.count,\n      timeWindow: rule.timeWindow,\n      grade: rule.grade,\n      app: rule.app,\n      ip: rule.ip,\n      port: rule.port\n    };\n    return $http({\n      url: '/degrade/new.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.saveRule = function (rule) {\n    var param = {\n      id: rule.id,\n      resource: rule.resource,\n      limitApp: rule.limitApp,\n      grade: rule.grade,\n      count: rule.count,\n      timeWindow: rule.timeWindow,\n    };\n    return $http({\n      url: '/degrade/save.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.deleteRule = function (rule) {\n    var param = {\n      id: rule.id,\n      app: rule.app\n    };\n    return $http({\n      url: '/degrade/delete.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.checkRuleValid = function (rule) {\n      if (rule.resource === undefined || rule.resource === '') {\n          alert('资源名称不能为空');\n          return false;\n      }\n      if (rule.grade === undefined || rule.grade < 0) {\n          alert('未知的降级策略');\n          return false;\n      }\n      if (rule.count === undefined || rule.count === '' || rule.count < 0) {\n          alert('降级阈值不能为空或小于 0');\n          return false;\n      }\n      if (rule.timeWindow === undefined || rule.timeWindow === '' || rule.timeWindow <= 0) {\n          alert('降级时间窗口必须大于 0');\n          return false;\n      }\n      // 异常比率类型.\n      if (rule.grade == 1 && rule.count > 1) {\n          alert('异常比率超出范围：[0.0 - 1.0]');\n          return false;\n      }\n      return true;\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/flow_service_v1.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('FlowServiceV1', ['$http', function ($http) {\n    this.queryMachineRules = function (app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/v1/flow/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.newRule = function (rule) {\n        var param = {\n            resource: rule.resource,\n            limitApp: rule.limitApp,\n            grade: rule.grade,\n            count: rule.count,\n            strategy: rule.strategy,\n            refResource: rule.refResource,\n            controlBehavior: rule.controlBehavior,\n            warmUpPeriodSec: rule.warmUpPeriodSec,\n            maxQueueingTimeMs: rule.maxQueueingTimeMs,\n            app: rule.app,\n            ip: rule.ip,\n            port: rule.port\n        };\n\n        return $http({\n            url: '/v1/flow/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (rule) {\n        var param = {\n            id: rule.id,\n            resource: rule.resource,\n            limitApp: rule.limitApp,\n            grade: rule.grade,\n            count: rule.count,\n            strategy: rule.strategy,\n            refResource: rule.refResource,\n            controlBehavior: rule.controlBehavior,\n            warmUpPeriodSec: rule.warmUpPeriodSec,\n            maxQueueingTimeMs: rule.maxQueueingTimeMs,\n        };\n\n        return $http({\n            url: '/v1/flow/save.json',\n            params: param,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (rule) {\n        var param = {\n            id: rule.id,\n            app: rule.app\n        };\n\n        return $http({\n            url: '/v1/flow/delete.json',\n            params: param,\n            method: 'DELETE'\n        });\n    };\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notNumberGreaterThanZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num <= 0;\n    }\n\n    this.checkRuleValid = function (rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.count === undefined || rule.count < 0) {\n            alert('限流阈值必须大于等于 0');\n            return false;\n        }\n        if (rule.strategy === undefined || rule.strategy < 0) {\n            alert('无效的流控模式');\n            return false;\n        }\n        if (rule.strategy == 1 || rule.strategy == 2) {\n            if (rule.refResource === undefined || rule.refResource == '') {\n                alert('请填写关联资源或入口');\n                return false;\n            }\n        }\n        if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {\n            alert('无效的流控整形方式');\n            return false;\n        }\n        if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {\n            alert('预热时长必须大于 0');\n            return false;\n        }\n        if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {\n            alert('排队超时时间必须大于 0');\n            return false;\n        }\n        if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {\n            alert('集群限流配置不正确');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/flow_service_v2.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('FlowServiceV2', ['$http', function ($http) {\n    this.queryMachineRules = function (app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/v2/flow/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.newRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule/' + rule.id,\n            data: rule,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule/' + rule.id,\n            method: 'DELETE'\n        });\n    };\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notNumberGreaterThanZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num <= 0;\n    }\n\n    this.checkRuleValid = function (rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.count === undefined || rule.count < 0) {\n            alert('限流阈值必须大于等于 0');\n            return false;\n        }\n        if (rule.strategy === undefined || rule.strategy < 0) {\n            alert('无效的流控模式');\n            return false;\n        }\n        if (rule.strategy == 1 || rule.strategy == 2) {\n            if (rule.refResource === undefined || rule.refResource == '') {\n                alert('请填写关联资源或入口');\n                return false;\n            }\n        }\n        if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {\n            alert('无效的流控整形方式');\n            return false;\n        }\n        if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {\n            alert('预热时长必须大于 0');\n            return false;\n        }\n        if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {\n            alert('排队超时时间必须大于 0');\n            return false;\n        }\n        if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {\n            alert('集群限流配置不正确');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/identityservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('IdentityService', ['$http', function ($http) {\n\n  this.fetchIdentityOfMachine = function (ip, port, searchKey) {\n    var param = {\n      ip: ip,\n      port: port,\n      searchKey: searchKey\n    };\n    return $http({\n      url: 'resource/machineResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n  this.fetchClusterNodeOfMachine = function (ip, port, searchKey) {\n    var param = {\n      ip: ip,\n      port: port,\n      type: 'cluster',\n      searchKey: searchKey\n    };\n    return $http({\n      url: 'resource/machineResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/machineservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) {\n  this.getAppMachines = function (app) {\n    return $http({\n      url: 'app/' + app + '/machines.json',\n      method: 'GET'\n    });\n  };\n  this.removeAppMachine = function (app, ip, port) {\n    return $http({\n      url: 'app/' + app + '/machine/remove.json',\n      method: 'POST',\n      headers: {\n        \"Content-type\": 'application/x-www-form-urlencoded; charset=UTF-8'\n      },\n      data: $httpParamSerializerJQLike({\n        ip: ip,\n        port: port\n      })\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/metricservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('MetricService', ['$http', function ($http) {\n\n  this.queryAppSortedIdentities = function (params) {\n    return $http({\n      url: '/metric/queryTopResourceMetric.json',\n      params: params,\n      method: 'GET'\n    });\n  };\n\n  this.queryByAppAndIdentity = function (params) {\n    return $http({\n      url: '/metric/queryByAppAndResource.json',\n      params: params,\n      method: 'GET'\n    });\n  };\n\n  this.queryByMachineAndIdentity = function (ip, port, identity, startTime, endTime) {\n    var param = {\n      ip: ip,\n      port: port,\n      identity: identity,\n      startTime: startTime.getTime(),\n      endTime: endTime.getTime()\n    };\n\n    return $http({\n      url: '/metric/queryByAppAndResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/param_flow_service.js",
    "content": "/**\n * Parameter flow control service.\n * \n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) {\n  this.queryMachineRules = function(app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: '/paramFlow/rules',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.addNewRule = function(rule) {\n    return $http({\n      url: '/paramFlow/rule',\n      data: rule,\n      method: 'POST'\n    });\n  };\n\n  this.saveRule = function (entity) {\n    return $http({\n      url: '/paramFlow/rule/' + entity.id,\n      data: entity,\n      method: 'PUT'\n    });\n  };\n\n  this.deleteRule = function (entity) {\n    return $http({\n      url: '/paramFlow/rule/' + entity.id,\n      method: 'DELETE'\n    });\n  };\n\n    function isNumberClass(classType) {\n        return classType === 'int' || classType === 'double' ||\n            classType === 'float' || classType === 'long' || classType === 'short';\n    }\n\n    function isByteClass(classType) {\n        return classType === 'byte';\n    }\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notGoodNumber(num) {\n        return num === undefined || num === '' || isNaN(num);\n    }\n\n    function notGoodNumberBetweenExclusive(num, l ,r) {\n        return num === undefined || num === '' || isNaN(num) || num < l || num > r;\n    }\n\n    function notValidParamItem(curExItem) {\n        if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {\n            return true;\n        }\n        if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {\n            return true;\n        }\n        return curExItem.object === undefined || curExItem.classType === undefined ||\n            notNumberAtLeastZero(curExItem.count);\n    }\n\n  this.checkRuleValid = function (rule) {\n      if (!rule.resource || rule.resource === '') {\n          alert('资源名称不能为空');\n          return false;\n      }\n      if (rule.grade != 1) {\n          alert('未知的限流模式');\n          return false;\n      }\n      if (rule.count < 0) {\n          alert('限流阈值必须大于等于 0');\n          return false;\n      }\n      if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) {\n          alert('热点参数索引必须大于等于 0');\n          return false;\n      }\n      if (rule.paramFlowItemList !== undefined) {\n          for (var i = 0; i < rule.paramFlowItemList.length; i++) {\n              var item = rule.paramFlowItemList[i];\n              if (notValidParamItem(item)) {\n                  alert('热点参数例外项不合法，请检查值和类型是否正确：参数为 ' + item.object + ', 类型为 ' +\n                      item.classType + ', 限流阈值为 ' + item.count);\n                  return false;\n              }\n          }\n      }\n      return true;\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/systemservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('SystemService', ['$http', function ($http) {\n  this.queryMachineRules = function (app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: 'system/rules.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.newRule = function (rule) {\n    var param = {\n      app: rule.app,\n      ip: rule.ip,\n      port: rule.port\n    };\n    if (rule.grade == 0) {// avgLoad\n      param.avgLoad = rule.avgLoad;\n    } else if (rule.grade == 1) {// avgRt\n      param.avgRt = rule.avgRt;\n    } else if (rule.grade == 2) {// maxThread\n      param.maxThread = rule.maxThread;\n    } else if (rule.grade == 3) {// qps\n      param.qps = rule.qps;\n    }\n\n    return $http({\n      url: '/system/new.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.saveRule = function (rule) {\n    var param = {\n      id: rule.id,\n    };\n    if (rule.grade == 0) {// avgLoad\n      param.avgLoad = rule.avgLoad;\n    } else if (rule.grade == 1) {// avgRt\n      param.avgRt = rule.avgRt;\n    } else if (rule.grade == 2) {// maxThread\n      param.maxThread = rule.maxThread;\n    } else if (rule.grade == 3) {// qps\n      param.qps = rule.qps;\n    }\n    return $http({\n      url: '/system/save.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.deleteRule = function (rule) {\n    var param = {\n      id: rule.id,\n      app: rule.app\n    };\n\n    return $http({\n      url: '/system/delete.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/main.css",
    "content": ".browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\nbody {\n    padding: 0;\n}\n\n/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.header,\n.marketing,\n.footer {\n    padding-left: 15px;\n    padding-right: 15px;\n}\n\n\n/* Custom page header */\n\n.header {\n    border-bottom: 1px solid #e5e5e5;\n    margin-bottom: 10px;\n}\n\n\n/* Make the masthead heading the same height as the navigation */\n\n.header h3 {\n    margin-top: 0;\n    margin-bottom: 0;\n    line-height: 40px;\n    padding-bottom: 19px;\n}\n\n\n/* Custom page footer */\n\n.footer {\n    padding-top: 19px;\n    color: #777;\n    border-top: 1px solid #e5e5e5;\n}\n\n.container-narrow > hr {\n    margin: 30px 0;\n}\n\n\n/* Main marketing message and sign up button */\n\n.jumbotron {\n    text-align: center;\n    border-bottom: 1px solid #e5e5e5;\n}\n\n.jumbotron .btn {\n    font-size: 21px;\n    padding: 14px 24px;\n}\n\n\n/* Supporting marketing content */\n\n.marketing {\n    margin: 40px 0;\n}\n\n.marketing p + h4 {\n    margin-top: 28px;\n}\n\n\n/* Responsive: Portrait tablets and up */\n\n@media screen and (min-width: 768px) {\n    .container {\n        width: inherit;\n        margin-left: 60px;\n        margin-right: 5px;\n    }\n    /* Remove the padding we set earlier */\n    .header,\n    .marketing,\n    .footer {\n        padding-left: 0;\n        padding-right: 0;\n    }\n    /* Space out the masthead */\n    .header {\n        margin-bottom: 30px;\n    }\n    /* Remove the bottom border on the jumbotron for visual effect */\n    .jumbotron {\n        border-bottom: 0;\n    }\n}\n\n.navbar-inverse {\n    background-color: #1d9d74;\n    border-color: #1b926c;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n    color: #b0ddce;\n    font-size: 15px;\n}\n\n.navbar-inverse .navbar-nav>.open>a,\n.navbar-inverse .navbar-nav>.open>a:focus,\n.navbar-inverse .navbar-nav>.open>a:hover {\n    background-color: #1b926c;\n}\n\n@media (min-width: 900px) {\n    .navbar-left {\n        float: left !important;\n    }\n    .navbar-right {\n        float: right !important;\n        margin-right: 0%;\n    }\n    .navbar-right ~ .navbar-right {\n        margin-right: 0;\n    }\n}\n\n.dropdown-menu {\n    min-width: 100px !important;\n}\n\n.nav-sidebar li.active a {\n    background: #DDD;\n}\n\n.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {\n    background: #1d9d74;\n    /*background: #d9d9d9;*/\n    color: white;\n}\n\n.broadcast-message,\n.broadcast-message-preview {\n    padding: 10px;\n    text-align: center;\n    background: #555;\n    color: #BBB;\n    margin-top: 50px;\n}\n\n.card {\n    position: relative;\n    border: 1px solid #d9d9d9;\n    border-radius: 3px;\n    color: #666;\n    background-color: #fff;\n    width: 100%;\n    border-radius: 5px;\n}\n\n.card .card-header {\n    padding: 9px 0;\n    height: 40px;\n    background: #555;\n    color: #fff;\n    text-align: center;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.card .card-body {\n    padding: 12px 10px;\n}\n\n.card .card-footer {\n    height: 20px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .detail-brand {\n    float: left;\n    width: 30%;\n    line-height: 98px;\n    font-size: 30px;\n    text-align: center;\n    color: white;\n}\n\n.card .default {\n    background: #1d9d74;\n}\n\n.card .info {\n    background: #6EBEE7;\n}\n\n.card .warn {\n    background: #ED7F54;\n}\n\n.card .danger {\n    background: #6583BE;\n}\n\n.card .detail .text-default {\n    color: #1d9d74;\n}\n\n.card .detail .text-info {\n    color: #6EBEE7;\n}\n\n.card .detail .text-warn {\n    color: #ED7F54;\n}\n\n.card .detail .text-danger {\n    color: #6583BE;\n}\n\n.card .detail {\n    float: right;\n    width: 70%;\n    line-height: 98px;\n    text-align: center;\n}\n\n.card .detail .text {\n    font-size: 12px;\n}\n\n.card .detail .number {\n    font-size: 30px;\n    font-weight: 500;\n}\n\n.h100 {\n    height: 100px;\n}\n\n.inline {\n    display: inline;\n}\n\n.separator {\n    height: 1px;\n    background-color: #e5e5e5;\n    margin-top: 10px;\n}\n\n.card > .card-body > table > thead > tr > td,\n.card > .card-body > table > tbody > tr > td {\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.card > .card-body > table > thead > tr > td {\n    font-weight: 500;\n    font-size: 13px;\n    text-align: center;\n}\n\n.card > .card-body > table > thead > tr > td > span {\n    font-weight: 500;\n    font-size: 10px;\n}\n\n.card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    text-align: center;\n}\n\n.card > .card-body > table > tbody > tr > td > a {\n    color: #666;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td,\n.thumbnails > .card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    color: #777;\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) {\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) {\n    font-weight: 700;\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1),\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) {\n    text-align: left;\n}\n\n.tools-header {\n    background: whitesmoke;\n    padding: 9px 0;\n    height: 40px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.tools-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.tools-header .brand > a {\n    color: #666;\n}\n\n.tools-header > button,\n.tools-header > select,\n.tools-header > a {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 25px;\n    padding: 0 10px;\n    line-height: 25px;\n    color: #666;\n}\n\n.tools-header .paged {\n    margin-right: 0px;\n}\n\n.btn {\n    height: 32px;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn:focus,\n.btn:active {\n    outline: none !important;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n\n.btn.btn-danger-tag {\n    color: #ffffff;\n    background-color: #d9534f;\n    border-color: #d43f3a;\n    line-height: 1px;\n    font-size: 11px;\n    padding: 4px 4px;\n}\n\n.btn.btn-danger {\n    color: #333;\n    background-color: #fff;\n    border-color: #ccc;\n}\n\n.btn.btn-danger:hover,\n.btn.btn-danger:focus,\n.btn.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.form-control {\n    height : 32px;\n}\n\n.form-control:focus {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.input-label:before {\n    display: inline-block;\n    content: \"*\";\n    color: #f44336;\n    font-family: SimSun;\n    font-size: 12px;\n    -webkit-transform: TranslateX(-10px);\n    -ms-transform: TranslateX(-10px);\n    transform: TranslateX(-10px);\n}\n\n.label.label-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.badge-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.bootstrap-tagsinput {\n    background-color: #fff;\n    border: 1px solid #ccc;\n    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n    display: inline-block;\n    padding: 4px 6px;\n    color: #555;\n    vertical-align: middle;\n    border-radius: 4px;\n    /* max-width: 100%; */\n    width: 85%;\n    height: 100px;\n    line-height: 20px;\n    cursor: text;\n}\n\n.bootstrap-tagsinput > .dropdown-menu {\n    min-width: 40px;\n    font-size: 12px;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    background-color: #1d9d74;\n    background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74));\n    background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%);\n    filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);\n    background-repeat: repeat-x;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    color: #fff;\n    text-decoration: none;\n    background-color: #1d9d74;\n    outline: 0;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus {\n    color: white;\n    text-decoration: none;\n    outline: 0;\n    background-color: #1d9d74;\n}\n\n.inputs-header {\n    padding: 9px 0;\n    height: 50px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.inputs-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.inputs-header .brand > a {\n    color: #666;\n}\n\n.inputs-header > input {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n}\n\n.inputs-header > a {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 5 5px;\n}\n\n.inputs-header > select {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n    height: 25px;\n    font-size: 12px;\n}\n\n.witdh-150 {\n    max-width: 150px;\n}\n\n.witdh-200 {\n    max-width: 200px;\n}\n\n.width-200 {\n    max-width: 200px;\n}\n\n.witdh-300 {\n    max-width: 300px;\n}\n\n.width-300 {\n    max-width: 300px;\n}\n\n.card.highlight {\n    border-color: #d9534f;\n}\n\n.card .pagination-footer {\n    height: 40px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .pagination-footer .tools {\n    font-size: 12px;\n    margin: 11px 0;\n    float: right;\n    display: inline;\n    margin-right: 20px;\n}\n\n.card > .pagination-footer > .tools > span > input {\n    height: 25px;\n    max-width: 50px;\n    display: inline;\n}\n\n.pagination {\n    display: inline-block;\n    padding-left: 0;\n    margin: 8px 0;\n    float: right;\n    border-radius: 4px;\n}\n\n\n.pagination > a {\n    margin-right: 5px;\n    height: 28px;\n    width: 28px;\n    padding: 5px 0px;\n}\n\n.pagination > .btn.active {\n        color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n\n\n\n.datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td,\n.timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td   {\n    padding: 5px 3px;\n}\n\n.datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn,\n.timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn  {\n    border: 1px solid #FFFDFD;\n}\n\n.datepicker > .table > thead > tr > td > .btn-default:hover,\n.datepicker > .table > thead > tr > td > .btn-default:focus,\n.datepicker > .table > thead > tr > td > .btn-default:active,\n.datepicker > .table > tbody > tr > td > .btn-default:hover,\n.datepicker > .table > tbody > tr > td > .btn-default:focus,\n.datepicker > .table > tbody > tr > td > .btn-default:active,\n.timepicker > .table > thead > tr > td > .btn-default:hover,\n.timepicker > .table > thead > tr > td > .btn-default:focus,\n.timepicker > .table > thead > tr > td > .btn-default:active,\n.timepicker > .table > tbody > tr > td > .btn-default:hover,\n.timepicker > .table > tbody > tr > td > .btn-default:focus,\n.timepicker > .table > tbody > tr > td > .btn-default:active  {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a,\n.timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a {\n    height: 25px;\n    width: 25px;\n    padding: 3px 0px;\n}\n\n.datepicker > .table > tbody > tr:first-child > td > a {\n    padding: 4px 0px;\n}\n\n.datepicker > .table > thead > tr > td > a.btn.active, \n.datepicker > .table > tbody > tr > td > a.btn.active,\n.timepicker > .table > thead > tr > td > a.btn.active, \n.timepicker > .table > tbody > tr > td > a.btn.active {\n/*    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;*/\n        color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n    box-shadow: inset 0 0px 0px rgba(0,0,0,0.125);\n}\n\n.datepicker > .table > thead > tr > td:not(:first-child):last-child > a,\n.timepicker > .table > thead > tr > td:not(:first-child):last-child > a  {\n    height: 25px;\n    width: 50px;\n    padding: 5px 0px;\n}\n\n.datepicker > .table > tbody > tr > td > a, \n.timepicker > .table > tbody > tr > td > a {\n    margin-left: 8px;\n}\n\n\n.selectize-input-200 > .selectize-input {\n    min-width: 250px;\n}\n\n.highlight-border {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}.browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\nbody {\n    padding: 0;\n}\n\n\n/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.header,\n.marketing,\n.footer {\n    padding-left: 15px;\n    padding-right: 15px;\n}\n\n\n/* Custom page header */\n\n.header {\n    border-bottom: 1px solid #e5e5e5;\n    margin-bottom: 10px;\n}\n\n\n/* Make the masthead heading the same height as the navigation */\n\n.header h3 {\n    margin-top: 0;\n    margin-bottom: 0;\n    line-height: 40px;\n    padding-bottom: 19px;\n}\n\n\n/* Custom page footer */\n\n.footer {\n    padding-top: 19px;\n    color: #777;\n    border-top: 1px solid #e5e5e5;\n}\n\n.container-narrow > hr {\n    margin: 30px 0;\n}\n\n\n/* Main marketing message and sign up button */\n\n.jumbotron {\n    text-align: center;\n    border-bottom: 1px solid #e5e5e5;\n}\n\n.jumbotron .btn {\n    font-size: 21px;\n    padding: 14px 24px;\n}\n\n\n/* Supporting marketing content */\n\n.marketing {\n    margin: 40px 0;\n}\n\n.marketing p + h4 {\n    margin-top: 28px;\n}\n\n\n/* Responsive: Portrait tablets and up */\n\n@media screen and (min-width: 768px) {\n    .container {\n        width: inherit;\n        margin-left: 60px;\n        margin-right: 5px;\n    }\n    /* Remove the padding we set earlier */\n    .header,\n    .marketing,\n    .footer {\n        padding-left: 0;\n        padding-right: 0;\n    }\n    /* Space out the masthead */\n    .header {\n        margin-bottom: 30px;\n    }\n    /* Remove the bottom border on the jumbotron for visual effect */\n    .jumbotron {\n        border-bottom: 0;\n    }\n}\n\n.navbar-inverse {\n    background-color: #1d9d74;\n    border-color: #1b926c;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n    color: #b0ddce;\n    font-size: 15px;\n}\n\n.navbar-inverse .navbar-nav>.open>a,\n.navbar-inverse .navbar-nav>.open>a:focus,\n.navbar-inverse .navbar-nav>.open>a:hover {\n    background-color: #1b926c;\n}\n\n@media (min-width: 900px) {\n    .navbar-left {\n        float: left !important;\n    }\n    .navbar-right {\n        float: right !important;\n        margin-right: 0%;\n    }\n    .navbar-right ~ .navbar-right {\n        margin-right: 0;\n    }\n}\n\n.dropdown-menu {\n    min-width: 100px !important;\n}\n\n.nav-sidebar li.active a {\n    background: #DDD;\n}\n\n.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {\n    background: #1d9d74;\n    /*background: #d9d9d9;*/\n    color: white;\n}\n\n.broadcast-message,\n.broadcast-message-preview {\n    padding: 10px;\n    text-align: center;\n    background: #555;\n    color: #BBB;\n    margin-top: 50px;\n}\n\n.card {\n    position: relative;\n    border: 1px solid #d9d9d9;\n    border-radius: 3px;\n    color: #666;\n    background-color: #fff;\n    width: 100%;\n    border-radius: 5px;\n}\n\n.card .card-header {\n    padding: 9px 0;\n    height: 40px;\n    background: #555;\n    color: #fff;\n    text-align: center;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.card .card-body {\n    padding: 12px 10px;\n}\n\n.card .card-footer {\n    height: 20px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .detail-brand {\n    float: left;\n    width: 30%;\n    line-height: 98px;\n    font-size: 30px;\n    text-align: center;\n    color: white;\n}\n\n.card .default {\n    background: #1d9d74;\n}\n\n.card .info {\n    background: #6EBEE7;\n}\n\n.card .warn {\n    background: #ED7F54;\n}\n\n.card .danger {\n    background: #6583BE;\n}\n\n.card .detail .text-default {\n    color: #1d9d74;\n}\n\n.card .detail .text-info {\n    color: #6EBEE7;\n}\n\n.card .detail .text-warn {\n    color: #ED7F54;\n}\n\n.card .detail .text-danger {\n    color: #6583BE;\n}\n\n.card .detail {\n    float: right;\n    width: 70%;\n    line-height: 98px;\n    text-align: center;\n}\n\n.card .detail .text {\n    font-size: 12px;\n}\n\n.card .detail .number {\n    font-size: 30px;\n    font-weight: 500;\n}\n\n.h100 {\n    height: 100px;\n}\n\n.inline {\n    display: inline;\n}\n\n.separator {\n    height: 1px;\n    background-color: #e5e5e5;\n    margin-top: 10px;\n}\n\n.card > .card-body > table > thead > tr > td,\n.card > .card-body > table > tbody > tr > td {\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.card > .card-body > table > thead > tr > td {\n    font-weight: 500;\n    font-size: 13px;\n    text-align: center;\n}\n\n.card > .card-body > table > thead > tr > td > span {\n    font-weight: 500;\n    font-size: 10px;\n}\n\n.card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    text-align: center;\n}\n\n.card > .card-body > table > tbody > tr > td > a {\n    color: #666;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td,\n.thumbnails > .card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    color: #777;\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) {\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) {\n    font-weight: 700;\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1),\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) {\n    text-align: left;\n}\n\n.tools-header {\n    background: whitesmoke;\n    padding: 9px 0;\n    height: 40px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.tools-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.tools-header .brand > a {\n    color: #666;\n}\n\n.tools-header > button,\n.tools-header > select,\n.tools-header > a {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 25px;\n    padding: 0 10px;\n    line-height: 25px;\n    color: #666;\n}\n\n.tools-header .paged {\n    margin-right: 0px;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.btn:focus,\n.btn:active {\n    outline: none !important;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn-default-inverse {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn-default-inverse:hover,\n.btn-default-inverse:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn.btn-danger-tag {\n    color: #ffffff;\n    background-color: #d9534f;\n    border-color: #d43f3a;\n    line-height: 1px;\n    font-size: 11px;\n    padding: 4px 4px;\n}\n\n.btn.btn-danger {\n    color: #333;\n    background-color: #fff;\n    border-color: #ccc;\n}\n\n.btn.btn-danger:hover,\n.btn.btn-danger:focus,\n.btn.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.form-control {\n    height : 32px;\n}\n\n.form-control:focus {\n    border-color: #1d9d74;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.input-label:before {\n    display: inline-block;\n    content: \"*\";\n    color: #f44336;\n    font-family: SimSun;\n    font-size: 12px;\n    -webkit-transform: TranslateX(-10px);\n    -ms-transform: TranslateX(-10px);\n    transform: TranslateX(-10px);\n}\n\n.label.label-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.badge-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.bootstrap-tagsinput {\n    background-color: #fff;\n    border: 1px solid #ccc;\n    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n    display: inline-block;\n    padding: 4px 6px;\n    color: #555;\n    vertical-align: middle;\n    border-radius: 4px;\n    /* max-width: 100%; */\n    width: 85%;\n    height: 100px;\n    line-height: 20px;\n    cursor: text;\n}\n\n.bootstrap-tagsinput > .dropdown-menu {\n    min-width: 40px;\n    font-size: 12px;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    background-color: #1d9d74;\n    background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74));\n    background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%);\n    filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);\n    background-repeat: repeat-x;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    color: #fff;\n    text-decoration: none;\n    background-color: #1d9d74;\n    outline: 0;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus {\n    color: white;\n    text-decoration: none;\n    outline: 0;\n    background-color: #1d9d74;\n}\n\n.inputs-header {\n    padding: 9px 0;\n    height: 50px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.inputs-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.inputs-header .brand > a {\n    color: #666;\n}\n\n.inputs-header > input {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n}\n\n.inputs-header > a {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 5 5px;\n}\n\n.inputs-header > select {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n    height: 25px;\n    font-size: 12px;\n}\n\n.witdh-150 {\n    max-width: 150px;\n}\n\n.witdh-200 {\n    max-width: 200px;\n}\n\n.card.highlight {\n    border-color: #d9534f;\n}\n\n.card .pagination-footer {\n    height: 40px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .pagination-footer .tools {\n    font-size: 12px;\n    margin: 11px 0;\n    float: right;\n    display: inline;\n    margin-right: 20px;\n}\n\n.card > .pagination-footer > .tools > span > input {\n    height: 25px;\n    max-width: 50px;\n    display: inline;\n}\n\n.pagination {\n    display: inline-block;\n    padding-left: 0;\n    margin: 8px 0;\n    float: right;\n    border-radius: 4px;\n}\n\n\n.pagination > a {\n    margin-right: 5px;\n    height: 28px;\n    width: 28px;\n    padding: 5px 0px;\n}\n\n.pagination > .btn.active {\n        color: #ffffff;\n    background-color: #449d44;\n    border-color: #449d44;\n}\n\n\n\n\n.datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td,\n.timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td   {\n    padding: 5px 3px;\n}\n\n.datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn,\n.timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn  {\n    border: 1px solid #FFFDFD;\n}\n\n.datepicker > .table > thead > tr > td > .btn-default:hover,\n.datepicker > .table > thead > tr > td > .btn-default:focus,\n.datepicker > .table > thead > tr > td > .btn-default:active,\n.datepicker > .table > tbody > tr > td > .btn-default:hover,\n.datepicker > .table > tbody > tr > td > .btn-default:focus,\n.datepicker > .table > tbody > tr > td > .btn-default:active,\n.timepicker > .table > thead > tr > td > .btn-default:hover,\n.timepicker > .table > thead > tr > td > .btn-default:focus,\n.timepicker > .table > thead > tr > td > .btn-default:active,\n.timepicker > .table > tbody > tr > td > .btn-default:hover,\n.timepicker > .table > tbody > tr > td > .btn-default:focus,\n.timepicker > .table > tbody > tr > td > .btn-default:active  {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a,\n.timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a {\n    height: 25px;\n    width: 25px;\n    padding: 3px 0px;\n}\n\n.datepicker > .table > tbody > tr:first-child > td > a {\n    padding: 4px 0px;\n}\n\n.datepicker > .table > thead > tr > td > a.btn.active, \n.datepicker > .table > tbody > tr > td > a.btn.active,\n.timepicker > .table > thead > tr > td > a.btn.active, \n.timepicker > .table > tbody > tr > td > a.btn.active {\n/*    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;*/\n        color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n    box-shadow: inset 0 0px 0px rgba(0,0,0,0.125);\n}\n\n.datepicker > .table > thead > tr > td:not(:first-child):last-child > a,\n.timepicker > .table > thead > tr > td:not(:first-child):last-child > a  {\n    height: 25px;\n    width: 50px;\n    padding: 5px 0px;\n}\n\n.datepicker > .table > tbody > tr > td > a, \n.timepicker > .table > tbody > tr > td > a {\n    margin-left: 8px;\n}\n\n\n.selectize-input-200 > .selectize-input {\n    min-width: 250px;\n}\n\n.highlight-border {\n    border-color: #1d9d74;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n\n.sortorder:after {\n  content: '\\25b2';   \n}\n.sortorder.reverse:after {\n  content: '\\25bc';   \n}\n\n\n\n.input-control select {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  appearance: none;\n  position: relative;\n  border: 1px #d9d9d9 solid;\n  width: 100%;\n  height: 100%;\n  padding: .3125rem;\n  z-index: 0;\n}\n\n.navbar-inverse {\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.sidebar {\n    z-index: 1;\n    width: 220px;\n    /*position: fixed;*/\n    top: 0;\n    left: 0;\n    height: 100%;\n}\n\n#page-wrapper {\n    position: inherit;\n    margin: 70px 0 0 220px;\n    padding: 12px 30px;\n    border-left: 0px solid #e7e7e7;\n}\n\n.sidebar .sidebar-nav.navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n    background-color: #F5F5F5;\n    position: relative;\n    color: black;\n    width: 100%;\n    padding: 0;\n    margin: 0;\n    list-style: none inside none;\n}\n\n.sidebar a {\n    color: #555;\n}\n\n.sidebar ul li:hover {\n    color:red;\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.form-control:focus {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.highlight-border {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}.browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn-default-inverse {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.btn-default-inverse:hover,\n.btn-default-inverse:focus,\n.btn-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.btn-danger-inverse {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.btn-danger-inverse:hover,\n.btn-danger-inverse:focus,\n.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.btn-tab-active,\n.btn-tab-active:hover,\n.btn-tab-active:focus, \n.btn-tab-default:hover,\n.btn-tab-default:focus,\n.btn-tab-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n    font-weight: 600;\n}\n.btn-tab-default {\n    color: #777;\n    background: white;\n    font-weight: 600;\n}\n\n.pagination > .btn.active {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn-default:hover, .btn-default:focus, .btn-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.bootstrap-switch.bootstrap-switch-on {\n    border-color: #337ab7;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n    color: #fff;\n    background: #337ab7;\n}\n\n.selectize-input-200 > .selectize-input {\n    min-width: 200px;\n    border-color: #337ab7;\n}\n\n.btn-outline-primary {\n    color: #007bff;\n    background-color: transparent;\n    background-image: none;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n    color: #fff;\n    background-color: #007bff;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n    color: #007bff;\n    background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n    color: #fff;\n    background-color: #007bff;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n    color: #6c757d;\n    background-color: transparent;\n    background-image: none;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n    color: #fff;\n    background-color: #6c757d;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n    box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n    color: #6c757d;\n    background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n    color: #fff;\n    background-color: #6c757d;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n    color: #28a745;\n    background-color: transparent;\n    background-image: none;\n    border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n    color: #fff;\n    background-color: #28a745;\n    border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n    box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n    color: #28a745;\n    background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n    color: #fff;\n    background-color: #28a745;\n    border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n    color: #17a2b8;\n    background-color: transparent;\n    background-image: none;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n    color: #fff;\n    background-color: #17a2b8;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n    box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n    color: #17a2b8;\n    background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n    color: #fff;\n    background-color: #17a2b8;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n    color: #ffc107;\n    background-color: transparent;\n    background-image: none;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n    color: #212529;\n    background-color: #ffc107;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n    box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n    color: #ffc107;\n    background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n    color: #212529;\n    background-color: #ffc107;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n    color: #dc3545;\n    background-color: transparent;\n    background-image: none;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n    color: #fff;\n    background-color: #dc3545;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n    color: #dc3545;\n    background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n    color: #fff;\n    background-color: #dc3545;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n    color: #f8f9fa;\n    background-color: transparent;\n    background-image: none;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n    color: #212529;\n    background-color: #f8f9fa;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n    box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n    color: #f8f9fa;\n    background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n    color: #212529;\n    background-color: #f8f9fa;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n    color: #343a40;\n    background-color: transparent;\n    background-image: none;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n    color: #fff;\n    background-color: #343a40;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n    box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n    color: #343a40;\n    background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n    color: #fff;\n    background-color: #343a40;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/page.css",
    "content": "/*!\n * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)\n * Code licensed under the Apache License v2.0.\n * For details, see http://www.apache.org/licenses/LICENSE-2.0.\n */\n\nbody {\n    background-color: #f8f8f8;\n}\n\n.example {\n  padding: .625rem 1.825rem .625rem 2.5rem;\n  border: 1px #ccc dashed;\n  position: relative;\n  margin: 0 0 .625rem 0;\n  background-color: #ffffff;\n}\n\ndl dt,\ndl dd {\n  line-height: 1.25rem;\n}\ndl dt {\n  font-style: normal;\n  font-weight: 700;\n}\ndl dd {\n  margin-left: .9375rem;\n}\ndl.horizontal dt {\n  float: left;\n  width: 10rem;\n  overflow: hidden;\n  clear: left;\n  text-align: right;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\ndl.horizontal dd {\n  margin-left: 11.25rem;\n}\n\n#wrapper {\n    width: 100%;\n}\n\n#page-wrapper {\n    padding: 0 15px;\n    min-height: 568px;\n    background-color: #fff;\n}\n\n@media(min-width:768px) {\n    #page-wrapper {\n        position: inherit;\n        margin: 0 0 0 250px;\n        padding: 0 30px;\n        border-left: 1px solid #e7e7e7;\n    }\n}\n\n.navbar-top-links {\n    margin-right: 0;\n}\n\n.navbar-top-links li {\n    display: inline-block;\n}\n\n.navbar-top-links li:last-child {\n    margin-right: 15px;\n}\n\n.navbar-top-links li a {\n    padding: 15px;\n    min-height: 50px;\n}\n\n.navbar-top-links .dropdown-menu li {\n    display: block;\n}\n\n.navbar-top-links .dropdown-menu li:last-child {\n    margin-right: 0;\n}\n\n.navbar-top-links .dropdown-menu li a {\n    padding: 3px 20px;\n    min-height: 0;\n}\n\n.navbar-top-links .dropdown-menu li a div {\n    white-space: normal;\n}\n\n.navbar-top-links .dropdown-messages,\n.navbar-top-links .dropdown-tasks,\n.navbar-top-links .dropdown-alerts {\n    width: 310px;\n    min-width: 0;\n}\n\n.navbar-top-links .dropdown-messages {\n    margin-left: 5px;\n}\n\n.navbar-top-links .dropdown-tasks {\n    margin-left: -59px;\n}\n\n.navbar-top-links .dropdown-alerts {\n    margin-left: -123px;\n}\n\n.navbar-top-links .dropdown-user {\n    right: 0;\n    left: auto;\n}\n\n.sidebar .sidebar-nav.navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n    background-color: #71b1d1;\n\tcolor: #ffffff;\n\tposition: relative;\n\twidth: 100%;\n\tpadding: 0;\n\tmargin: 0;\n\tlist-style: none inside none;\n}\n\n.sidebar .sidebar-search {\n    padding: 15px;\n}\n\n.sidebar ul li {\n    border-bottom: 1px solid #e7e7e7;\n}\n\n.sidebar ul li a.active {\n  background-color: #ffffff;\n  color: #ffffff;\n}\n\n.sidebar a{\n\tcolor: #fff;\n}\n\n.sidebar .arrow {\n    float: right;\n}\n\n.sidebar .fa.arrow:before {\n    content: \"\\f104\";\n}\n\n.sidebar .active>a>.fa.arrow:before {\n    content: \"\\f107\";\n}\n\n.sidebar .nav-second-level li,\n.sidebar .nav-third-level li {\n    border-bottom: 0!important;\n}\n\n.sidebar .nav-second-level li a {\n    padding-left: 37px;\n}\n\n.sidebar .nav-third-level li a {\n    padding-left: 52px;\n}\n\n@media(min-width:768px) {\n    .sidebar {\n        z-index: 1;\n        position: absolute;\n        width: 250px;\n        margin-top: 51px;\n    }\n\n    .navbar-top-links .dropdown-messages,\n    .navbar-top-links .dropdown-tasks,\n    .navbar-top-links .dropdown-alerts {\n        margin-left: auto;\n    }\n}\n\n\n.btn-outline {\n    color: inherit;\n    background-color: transparent;\n    transition: all .5s;\n}\n\n.btn-primary.btn-outline {\n    color: #428bca;\n}\n\n.btn-success.btn-outline {\n    color: #5cb85c;\n}\n\n.btn-info.btn-outline {\n    color: #5bc0de;\n}\n\n.btn-warning.btn-outline {\n    color: #f0ad4e;\n}\n\n.btn-danger.btn-outline {\n    color: #d9534f;\n}\n\n.btn-primary.btn-outline:hover,\n.btn-success.btn-outline:hover,\n.btn-info.btn-outline:hover,\n.btn-warning.btn-outline:hover,\n.btn-danger.btn-outline:hover {\n    color: #fff;\n}\n\n.chat {\n    margin: 0;\n    padding: 0;\n    list-style: none;\n}\n\n.chat li {\n    margin-bottom: 10px;\n    padding-bottom: 5px;\n    border-bottom: 1px dotted #999;\n}\n\n.chat li.left .chat-body {\n    margin-left: 60px;\n}\n\n.chat li.right .chat-body {\n    margin-right: 60px;\n}\n\n.chat li .chat-body p {\n    margin: 0;\n}\n\n.panel .slidedown .glyphicon,\n.chat .glyphicon {\n    margin-right: 5px;\n}\n\n.chat-panel .panel-body {\n    height: 350px;\n    overflow-y: scroll;\n}\n\n.login-panel {\n    margin-top: 25%;\n}\n\n.flot-chart {\n    display: block;\n    height: 400px;\n}\n\n.flot-chart-content {\n    width: 100%;\n    height: 100%;\n}\n\n.dataTables_wrapper {\n    position: relative;\n    clear: both;\n}\n\ntable.dataTable thead .sorting,\ntable.dataTable thead .sorting_asc,\ntable.dataTable thead .sorting_desc,\ntable.dataTable thead .sorting_asc_disabled,\ntable.dataTable thead .sorting_desc_disabled {\n    background: 0 0;\n}\n\ntable.dataTable thead .sorting_asc:after {\n    content: \"\\f0de\";\n    float: right;\n    font-family: fontawesome;\n}\n\ntable.dataTable thead .sorting_desc:after {\n    content: \"\\f0dd\";\n    float: right;\n    font-family: fontawesome;\n}\n\ntable.dataTable thead .sorting:after {\n    content: \"\\f0dc\";\n    float: right;\n    font-family: fontawesome;\n    color: rgba(50,50,50,.5);\n}\n\n.btn-circle {\n    width: 30px;\n    height: 30px;\n    padding: 6px 0;\n    border-radius: 15px;\n    text-align: center;\n    font-size: 12px;\n    line-height: 1.428571429;\n}\n\n.btn-circle.btn-lg {\n    width: 50px;\n    height: 50px;\n    padding: 10px 16px;\n    border-radius: 25px;\n    font-size: 18px;\n    line-height: 1.33;\n}\n\n.btn-circle.btn-xl {\n    width: 70px;\n    height: 70px;\n    padding: 10px 16px;\n    border-radius: 35px;\n    font-size: 24px;\n    line-height: 1.33;\n}\n\n.show-grid [class^=col-] {\n    padding-top: 10px;\n    padding-bottom: 10px;\n    border: 1px solid #ddd;\n    background-color: #eee!important;\n}\n\n.show-grid {\n    margin: 15px 0;\n}\n\n.huge {\n    font-size: 40px;\n}\n\n.panel-green {\n    border-color: #5cb85c;\n}\n\n.panel-green .panel-heading {\n    border-color: #5cb85c;\n    color: #fff;\n    background-color: #5cb85c;\n}\n\n.panel-green a {\n    color: #5cb85c;\n}\n\n.panel-green a:hover {\n    color: #3d8b3d;\n}\n\n.panel-red {\n    border-color: #d9534f;\n}\n\n.panel-red .panel-heading {\n    border-color: #d9534f;\n    color: #fff;\n    background-color: #d9534f;\n}\n\n.panel-red a {\n    color: #d9534f;\n}\n\n.panel-red a:hover {\n    color: #b52b27;\n}\n\n.panel-yellow {\n    border-color: #f0ad4e;\n}\n\n.panel-yellow .panel-heading {\n    border-color: #f0ad4e;\n    color: #fff;\n    background-color: #f0ad4e;\n}\n\n.panel-yellow a {\n    color: #f0ad4e;\n}\n\n.panel-yellow a:hover {\n    color: #df8a13;\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/timeline.css",
    "content": ".timeline {\n    position: relative;\n    padding: 20px 0 20px;\n    list-style: none;\n}\n\n.timeline:before {\n    content: \" \";\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 50%;\n    width: 3px;\n    margin-left: -1.5px;\n    background-color: #eeeeee;\n}\n\n.timeline > li {\n    position: relative;\n    margin-bottom: 20px;\n}\n\n.timeline > li:before,\n.timeline > li:after {\n    content: \" \";\n    display: table;\n}\n\n.timeline > li:after {\n    clear: both;\n}\n\n.timeline > li:before,\n.timeline > li:after {\n    content: \" \";\n    display: table;\n}\n\n.timeline > li:after {\n    clear: both;\n}\n\n.timeline > li > .timeline-panel {\n    float: left;\n    position: relative;\n    width: 46%;\n    padding: 20px;\n    border: 1px solid #d4d4d4;\n    border-radius: 2px;\n    -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);\n    box-shadow: 0 1px 6px rgba(0,0,0,0.175);\n}\n\n.timeline > li > .timeline-panel:before {\n    content: \" \";\n    display: inline-block;\n    position: absolute;\n    top: 26px;\n    right: -15px;\n    border-top: 15px solid transparent;\n    border-right: 0 solid #ccc;\n    border-bottom: 15px solid transparent;\n    border-left: 15px solid #ccc;\n}\n\n.timeline > li > .timeline-panel:after {\n    content: \" \";\n    display: inline-block;\n    position: absolute;\n    top: 27px;\n    right: -14px;\n    border-top: 14px solid transparent;\n    border-right: 0 solid #fff;\n    border-bottom: 14px solid transparent;\n    border-left: 14px solid #fff;\n}\n\n.timeline > li > .timeline-badge {\n    z-index: 100;\n    position: absolute;\n    top: 16px;\n    left: 50%;\n    width: 50px;\n    height: 50px;\n    margin-left: -25px;\n    border-radius: 50% 50% 50% 50%;\n    text-align: center;\n    font-size: 1.4em;\n    line-height: 50px;\n    color: #fff;\n    background-color: #999999;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel {\n    float: right;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel:before {\n    right: auto;\n    left: -15px;\n    border-right-width: 15px;\n    border-left-width: 0;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel:after {\n    right: auto;\n    left: -14px;\n    border-right-width: 14px;\n    border-left-width: 0;\n}\n\n.timeline-badge.primary {\n    background-color: #2e6da4 !important;\n}\n\n.timeline-badge.success {\n    background-color: #3f903f !important;\n}\n\n.timeline-badge.warning {\n    background-color: #f0ad4e !important;\n}\n\n.timeline-badge.danger {\n    background-color: #d9534f !important;\n}\n\n.timeline-badge.info {\n    background-color: #5bc0de !important;\n}\n\n.timeline-title {\n    margin-top: 0;\n    color: inherit;\n}\n\n.timeline-body > p,\n.timeline-body > ul {\n    margin-bottom: 0;\n}\n\n.timeline-body > p + p {\n    margin-top: 5px;\n}\n\n@media(max-width:767px) {\n    ul.timeline:before {\n        left: 40px;\n    }\n\n    ul.timeline > li > .timeline-panel {\n        width: calc(100% - 90px);\n        width: -moz-calc(100% - 90px);\n        width: -webkit-calc(100% - 90px);\n    }\n\n    ul.timeline > li > .timeline-badge {\n        top: 16px;\n        left: 15px;\n        margin-left: 0;\n    }\n\n    ul.timeline > li > .timeline-panel {\n        float: right;\n    }\n\n    ul.timeline > li > .timeline-panel:before {\n        right: auto;\n        left: -15px;\n        border-right-width: 15px;\n        border-left-width: 0;\n    }\n\n    ul.timeline > li > .timeline-panel:after {\n        right: auto;\n        left: -14px;\n        border-right-width: 14px;\n        border-left-width: 0;\n    }\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/authority.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n            <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增授权规则</button>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">授权规则</span>\n                    <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n                    <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n                    <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n                        <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                                   placeholder=\"机器\"></selectize>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\">\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 40%\">\n                                资源名\n                            </td>\n                            <td style=\"width: 10%;\">\n                                流控应用\n                            </td>\n                            <td style=\"width: 10%;\">\n                                授权类型\n                            </td>\n                            <td style=\"width: 12%;\">\n                                操作\n                            </td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr dir-paginate=\"ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                            pagination-id=\"entriesPagination\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.resource}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.limitApp }}</td>\n                            <td>\n                                <span ng-if=\"ruleEntity.rule.strategy == 0\">白名单</span>\n                                <span ng-if=\"ruleEntity.rule.strategy == 1\">黑名单</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n                <div class=\"pagination-footer\">\n                    <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n                                             on-page-change=\"\">\n                    </dir-pagination-controls>\n                    <div class=\"tools\" style=\"\">\n                        <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n                        <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n                    </div>\n                    <!-- .tools -->\n                </div>\n                <!-- pagination-footer -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster/client.html",
    "content": "<div class=\"row clearfix\">\n    <form role=\"form\" class=\"form-horizontal\">\n        <div class=\"form-group\" ng-if=\"stateVO.currentMode == 0\">\n            <label class=\"col-sm-2 control-label\">连接状态</label>\n            <div class=\"col-sm-4\">\n                <p class=\"form-control-static text-danger\" ng-if=\"stateVO.client.clientConfig.clientState === 0\">未连接</p>\n                <p class=\"form-control-static\" ng-if=\"stateVO.client.clientConfig.clientState === 1\">连接中</p>\n                <p class=\"form-control-static text-success\" ng-if=\"stateVO.client.clientConfig.clientState === 2\">已连接</p>\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server IP</label>\n            <div class=\"col-sm-4\">\n                <input type=\"text\" class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.serverHost' placeholder='请指定 Token Server IP' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.serverPort' placeholder='请指定 Token Server 端口' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">请求超时时间（ms）</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" required class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.requestTimeout' placeholder='请指定请求超时时间（ms）' />\n            </div>\n        </div>\n    </form>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster/server.html",
    "content": "<div class=\"row clearfix\">\n    <form role=\"form\" class=\"form-horizontal\">\n        <div class=\"form-group\" ng-if=\"stateVO.currentMode == 1\">\n            <label class=\"col-sm-2 control-label\">Token Server 模式</label>\n            <div class=\"col-sm-4\">\n                <p class=\"form-control-static\" ng-if=\"!stateVO.server.embedded\">独立模式 (Alone)</p>\n                <p class=\"form-control-static\" ng-if=\"stateVO.server.embedded\">嵌入模式 (Embedded)</p>\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"1\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">命名空间集合</label>\n            <div class=\"col-sm-4\">\n                <input type=\"text\" required class=\"form-control highlight-border\" ng-model='stateVO.server.namespaceSetStr' placeholder='请指定服务端服务的命名空间集合（以,分隔）' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">最大全局 QPS</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" max=\"100000\" required class=\"form-control highlight-border\" ng-model='stateVO.server.flow.maxAllowedQps' placeholder='请指定服务端最大全局 QPS' />\n            </div>\n        </div>\n    </form>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_assign_manage.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - 机器分配/管控</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <form role=\"form\" class=\"form-horizontal\">\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Server 列表</label>\n                            <div class=\"col-sm-4\">\n                                <select ng-model=\"tmp.curChosenServer\" ng-change=\"onCurrentServerChange()\" size=\"8\"\n                                        ng-options=\"serverGroup.machineId for serverGroup in clusterMap\"\n                                        class=\"form-control\"></select>\n                            </div>\n                            <button type=\"button\" class=\"btn btn-outline-warning\" ng-click=\"removeFromServerList()\">移除\n                            </button>\n                        </div>\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n                            <div class=\"col-sm-4\">\n                                <input type=\"number\" class=\"form-control highlight-border\"\n                                       ng-disabled=\"!tmp.curChosenServer.belongToApp\"\n                                       ng-model='tmp.curChosenServer.port' placeholder='port' min=\"1\" max=\"65535\"/>\n                            </div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">命名空间集合</label>\n                            <div class=\"col-sm-4\">\n                                <input type=\"text\" required class=\"form-control highlight-border\"\n                                       ng-disabled=\"!tmp.curChosenServer.belongToApp\"\n                                       ng-model='tmp.curChosenServer.namespaceSetStr'\n                                       placeholder='请指定服务端服务的命名空间集合（以,分隔）'/>\n                            </div>\n                        </div>\n                    </form>\n                    <form role=\"form\" class=\"form-inline\" style=\"margin-top: 30px; margin-left: 20px;\">\n                        <div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <label class=\"control-label\" style=\"width: 200px; text-align: center;\">当前对应客户端列表</label>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curClientChosen\"\n                                            ng-options=\"ip for ip in tmp.curChosenServer.clientSet\"\n                                            class=\"form-control\" style=\"width: 100%;\"></select>\n                                </div>\n\n                            </div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-disabled=\"!tmp.curChosenServer || !tmp.curChosenServer.machineId\"\n                                            ng-click=\"moveToServerGroup()\">←\n                                    </button>\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-click=\"moveToRemainingSharePool()\">→\n                                    </button>\n                                </div>\n                            </div>\n\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <label class=\"control-label\" style=\"width: 220px; text-align: center;\">未分配机器列表</label>\n                                    <div>\n                                        <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curRemainingClientChosen\"\n                                                ng-options=\"ip for ip in remainingClientAddressList\"\n                                                class=\"form-control\" style=\"width: 100%;\">\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-6\">\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-click=\"addToServerList()\">添加为 server\n                                    </button>\n                                </div>\n                            </div>\n                        </div>\n                    </form>\n                    <div class=\"separator\"></div>\n                    <div style=\"margin-top: 20px;\">\n                        <button type=\"button\" style=\"margin: 0 10px 10px 10px;\" class=\"btn btn-outline-success\"\n                                ng-click=\"saveAndApplyAssign()\">保存并执行分配\n                        </button>\n                    </div>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_client_list.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.clusterAppServerList({app: app})\">\n            Token Server 列表\n        </a>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Client 列表</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"min-width: 12%;\">Client ID</td>\n                            <td>Server IP</td>\n                            <td>Server 端口</td>\n                            <td>连接状态</td>\n                            <td style=\"min-width: 15%;\">操作</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"clientVO in clientVOList\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{clientVO.id}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{clientVO.state.clientConfig.serverHost}}</td>\n                            <td>{{clientVO.state.clientConfig.serverPort}}</td>\n                            <td>\n                                <span class=\"form-control-static text-danger\" ng-if=\"clientVO.state.clientConfig.clientState === 0\">未连接</span>\n                                <span class=\"form-control-static\" ng-if=\"clientVO.state.clientConfig.clientState === 1\">连接中</span>\n                                <span class=\"form-control-static text-success\" ng-if=\"clientVO.state.clientConfig.clientState === 2\">已连接</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\"\n                                        ng-click=\"modifyClientConfigDialog(clientVO)\" style=\"font-size: 12px; height:25px;\">编辑配置</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_server_list.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-if=\"!loadError\" ng-click=\"newServerDialog()\">\n            <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增 Token Server</button>\n        <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.clusterAppClientList({app: app})\">\n            Token Client 列表\n        </a>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Server 列表</span>\n                    <input class=\"form-control width-200\" placeholder=\"搜索 server...\" ng-model=\"searchKey\">\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 15%;\">Server ID</td>\n                            <td style=\"width: 10%;\">Port</td>\n                            <td style=\"width: 15%;\">命名空间集合</td>\n                            <td style=\"width: 10%;\">运行模式</td>\n                            <td>总连接数</td>\n                            <td>QPS 总览</td>\n                            <td style=\"width: 20%;\">操作</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"serverVO in serverVOList | filter: {id: searchKey}\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.id}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">{{serverVO.id}}（自主指定）</span>\n                            </td>\n                            <td>{{serverVO.port}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.state.namespaceSetStr}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                                <span ng-if=\"serverVO.belongToApp && serverVO.state.embedded\">嵌入模式</span>\n                                <span ng-if=\"serverVO.belongToApp && !serverVO.state.embedded\">独立模式</span>\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.connectedCount}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td>\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.state.requestLimitDataStr}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\" ng-if=\"serverVO.belongToApp\"\n                                        ng-click=\"viewConnectionDetail(serverVO)\" style=\"font-size: 12px; height:25px;\">连接详情</button>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\"\n                                        ng-click=\"modifyServerAssignConfig(serverVO)\" style=\"font-size: 12px; height:25px;\">管理</button>\n                                <button class=\"btn btn-xs btn-outline-danger\" type=\"button\"\n                                        ng-click=\"unbindServer(serverVO.id)\" style=\"font-size: 12px; height:25px;\">移除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_server_overview.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Server 总览</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <form role=\"form\" class=\"form-horizontal\">\n                        <div class=\"form-group\" hidden>\n                            <label class=\"col-sm-2 control-label\">Token Server 列表</label>\n                            <div class=\"col-sm-4\">\n                                <select ng-model=\"tmp.curChosenServer\" ng-change=\"onChosenServerChange()\"\n                                        ng-options=\"serverEntity.id for serverEntity in serverVOList\"\n                                        class=\"form-control\"></select>\n                            </div>\n                        </div>\n                    </form>\n\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 12%;\">Server ID</td>\n                            <td style=\"width: 5%;\">Port</td>\n                            <td style=\"width: 10%;\">命名空间集合</td>\n                            <td>总连接数</td>\n                            <td>连接情况</td>\n                            <td>QPS 总览</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"serverVO in clientVOList\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{serverVO.id}}</td>\n                            <td>{{serverVO.port}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{serverVO.state.namespaceSetStr}}\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{serverVO.connectedCount}}\n                            </td>\n                            <td>\n                                <p ng-repeat=\"cg in serverVO.state.connection\">\n                                    namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients:\n                                    {{generateConnectionSet(cg.connectionSet)}}\n                                </p>\n                            </td>\n                            <td>\n                                <p ng-repeat=\"crl in serverVO.state.requestLimitData\">\n                                    namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS:\n                                    {{crl.maxAllowedQps}}\n                                </p>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_single_config.html",
    "content": "\n<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">集群限流</span>\n          <input class=\"form-control witdh-200\" placeholder=\"机器搜索\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                       placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!-- error panel -->\n        <div class=\"row clearfix\" ng-if=\"loadError\">\n          <div class=\"col-md-6 col-md-offset-3\">\n            <div class=\"panel panel-default\">\n              <div class=\"panel-body\">\n                <center>\n                  <p>{{loadError.message}}</p>\n                </center>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\"  ng-if=\"!loadError\">\n          <form role=\"form\" class=\"form-horizontal\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">当前模式</label>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == 0\">Client</p>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == 1\">Server</p>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == -1\">未开启</p>\n            </div>\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">集群限流模式变换</label>\n              <div class=\"col-sm-4\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"mode\" value=\"0\" ng-model='stateVO.stateInfo.mode' ng-disabled=\"!stateVO.stateInfo.clientAvailable\" />&nbsp;Client&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"mode\" value=\"1\" ng-model='stateVO.stateInfo.mode' ng-disabled=\"!stateVO.stateInfo.serverAvailable\" />&nbsp;Server\n                </div>\n              </div>\n            </div>\n          </form>\n          <!-- no-cluster-mode-available-panel.start -->\n          <div ng-if=\"!stateVO.stateInfo.clientAvailable && !stateVO.stateInfo.serverAvailable\">\n            <!-- error panel -->\n            <div class=\"row clearfix\">\n              <div class=\"col-md-6 col-md-offset-3\">\n                <div class=\"panel panel-default\">\n                  <div class=\"panel-body\">\n                    <center>\n                      <p>该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖，请引入相关依赖。</p>\n                    </center>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <!-- no-cluster-mode-available-panel.stop -->\n\n          <div ng-if=\"stateVO.stateInfo.clientAvailable || stateVO.stateInfo.serverAvailable\">\n            <div ng-if=\"stateVO.stateInfo.clientAvailable && stateVO.stateInfo.mode == 0\">\n              <div ng-include=\"'app/views/cluster/client.html'\"></div>\n            </div>\n            <div ng-if=\"stateVO.stateInfo.serverAvailable && stateVO.stateInfo.mode == 1\">\n              <div ng-include=\"'app/views/cluster/server.html'\"></div>\n            </div>\n\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n              <button style=\"margin: 0 10px 10px 10px;\" class=\"btn btn-outline-success\"\n                      ng-click=\"saveConfig()\">保存配置</button>\n            </div>\n          </div>\n\n        </div>\n        <!-- .card-body -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dashboard/home.html",
    "content": "<div>\n  <div class=\"row\">\n    <div class=\"col-lg-12\">\n      <h1 class=\"page-header\">欢迎使用 Sentinel 控制台</h1>\n    </div>\n    <!-- /.col-lg-12 -->\n  </div>\n  <!-- /.row -->\n  <!-- /.panel -->\n  <div class=\"row\">\n  </div>\n  <!-- /.row -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dashboard/main.html",
    "content": "<div id=\"wrapper\">\n  <!-- Navigation -->\n  <header></header>\n  <!-- /.navbar-top-links -->\n  <!-- /.navbar-static-side -->\n  <div id=\"page-wrapper\">\n    <div ui-view></div>\n  </div>\n  <!-- /#page-wrapper -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/degrade.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增降级规则</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">降级规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <!--<td style=\"width: 10%;\">-->\n                  <!--降级应用-->\n                <!--</td>-->\n                <td style=\"width: 10%;\">\n                  降级模式\n                </td>\n                <td style=\"width: 10%;\">\n                  阈值\n                </td>\n                <td style=\"width: 10%;\">\n                  时间窗口(s)\n                </td>\n                <!--<td style=\"width: 8%;\">-->\n                <!--状态-->\n                <!--</td>-->\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <!--<td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>-->\n                <td>\n                  <span ng-if=\"rule.grade == 0\">RT</span>\n                  <span ng-if=\"rule.grade == 1\" title=\"秒级异常比例\">异常比例</span>\n                  <span ng-if=\"rule.grade == 2\" title=\"分钟级异常数\">异常数</span>\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.timeWindow}}s\n                </td>\n\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/authority-rule-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">{{authorityRuleDialog.title}}</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">资源名</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" ng-if=\"authorityRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.rule.resource'\n                                   disabled=\"\" />\n                            <input type=\"text\" ng-if=\"authorityRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名称\" ng-model='currentRule.rule.resource'\n                            />\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">流控应用</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.rule.limitApp' placeholder='指调用方，多个调用方名称用半角英文逗号（,）分隔'\n                            />\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">授权类型</label>\n                        <div class=\"col-sm-4\">\n                            <div class=\"form-control highlight-border\" align=\"center\">\n                                <input type=\"radio\" name=\"strategy\" value=\"0\" checked ng-model='currentRule.rule.strategy' />&nbsp;白名单&nbsp;&nbsp;\n                                <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='currentRule.rule.strategy' />&nbsp;黑名单\n                            </div>\n                        </div>\n                    </div>\n\n                </form>\n            </div>\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n                <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n                <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{authorityRuleDialog.confirmBtnText}}</button>\n                <button ng-if=\"authorityRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n                        ng-click=\"saveRuleAndContinue()\">{{authorityRuleDialog.saveAndContinueBtnText}}</button>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-client-config-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">修改 Token Client 配置</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Client ID</label>\n                <div class=\"col-sm-4\">\n                    <p class=\"form-control-static\">{{ccDialogData.clientId}}</p>\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Token Server IP</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"text\" class=\"form-control highlight-border\" ng-model='ccDialogData.serverHost' placeholder='请指定 Token Server IP' />\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Token Server 端口</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"number\" min=\"0\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='ccDialogData.serverPort' placeholder='请指定 Token Server 端口' />\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">请求超时时间（ms）</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"number\" min=\"0\" required class=\"form-control highlight-border\" ng-model='ccDialogData.requestTimeout' placeholder='请指定请求超时时间（ms）' />\n                </div>\n            </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"ccDialog.close()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"doModifyClientConfig()\">保存</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-assign-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">{{serverAssignDialogData.title}}</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div ng-if=\"serverAssignDialogData.type == 'edit'\">\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Token Server</label>\n                            <div class=\"col-sm-4\">\n                                <p class=\"form-control-static\">{{serverAssignDialogData.serverData.currentServer}}</p>\n                            </div>\n\n                            <label class=\"col-sm-2 control-label\">Server 端口</label>\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                       ng-disabled=\"!serverAssignDialogData.serverData.belongToApp\"\n                                       ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口'/>\n                            </div>\n                        </div>\n                        <div class=\"form-group\" ng-if=\"serverAssignDialogData.serverData.belongToApp\">\n                            <label class=\"col-sm-2 control-label\"\n                                   title=\"server 最大允许的总 QPS，注意 embedded 模式下不要设的太大\">最大允许 QPS</label>\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" min=\"0\" max=\"200000\" class=\"form-control highlight-border\"\n                                       ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div ng-if=\"serverAssignDialogData.type == 'add'\">\n                        <div class=\"form-group\" >\n                            <label class=\"col-sm-2 control-label\">机器类型</label>\n                            <div class=\"col-sm-4\">\n                                <div class=\"form-control highlight-border\" align=\"center\">\n                                    <input type=\"radio\" name=\"strategy\" value=\"0\" checked ng-model='serverAssignDialogData.serverData.serverType' />&nbsp;应用内机器&nbsp;&nbsp;\n                                    <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='serverAssignDialogData.serverData.serverType' />&nbsp;外部指定机器\n                                </div>\n                            </div>\n\n                            <div ng-if=\"serverAssignDialogData.serverData.serverType == 1\">\n                                <div class=\"col-sm-6\">\n                                    <p class=\"form-control-static text-primary\" style=\"font-size: x-small;\">若指定外部 server，请先在相应页面对外部 server 进行配置，然后在此页面指定。</p>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div ng-if=\"serverAssignDialogData.serverData.serverType == 0\">\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\">选择机器</label>\n                                <div class=\"col-sm-4\">\n                                    <select ng-model=\"serverAssignDialogData.serverData.currentServer\" ng-change=\"onCurrentServerChange()\"\n                                            ng-options=\"machineId for machineId in remainingMachineList\"\n                                            class=\"form-control\"></select>\n                                </div>\n\n                                <label class=\"col-sm-2 control-label\">Server 端口</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>\n                                </div>\n                            </div>\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\"\n                                       title=\"server 最大允许的总 QPS，注意 embedded 模式下不要设的太大\">最大允许 QPS</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"0\" max=\"200000\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div ng-if=\"serverAssignDialogData.serverData.serverType == 1\">\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\">Server IP</label>\n                                <div class=\"col-sm-4\">\n                                    <input type=\"text\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.currentServer' placeholder='请输入独立的 Token Server IP'/>\n                                </div>\n\n                                <label class=\"col-sm-2 control-label\">Server 端口</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </form>\n\n                <!-- assign form start -->\n                <form role=\"form\" class=\"form-inline\" ng-if=\"serverAssignDialogData.serverData.currentServer\"\n                      style=\"margin-top: 30px; margin-left: 20px; text-align: center;\">\n                    <div>\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n                                <label class=\"control-label\" style=\"width: 220px; text-align: center;\">请从中选取 client：</label>\n                                <div>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curRemainingClientChosen\"\n                                            ng-options=\"ip for ip in remainingMachineList | filter: notChosenServer\"\n                                            class=\"form-control\" style=\"width: 100%;\">\n                                    </select>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n\n                                <button type=\"button\" class=\"btn btn-outline-primary\"\n                                        ng-click=\"moveToRemainingSharePool()\">←\n                                </button>\n                                <button type=\"button\" class=\"btn btn-outline-primary\"\n                                        ng-click=\"moveToServerGroup()\">→\n                                </button>\n                            </div>\n                        </div>\n\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n                                <label class=\"control-label\" style=\"width: 200px; text-align: center;\">已选取的 client 列表</label>\n                                <div>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curClientChosen\"\n                                            ng-options=\"ip for ip in serverAssignDialogData.serverData.clientSet\"\n                                            class=\"form-control\" style=\"width: 100%;\"></select>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </form>\n            </div>\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n                <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"serverAssignDialog.close()\">取消</button>\n                <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveAssignForDialog()\">{{serverAssignDialogData.confirmBtnText}}</button>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">连接详情</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-3 control-label\">Token Server</label>\n                        <div class=\"col-sm-4\">\n                            <p class=\"form-control-static\">{{connectionDetailDialogData.serverData.id}}</p>\n                        </div>\n                    </div>\n                </form>\n\n                <div class=\"col-md-12\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"min-width: 15%;\" class=\"text-center\">命名空间</td>\n                            <td class=\"text-center\">连接数</td>\n                            <td class=\"text-center\">连接详情</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"cg in connectionDetailDialogData.serverData.state.connection\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\" class=\"text-center\">{{cg.namespace}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\" class=\"text-center\">{{cg.connectedCount}}</td>\n                            <td class=\"text-center\">{{generateConnectionSet(cg.connectionSet)}}</td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/confirm-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{confirmDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <p style=\"margin-left: 20px; margin-right: 20px;\">\n          {{confirmDialog.attentionTitle}}:\n          <br/>\n          <br/>\n          <code style=\"word-wrap: break-word; white-space:normal\">{{confirmDialog.attention}}</code>\n        </p>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-default-inverse\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;\" ng-click=\"confirm()\">{{confirmDialog.confirmBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/degrade-rule-dialog.html",
    "content": "<div>\n\t<span class=\"brand\" style=\"font-weight:bold;\">{{degradeRuleDialog.title}}</span>\n\t<div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n\t\t<div class=\"panel-body\">\n\t\t\t<div class=\"row\">\n\t\t\t\t<form role=\"form\" class=\"form-horizontal\">\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">资源名</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"degradeRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.resource'\n\t\t\t\t\t\t\t disabled=\"\" />\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"degradeRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.resource'\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!--<div class=\"form-group\">-->\n\t\t\t\t\t\t<!--<label class=\"col-sm-2 control-label\">流控应用</label>-->\n\t\t\t\t\t\t<!--<div class=\"col-sm-9\">-->\n\t\t\t\t\t\t\t<!--<input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.limitApp' placeholder='\"default\"表示所有应用。' />-->\n\t\t\t\t\t\t<!--</div>-->\n\t\t\t\t\t<!--</div>-->\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">降级策略</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<div class=\"form-control highlight-border\" align=\"center\">\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"0\" checked ng-model='currentRule.grade' title=\"秒级平均响应时间\" />&nbsp;RT&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' title=\"秒级异常比例\" />&nbsp;异常比例&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' title=\"分钟级异常数，仅 1.3.0 及以上版本生效\" />&nbsp;异常数\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 0\" class=\"col-sm-2 control-label\">RT</label>\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 1\" class=\"col-sm-2 control-label\">异常比例</label>\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 2\" class=\"col-sm-2 control-label\">异常数</label>\n\t\t\t\t\t\t<div class=\"col-sm-3\">\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 0\" placeholder=\"毫秒\"/>\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 1\" placeholder=\"0.0~1.0\"/>\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 2\" placeholder=\"异常数\"/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">时间窗口</label>\n\t\t\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t\t\t\t<input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.timeWindow' placeholder=\"降级时间间隔, 单位秒\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t\t<div class=\"separator\"></div>\n\t\t\t<div clss=\"row\" style=\"margin-top: 20px;\">\n\t\t\t\t<button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n\t\t\t\t<button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{degradeRuleDialog.confirmBtnText}}</button>\n\t\t\t\t<button ng-if=\"degradeRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n\t\t\t\t ng-click=\"saveRuleAndContinue()\">{{degradeRuleDialog.saveAndContinueBtnText}}</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/flow-rule-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{flowRuleDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">资源名</label>\n            <div class=\"col-sm-9\">\n              <input type=\"text\" ng-if=\"flowRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.resource'\n                disabled=\"\" />\n              <input type=\"text\" ng-if=\"flowRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.resource'\n              />\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\" data-toggle=\"tooltip\" title=\"流控针对应用，即流量入口的调用来源（origin）\">来源应用</label>\n            <div class=\"col-sm-9\">\n              <input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.limitApp' placeholder='指调用方，\"default\"表示所有应用。'\n              />\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值类型</label>\n            <div class=\"col-sm-4\">\n              <div class=\"form-control highlight-border\" align=\"center\">\n                <input type=\"radio\" name=\"grade\" value=\"1\" checked ng-model='currentRule.grade' />&nbsp;QPS&nbsp;&nbsp;\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' />&nbsp;线程数\n              </div>\n            </div>\n            <div ng-if=\"!currentRule.clusterMode\">\n              <label class=\"col-sm-2 control-label\">单机阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"单机阈值\" />\n              </div>\n            </div>\n            <div ng-if=\"currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 0\">\n              <label class=\"col-sm-2 control-label\">均摊阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"单机均摊阈值\" />\n              </div>\n            </div>\n            <div ng-if=\"currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 1\">\n              <label class=\"col-sm-2 control-label\">集群阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"集群总体阈值\" />\n              </div>\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">是否集群</label>\n            <div class=\"col-sm-2\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.clusterMode\">\n              </label>\n            </div>\n            <div ng-if=\"currentRule.clusterMode\">\n              <label class=\"col-sm-3 control-label\">集群阈值模式</label>\n              <div class=\"col-sm-4\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"clusterThresholdType\" value=\"0\" ng-model='currentRule.clusterConfig.thresholdType' />&nbsp;单机均摊&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"clusterThresholdType\" value=\"1\" ng-model='currentRule.clusterConfig.thresholdType' />&nbsp;总体阈值\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"form-group\" ng-if=\"currentRule.clusterMode\">\n            <label class=\"col-sm-2 control-label\">失败退化</label>\n            <div class=\"col-sm-8\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.clusterConfig.fallbackToLocalWhenFail\">\n              </label>\n              <i class=\"glyphicon glyphicon-info-sign\"></i>&nbsp;如果 Token Server 不可用是否退化到单机限流\n            </div>\n          </div>\n\n          <div ng-if=\"!flowRuleDialog.showAdvanceButton\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\" title=\"调用关系流控模式\">流控模式</label>\n              <div class=\"col-sm-9\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"strategy\" value=\"0\" ng-model='currentRule.strategy' />&nbsp;直接&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='currentRule.strategy' />&nbsp;关联&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"strategy\" value=\"2\" ng-model='currentRule.strategy' />&nbsp;链路&nbsp;&nbsp;\n                </div>\n              </div>\n            </div>\n\n            <div class=\"form-group\" ng-show=\"currentRule.strategy==1\">\n              <label class=\"col-sm-2 control-label\">关联资源</label>\n              <div class=\"col-sm-9\">\n                <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"关联资源\" ng-model='currentRule.refResource' />\n              </div>\n            </div>\n            <div class=\"form-group\" ng-show=\"currentRule.strategy==2\">\n              <label class=\"col-sm-2 control-label\">入口资源</label>\n              <div class=\"col-sm-9\">\n                <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"入口资源\" ng-model='currentRule.refResource' />\n              </div>\n            </div>\n          </div>\n          <div ng-if=\"currentRule.grade==1 && !flowRuleDialog.showAdvanceButton\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">流控效果</label>\n              <div class=\"col-sm-9\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"0\" checked ng-model='currentRule.controlBehavior' />&nbsp;快速失败&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"1\" ng-model='currentRule.controlBehavior' />&nbsp;Warm Up&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"2\" ng-model='currentRule.controlBehavior' />&nbsp;排队等待\n                </div>\n              </div>\n\n            </div>\n            <div class=\"form-group\">\n              <div ng-if=\"currentRule.controlBehavior==1\">\n                <label class=\"col-sm-2 control-label\">预热时长</label>\n                <div class=\"col-sm-9\">\n                  <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.warmUpPeriodSec' placeholder=\"秒\" />\n                </div>\n              </div>\n              <div ng-if=\"currentRule.controlBehavior==2\">\n                <label class=\"col-sm-2 control-label\">超时时间</label>\n                <div class=\"col-sm-9\">\n                  <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.maxQueueingTimeMs' placeholder=\"毫秒\" />\n                </div>\n              </div>\n            </div>\n          </div>\n          <div class=\"form-group text-center\">\n            <a ng-click=\"onOpenAdvanceClick()\" ng-if=\"flowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">高级选项</a>\n            <a ng-click=\"onCloseAdvanceClick()\" ng-if=\"!flowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">关闭高级选项</a>\n          </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{flowRuleDialog.confirmBtnText}}</button>\n        <button ng-if=\"flowRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n          ng-click=\"saveRuleAndContinue()\">{{flowRuleDialog.saveAndContinueBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/param-flow-rule-dialog.html",
    "content": "<div>\n\t<span class=\"brand\" style=\"font-weight:bold;\">{{paramFlowRuleDialog.title}}</span>\n\t<div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n\t\t<div class=\"panel-body\">\n\t\t\t<div class=\"clearfix\">\n\t\t\t\t<form role=\"form\" class=\"form-horizontal\">\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">资源名</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"paramFlowRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.rule.resource' disabled=\"\" />\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"paramFlowRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.rule.resource' required />\n\t\t\t\t\t\t</div>\n                    </div>\n                    \n                    <div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">限流模式</label>\n\t\t\t\t\t\t<p class=\"col-sm-9 control-label\" style=\"text-align: left; font-weight: normal;\">QPS 模式</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数索引</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.paramIdx' placeholder='请填入传入的热点参数的索引（从 0 开始）' />\n\t\t\t\t\t\t</div>\n                    </div>\n                    \n                    <div class=\"form-group\">\n\t\t\t\t\t\t<div ng-if=\"!currentRule.rule.clusterMode\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">单机阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='单机阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 0\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">均摊阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='集群均摊阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 1\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">集群阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='集群总体阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n                    </div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">是否集群</label>\n\t\t\t\t\t\t<div class=\"col-sm-2\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.rule.clusterMode\">\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode\">\n\t\t\t\t\t\t\t<label class=\"col-sm-3 control-label\">集群阈值模式</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t\t\t\t\t<div class=\"form-control highlight-border\" align=\"center\">\n\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"clusterThresholdType\" value=\"0\" ng-model='currentRule.rule.clusterConfig.thresholdType' />&nbsp;单机均摊&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"clusterThresholdType\" value=\"1\" ng-model='currentRule.rule.clusterConfig.thresholdType' />&nbsp;总体阈值\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\n                    <!-- exclusion item part start -->\n\t\t\t\t\t<div ng-if=\"!paramFlowRuleDialog.showAdvanceButton\">\n\t\t\t\t\t\t<hr />\n\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t<div class=\"form-group\" style=\"text-align: center\">\n\t\t\t\t\t\t\t\t<label class=\"control-label\">参数例外项</label>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数类型</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-9\">\n\t\t\t\t\t\t\t\t\t<select ng-model=\"curExItem.classType\" ng-options=\"classType for classType in paramItemClassTypeList\" class=\"form-control\" placeholder=\"请选择参数类型\">\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数值</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-3\">\n\t\t\t\t\t\t\t\t\t<input ng-model=\"curExItem.object\" type=\"text\" class=\"form-control\" placeholder=\"例外项参数值\">\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">限流阈值</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" ng-model=\"curExItem.count\" class=\"form-control\" placeholder=\"限流阈值\">\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div class=\"col-md-2\">\n\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-success\"\n\t\t\t\t\t\t\t\t\t\t\tng-disabled=\"notValidParamItem(curExItem)\"\n\t\t\t\t\t\t\t\t\t\t\tng-click=\"addParamItem()\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"fa fa-plus\">&nbsp;添加</span>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<div class=\"col-md-12\">\n\t\t\t\t\t\t\t\t\t<table class=\"table table-condensed table-hover\">\n\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t<th>参数值</th>\n\t\t\t\t\t\t\t\t\t\t<th>参数类型</th>\n\t\t\t\t\t\t\t\t\t\t<th>限流阈值</th>\n\t\t\t\t\t\t\t\t\t\t<th>操作</th>\n\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t<tr ng-repeat=\"paramItem in currentRule.rule.paramFlowItemList\">\n\t\t\t\t\t\t\t\t\t\t\t<td><input ng-model=\"paramItem.object\" type=\"text\" class=\"form-control\" placeholder=\"例外项参数\"></td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<p>{{paramItem.classType}}</p>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"number\" ng-model=\"paramItem.count\" class=\"form-control\" placeholder=\"限流阈值\">\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-danger\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tng-click=\"removeParamItem(paramItem.object, paramItem.classType)\"><span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"fa fa-trash-o\">&nbsp;删除</span></button>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- exclusion item part end -->\n\t\t\t\t\t<div class=\"form-group text-center\" ng-if=\"paramFlowRuleDialog.supportAdvanced\">\n\t\t\t\t\t\t<a ng-click=\"onOpenAdvanceClick()\" ng-if=\"paramFlowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">高级选项</a>\n\t\t\t\t\t\t<a ng-click=\"onCloseAdvanceClick()\" ng-if=\"!paramFlowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">关闭高级选项</a>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t\t<div class=\"separator\"></div>\n\t\t\t<div clss=\"row\" style=\"margin-top: 20px;\">\n\t\t\t\t<button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n\t\t\t\t<button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{paramFlowRuleDialog.confirmBtnText}}</button>\n\t\t\t\t<button ng-if=\"paramFlowRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n\t\t\t\t ng-click=\"saveRuleAndContinue()\">{{paramFlowRuleDialog.saveAndContinueBtnText}}</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/system-rule-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{systemRuleDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值类型</label>\n            <div class=\"col-sm-9\">\n              <div class=\"form-control\" ng-if=\"systemRuleDialog.type == 'edit'\" align=\"center\">\n                <!--avgLoad -->\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;LOAD&nbsp;&nbsp;\n                <!--avgRt -->\n                <input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;RT&nbsp;&nbsp;\n                <!--maxThread -->\n                <input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;线程数&nbsp;&nbsp;\n                <!--qps -->\n                <input type=\"radio\" name=\"grade\" value=\"3\" checked ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\"\n                />&nbsp;入口 QPS\n              </div>\n              <div class=\"form-control highlight-border\" ng-if=\"systemRuleDialog.type == 'add'\" align=\"center\">\n                <!--avgLoad -->\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;LOAD&nbsp;&nbsp;\n                <!--avgRt -->\n                <input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;RT&nbsp;&nbsp;\n                <!--maxThread -->\n                <input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;线程数&nbsp;&nbsp;\n                <!--qps -->\n                <input type=\"radio\" name=\"grade\" value=\"3\" checked ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\"\n                />&nbsp;入口 QPS\n              </div>\n            </div>\n          </div>\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值</label>\n            <div class=\"col-sm-9\">\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.avgLoad' placeholder=\"[0, ~)的小数\" ng-if=\"currentRule.grade == 0\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.avgRt' placeholder=\"[0, ~)的整数\" ng-if=\"currentRule.grade == 1\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.maxThread' placeholder=\"[0, ~)的整数\" ng-if=\"currentRule.grade == 2\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.qps' placeholder=\"[0, ~)的小数\" ng-if=\"currentRule.grade == 3\"\n              />\n            </div>\n          </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;\" ng-click=\"saveRule()\">{{systemRuleDialog.confirmBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/flow_v1.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增流控规则</button>\n    <!--<a class=\"btn btn-outline-success\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.flow({app: app})\">-->\n      <!--回到集群页面</a>-->\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">流控规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <td style=\"width: 8%;\">\n                  来源应用\n                </td>\n                <td style=\"width: 8%;\">\n                  流控模式\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 6%;\">\n                  阈值\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值模式\n                </td>\n                <td style=\"width: 10%;\">\n                  流控效果\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>\n                <td>\n                  <span ng-if=\"rule.strategy == 0\">直接</span>\n                  <span ng-if=\"rule.strategy == 1\">关联</span>\n                  <span ng-if=\"rule.strategy == 2\">链路</span>\n                </td>\n                <td>\n                  {{rule.grade==0 ? '线程数' : 'QPS'}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td>\n                  <span>{{generateThresholdTypeShow(rule)}}</span>\n                </td>\n                <td>\n                  <span ng-if=\"rule.controlBehavior == 0\">快速失败</span>\n                  <span ng-if=\"rule.controlBehavior == 1\">Warm Up</span>\n                  <span ng-if=\"rule.controlBehavior == 2\">排队等待</span>\n                  <span ng-if=\"rule.controlBehavior == 3\">预热排队</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/flow_v2.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增流控规则\n    </button>\n    <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.flowV1({app: app})\">\n      回到单机页面\n    </a>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">流控规则</span>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <td style=\"width: 10%;\">\n                  来源应用\n                </td>\n                <td style=\"width: 8%;\">\n                  流控模式\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值模式\n                </td>\n                <td style=\"width: 8%;\">\n                  流控效果\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>\n                <td>\n                  <span ng-if=\"rule.strategy == 0\">直接</span>\n                  <span ng-if=\"rule.strategy == 1\">关联</span>\n                  <span ng-if=\"rule.strategy == 2\">链路</span>\n                </td>\n                <td>\n                  {{rule.grade == 0 ? '线程数' : 'QPS'}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td>\n                  <span>{{generateThresholdTypeShow(rule)}}</span>\n                </td>\n                <td>\n                  <span ng-if=\"rule.controlBehavior == 0\">快速失败</span>\n                  <span ng-if=\"rule.controlBehavior == 1\">Warm Up</span>\n                  <span ng-if=\"rule.controlBehavior == 2\">排队等待</span>\n                  <span ng-if=\"rule.controlBehavior == 3\">预热排队</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/identity.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"btn-group\" style=\"float:right; margin-right: 30px;\" ng-show=\"!isTreeView\" ng-click=\"treeView()\">\n    <button class=\"btn btn-default-inverse\" style=\"height: 30px;font-size: 12px;\">树状视图</button>\n    <button class=\"btn btn-main\" style=\"height: 30px;font-size: 12px;\">列表视图</button>\n  </div>\n  <div class=\"btn-group\" style=\"float:right; margin-right: 30px;\" ng-show=\"isTreeView\" ng-click=\"listView()\">\n    <button class=\"btn btn-main\" style=\"height: 30px;font-size: 12px;\">树状视图</button>\n    <button class=\"btn btn-default-inverse\" style=\"height: 30px;font-size: 12px;\">列表视图</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">簇点链路</span>\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"queryIdentities()\">刷新</button>\n          <!-- <button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"expandAll()\"\n            ng-if=\"!isExpand && isTreeView\">展开</button>\n          <button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"collapseAll()\"\n            ng-if=\"isExpand && isTreeView\">折叠</button> -->\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\" ng-change=\"searchChange(searchKey)\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table rz-table id=\"identities\" class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <thead>\n                <tr style=\"background: #F3F5F7;\">\n                  <td style=\"width: 40%;\">\n                    资源名\n                  </td>\n                  <td style=\"width: 7%;\">通过QPS</td>\n                  <td style=\"width: 7%;\">拒绝QPS</td>\n                  <td style=\"width: 5%;\">线程数</td>\n                  <td style=\"width: 6%;\">平均RT</td>\n                  <td style=\"width: 6%;\">分钟通过</td>\n                  <td style=\"width: 6%;\">分钟拒绝</td>\n                  <td style=\"width: 23%\">操作</td>\n                </tr>\n                <tr></tr>\n              </thead>\n              <tbody>\n                <tr dir-paginate=\"resource in identities | itemsPerPage: pageSize\" current-page=\"currentPage\" pagination-id=\"entriesPagination\"\n                  data-tt-id=\"{{resource.ttId}}\" data-tt-parent-id=\"{{resource.parentTtId}}\" data-tt-visible=\"{{resource.visible}}\">\n                  <td style=\"white-space: normal; text-align: left;\">\n                    <!--<a ng-click=\"copyIdentity(resource.resource)\"-->\n                    <!--title=\"{{resource.resource}} 单击复制到剪切板\">-->\n                    <span style=\"word-wrap:break-word;word-break:break-all;\">{{resource.resource}}</span>\n                    <!--</a>-->\n                  </td>\n                  <td>{{resource.passQps}}</td>\n                  <td>{{resource.blockQps}}</td>\n                  <td>{{resource.threadNum}}</td>\n                  <td>{{resource.averageRt}}</td>\n                  <td>{{resource.oneMinutePass}}</td>\n                  <td ng-if=\"$index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1)\" ng-init=\"initTreeTable()\">\n                    {{resource.oneMinuteBlock}}</td>\n                  <td ng-if=\"!($index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1))\"> {{resource.oneMinuteBlock}}</td>\n                  <td>\n                    <div class=\"control-group\">\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewFlowRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;流控</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewDegradeRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;降级</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewParamFlowRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;热点</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewAuthorityRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;授权</button>\n                    </div>\n                  </td>\n                </tr>\n              </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\">\n            <span>共 {{totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{currentPage}} / {{totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/login.html",
    "content": "<div class=\"container\">\n    <div class=\"row\" style=\"margin: 200px auto 15px auto; text-align: center;\">\n        <img src=\"../../assets/img/sentinel-logo.png\" alt=\"Sentinel Logo\"\n             style=\"max-height: 25%; max-width: 25%;\">\n        <!-- <h1 id='login_title'>Sentinel 控制台</h1> -->\n    </div>\n    <div class=\"row\">\n        <div class=\"col-md-4\" >\n        </div>\n        <div class=\"col-md-4\">\n            <form class=\"form-horizontal\">\n                <div class=\"form-group\">\n                    <label class=\"col-md-2 control-label\">用户</label>\n                    <div class=\"col-md-9\">\n                        <input class=\"form-control\" type=\"text\" ng-model=\"username\" autofocus=\"autofocus\" required/>\n                    </div>\n                </div>\n\n                <div class=\"form-group\">\n                    <label class=\"col-md-2 control-label\">密码</label>\n                    <div class=\"col-md-9\">\n                        <input class=\"form-control\" type=\"password\" ng-model=\"password\" />\n                    </div>\n                </div>\n                <div class=\"row text-center\" style=\"margin-top: 20px;\">\n                    <button class=\"btn btn-outline-success\" style=\"height: 30px;font-size: 12px;\"\n                            ng-click=\"login()\">登录</button>\n                    <button class=\"btn btn-outline-primary\" style=\"height: 30px;font-size: 12px;margin-left: 10px;\"\n                            type=\"reset\">清空</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/machine.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<!--<div>-->\n<!--<span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length-healthyCount}}</span>-->\n<!--</div>-->\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">机器列表</span>\n          <span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length - healthyCount}}.</span>\n          <input class=\"form-control width-300\" placeholder=\"关键字\" ng-model=\"searchKey\">\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td>机器名</td>\n                <td>IP 地址</td>\n                <td>端口号</td>\n                <td>Sentinel 客户端版本</td>\n                <td>健康状态</td>\n                <td>心跳时间</td>\n                <td>操作</td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"entry in machines | filter : searchKey | itemsPerPage: machinesPageConfig.pageSize \" current-page=\"machinesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{entry.hostname}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{entry.ip}}</td>\n                <td> {{entry.port}} </td>\n                <td> {{entry.version}} </td>\n                <td ng-if=\"entry.healthy\">健康</td>\n                <td ng-if=\"!entry.healthy\" style=\"color: red\">失联</td>\n                <td>{{entry.lastHeartbeat | date: 'yyyy/MM/dd HH:mm:ss'}}</td>\n                <td>\n                    <button ng-if=\"!entry.healthy\" class=\"btn btn-xs btn-outline-danger\" style=\"height: 25px; font-size: 12px;\" ng-click=\"removeMachine(entry.ip, entry.port)\">移除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{machinesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"machinesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{machinesPageConfig.currentPageIndex}} / {{machinesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/metric.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">\n            <i class=\"fa fa-sort-amount-asc\" aria-hidden=\"true\" ng-show=\"!isDescOrder\"></i>\n            <i class=\"fa fa-sort-amount-desc\" aria-hidden=\"true\" ng-show=\"isDescOrder\"></i>\n            {{metricTypeDesc}}&nbsp;实时监控\n          </span>\n          <button class=\"btn btn-outline-primary\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"setDescOrder()\"\n            ng-if=\"!isDescOrder\">降序</button>\n          <button class=\"btn btn-outline-primary\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"setAscOrder()\"\n            ng-if=\"isDescOrder\">升序</button>\n\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"serviceQuery\" ng-change=\"searchService()\" style=\"margin-right: 20px;\">\n        </div>\n        <!--.tools-header -->\n        <!--<div id=\"g2container\"></div>-->\n        <div class=\"card-body\">\n          <div class=\"row\" style=\"margin-left: 3px;margin-right: 3px;\">\n            <div class=\"card-body\" style=\"padding: 0px 0px;\">\n              <div class=\"col-md-12\" ng-repeat=\"metric in metrics\" style=\"padding-left: 10px;padding-right: 10px;\">\n\n                <span ng-if=\"$index == metrics.length-1\" ng-init=\"initAllChart()\"></span>\n                <div class=\"card\" style=\"margin-bottom:20px;\" ng-if=\"$index < metrics.length-1\">\n                  <div class=\"tools-header\" style=\"height: 45px;\">\n                    <span class=\"brand\" style=\"font-weight:normal;;word-break:break-all;word-wrap:break-word;\">&nbsp{{metric.resource}}</span>\n                    <a ng-href=\"\">\n                      <i class=\"fa fa-align-justify\"></i>\n                    </a>\n                  </div>\n                  <!-- .tools-header -->\n\n                  <div class=\"card-body\">\n                    <div class=\"row\">\n                      <div class=\"col-md-6\">\n                        <!--<div ng-if=\"$index != metrics.length-1\" id=\"chart{{$index}}\"></div>-->\n                        <!--<div ng-if=\"$index == metrics.length-1\" id=\"chart{{$index}}\" ng-init=\"initAllChart()\"></div>-->\n                        <div id=\"chart{{$index}}\"></div>\n                      </div>\n                      <div class=\"col-md-6\">\n                        <table class=\"table table-hover table-striped table-bordered\">\n                          <thead>\n                            <tr style=\"font-size: 13px;text-align:center;font-weight: bold;\">\n                              <td style=\"word-wrap:break-word;word-break:break-all;\">时间</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"通过 QPS (pass qps)\">通过 QPS</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"拒绝 QPS (blocked qps)\">拒绝QPS</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"RT: 平均响应时间（毫秒）\">响应时间（ms）</td>\n                            </tr>\n                          </thead>\n                          <!-- thead -->\n                          <tbody>\n                            <tr style=\"font-size: 12px;text-align:center;\" ng-repeat=\"tableObj in metric.shortData\">\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.timestamp | date: 'HH:mm:ss'}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.passQps | number : 1}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.blockQps | number : 1}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\">{{tableObj.rt | number : 1}}</td>\n                            </tr>\n                            <tr style=\"font-size: 12px;text-align:center;\" ng-repeat=\"_tr in [] | range : (6 - metric.shortData.length)\">\n                              <td ng-repeat=\"_td in [] | range : 4\">-</td>\n                            </tr>\n                          </tbody>\n                          <!-- tbody -->\n                        </table>\n                      </div>\n                    </div>\n                  </div>\n                  <!-- .card-body -->\n                </div>\n                <!-- .card -->\n              </div>\n              <!-- .col-md-6 -->\n\n            </div>\n            <!-- .card-body -->\n          </div>\n          <!-- .row -->\n        </div>\n\n        <div style=\"display:none;\">\n          <li dir-paginate=\"empty in emptyObjs| itemsPerPage : servicePageConfig.pageSize\" current-page=\"servicePageConfig.currentPageIndex\"\n            pagination-id=\"servicesPagination\"></li>\n        </div>\n\n        <div class=\"pagination-footer\" style=\"margin-top:5px;\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"servicesPagination\"\n            on-page-change=\"pageChanged(newPageNumber)\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{emptyObjs.length}} 条记录, </span>\n            <span>\n              每页 {{servicePageConfig.pageSize}} 条记录\n              <!--每页-->\n              <!--<input class=\"form-control\" ng-model=\"servicePageConfig.pageSize\" ng-change=\"pageSizeChanged()\">-->\n              <!--条记录,-->\n            </span>\n            <span>第 {{servicePageConfig.currentPageIndex}} / {{servicePageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n        <div class=\"row\" style=\"margin-bottom:20px\"></div>\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/pagination.tpl.html",
    "content": "<div class=\"pagination\" ng-if=\"1 < pages.length || !autoHide\">\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == 1 }\" ng-click=\"setCurrent(1)\">\n    <i class=\"fa fa-angle-double-left\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == 1 }\" ng-click=\"setCurrent(pagination.current - 1)\">\n    <i class=\"fa fa-angle-left\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-repeat=\"pageNumber in pages track by tracker(pageNumber, $index)\" ng-class=\"{ active : pagination.current == pageNumber, disabled : pageNumber == '...' }\"\n    ng-click=\"setCurrent(pageNumber)\">{{ pageNumber }}</a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"\n    ng-click=\"setCurrent(pagination.current + 1)\">\n    <i class=\"fa fa-angle-right\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"\n    ng-click=\"setCurrent(pagination.last)\">\n    <i class=\"fa fa-angle-double-right\"></i>\n  </a>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/param_flow.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n      <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\" ng-if=\"!loadError\">\n      <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n        <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增热点限流规则</button>\n    </div>\n  </div>\n  \n  <div class=\"separator\"></div>\n  \n  <div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n      <div class=\"col-md-12\">\n        <div class=\"card\">\n          <div class=\"inputs-header\">\n            <span class=\"brand\" style=\"font-size: 13px;\">热点参数限流规则</span>\n            <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n            <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n            <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n              <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                placeholder=\"机器\"></selectize>\n            </div>\n          </div>\n\n            <!-- error panel -->\n            <div class=\"row clearfix\" ng-if=\"loadError\">\n                <div class=\"col-md-6 col-md-offset-3\">\n                    <div class=\"panel panel-default\">\n                        <div class=\"panel-body\">\n                            <center>\n                                <p>{{loadError.message}}</p>\n                            </center>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Table and pagination start -->\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 40%\">\n                                资源名\n                            </td>\n                            <td style=\"width: 10%;\">\n                                参数索引\n                            </td>\n                            <td style=\"width: 10%;\">\n                                流控模式\n                            </td>\n                            <td style=\"width: 10%;\">\n                                阈值\n                            </td>\n                            <td style=\"width: 8%;\">\n                                是否集群\n                            </td>\n                            <td style=\"width: 10%;\">\n                                例外项数目\n                            </td>\n                            <td style=\"width: 12%;\">\n                                操作\n                            </td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr dir-paginate=\"ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                            pagination-id=\"entriesPagination\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.resource}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.paramIdx}}</td>\n                            <td>\n                                {{ruleEntity.rule.grade == 1 ? 'QPS' : '未知'}}\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{ruleEntity.rule.count}}\n                            </td>\n                            <td>\n                                <span ng-if=\"ruleEntity.rule.clusterMode\">是</span>\n                                <span ng-if=\"!ruleEntity.rule.clusterMode\">否</span>\n                            </td>\n                            <td>\n                                {{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n                <div class=\"pagination-footer\" ng-if=\"!loadError\">\n                    <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n                                             on-page-change=\"\">\n                    </dir-pagination-controls>\n                    <div class=\"tools\" style=\"\">\n                        <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n                        <span>每页 <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录</span>\n                        <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n                    </div>\n                    <!-- .tools -->\n                </div>\n                <!-- pagination-footer -->\n            <!-- Table and pagination end -->\n\n        </div>\n        <!-- .card -->\n      </div>\n      <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n  </div>\n  <!-- .container-fluid -->\n  "
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/system.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增系统规则</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">系统规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 40%;\">\n                  单机阈值\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  <span ng-if=\"rule.avgLoad >= 0\">LOAD</span>\n                  <span ng-if=\"rule.avgRt >= 0\">RT</span>\n                  <span ng-if=\"rule.maxThread >= 0\">线程数</span>\n                  <span ng-if=\"rule.qps >= 0\">QPS</span>\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  <span ng-if=\"rule.avgLoad >= 0\">{{rule.avgLoad}}</span>\n                  <span ng-if=\"rule.avgRt >= 0\">{{rule.avgRt}}</span>\n                  <span ng-if=\"rule.maxThread >= 0\">{{rule.maxThread}}</span>\n                  <span ng-if=\"rule.qps >= 0\">{{rule.qps}}</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/dist/css/app.css",
    "content": ".chat,.timeline{list-style:none}#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner .4s linear infinite;-moz-animation:loading-bar-spinner .4s linear infinite;-ms-animation:loading-bar-spinner .4s linear infinite;-o-animation:loading-bar-spinner .4s linear infinite;animation:loading-bar-spinner .4s linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,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}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:table-cell;vertical-align:middle;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch span::before{content:\"\\200b\"}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0);visibility:hidden}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{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)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}.btn:active,.btn:focus,.selectize-input>input:focus{outline:0!important}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}.datetimepicker{border-radius:4px;direction:ltr;display:block;margin-top:1px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:320px}.datetimepicker>div{display:none}.datetimepicker .hour,.datetimepicker .minute{height:34px;line-height:34px;margin:0;width:25%}.datetimepicker .table{margin:0}.datetimepicker .table td,.datetimepicker .table th{border:0;border-radius:4px;height:20px;text-align:center}.datetimepicker .day:hover,.datetimepicker .hour:hover,.datetimepicker .left:hover,.datetimepicker .minute:hover,.datetimepicker .right:hover,.datetimepicker .switch:hover{background:#eee;cursor:pointer}.datetimepicker .disabled,.datetimepicker .disabled:hover{background:0 0;color:#ebebeb;cursor:default}.datetimepicker .active,.datetimepicker .active.disabled,.datetimepicker .active.disabled:hover,.datetimepicker .active:hover{background-color:#04c;background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;color:#fff;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#08c', endColorstr='#04c', GradientType=0);text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datetimepicker .current,.datetimepicker .current.disabled,.datetimepicker .current.disabled:hover,.datetimepicker .current:hover{background-color:#e5e5e5}.datetimepicker .active.active,.datetimepicker .active.disabled,.datetimepicker .active.disabled.active,.datetimepicker .active.disabled.disabled,.datetimepicker .active.disabled:active,.datetimepicker .active.disabled:hover,.datetimepicker .active.disabled:hover.active,.datetimepicker .active.disabled:hover.disabled,.datetimepicker .active.disabled:hover:active,.datetimepicker .active.disabled:hover:hover,.datetimepicker .active:active,.datetimepicker .active:hover,.datetimepicker .active:hover.active,.datetimepicker .active:hover.disabled,.datetimepicker .active:hover:active,.datetimepicker .active:hover:hover,.datetimepicker span.active.disabled:hover[disabled],.datetimepicker span.active.disabled[disabled],.datetimepicker span.active:hover[disabled],.datetimepicker span.active[disabled],.datetimepicker td.active.disabled:hover[disabled],.datetimepicker td.active.disabled[disabled],.datetimepicker td.active:hover[disabled],.datetimepicker td.active[disabled]{background-color:#04c}.datetimepicker span{border-radius:4px;cursor:pointer;display:block;float:left;height:54px;line-height:54px;margin:1%;width:23%}.datetimepicker span:hover{background:#eee}.datetimepicker .future,.datetimepicker .past{color:#999}.ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de}table.rz-table{table-layout:fixed;border-collapse:collapse}table.rz-table th{position:relative;min-width:25px}table.rz-table th .rz-handle{width:10px;height:100%;position:absolute;top:0;right:0;cursor:ew-resize!important}table.rz-table th .rz-handle.rz-handle-active{border-right:1px dotted #000}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;-webkit-box-shadow:inset 0 0 12px 4px #fff;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after{content:'!';visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{-webkit-box-shadow:0 2px 5px rgba(0,0,0,.2);box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#303030;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove,.selectize-input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:inline-block}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;padding:2px 0 0;border-left:1px solid #d0d0d0;-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0 0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.selectize-control,.selectize-input{position:relative}.selectize-dropdown,.selectize-input,.selectize-input input{color:#303030;font-family:inherit;font-size:13px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid #d0d0d0;padding:8px;width:100%;overflow:hidden;z-index:1;box-sizing:border-box;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.selectize-control.multi .selectize-input.has-items{padding:6px 8px 3px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input.focus{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.selectize-input.dropdown-active{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:baseline;display:-moz-inline-stack;display:inline-block;zoom:1}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#f2f2f2;color:#303030;border:0 solid #d0d0d0}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0 2px 0 0!important;text-indent:0!important;border:0!important;background:0 0!important;line-height:inherit!important;-webkit-user-select:auto!important;-webkit-box-shadow:none!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input::after{content:' ';display:block;clear:left}.selectize-input.dropdown-active::before{content:' ';display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;border:1px solid #d0d0d0;background:#fff;margin:-1px 0 0;border-top:0 none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.1);-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown .option{padding:5px 8px}.selectize-dropdown .option,.selectize-dropdown [data-disabled],.selectize-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.selectize-dropdown [data-selectable].option{opacity:1}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.selectize-dropdown .optgroup-header{color:#303030;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f5fafd;color:#495c68}.selectize-dropdown .active.create{color:#495c68}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:' ';display:block;position:absolute;top:50%;right:15px;margin-top:-3px;width:0;height:0;border-style:solid;border-width:5px 5px 0;border-color:grey transparent transparent}.selectize-control.single .selectize-input.dropdown-active:after{margin-top:-4px;border-width:0 5px 5px;border-color:transparent transparent grey}.selectize-control.rtl.single .selectize-input:after{left:15px;right:auto}.selectize-control.rtl .selectize-input>input{margin:0 4px 0 -2px!important}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}/*!\n * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)\n * Code licensed under the Apache License v2.0.\n * For details, see http://www.apache.org/licenses/LICENSE-2.0.\n */body{background-color:#f8f8f8}.example{padding:.625rem 1.825rem .625rem 2.5rem;border:1px dashed #ccc;position:relative;margin:0 0 .625rem;background-color:#fff}dl dd,dl dt{line-height:1.25rem}dl dt{font-style:normal;font-weight:700}dl dd{margin-left:.9375rem}dl.horizontal dt{float:left;width:10rem;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}dl.horizontal dd{margin-left:11.25rem}#wrapper{width:100%}#page-wrapper{min-height:568px;background-color:#fff}@media(min-width:768px){#page-wrapper{position:inherit;margin:0 0 0 250px;padding:0 30px;border-left:1px solid #e7e7e7}}.navbar-top-links{margin-right:0}.navbar-top-links li{display:inline-block}.flot-chart,.navbar-top-links .dropdown-menu li{display:block}.navbar-top-links li:last-child{margin-right:15px}.navbar-top-links li a{padding:15px;min-height:50px}.navbar-top-links .dropdown-menu li:last-child{margin-right:0}.navbar-top-links .dropdown-menu li a{padding:3px 20px;min-height:0}.navbar-top-links .dropdown-menu li a div{white-space:normal}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{width:310px;min-width:0}.navbar-top-links .dropdown-messages{margin-left:5px}.navbar-top-links .dropdown-tasks{margin-left:-59px}.navbar-top-links .dropdown-alerts{margin-left:-123px}.navbar-top-links .dropdown-user{right:0;left:auto}.sidebar .sidebar-search{padding:15px}.sidebar ul li{border-bottom:1px solid #e7e7e7}.sidebar ul li a.active{background-color:#fff;color:#fff}.sidebar .arrow{float:right}.sidebar .fa.arrow:before{content:\"\\f104\"}.sidebar .active>a>.fa.arrow:before{content:\"\\f107\"}.sidebar .nav-second-level li,.sidebar .nav-third-level li{border-bottom:0!important}.sidebar .nav-second-level li a{padding-left:37px}.sidebar .nav-third-level li a{padding-left:52px}@media(min-width:768px){.sidebar{z-index:1;position:absolute;width:250px;margin-top:51px}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{margin-left:auto}}.btn-outline{color:inherit;background-color:transparent;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.chat{margin:0;padding:0}.chat li{margin-bottom:10px;padding-bottom:5px;border-bottom:1px dotted #999}.chat li.left .chat-body{margin-left:60px}.chat li.right .chat-body{margin-right:60px}.chat li .chat-body p{margin:0}.chat .glyphicon,.panel .slidedown .glyphicon{margin-right:5px}.chat-panel .panel-body{height:350px;overflow-y:scroll}.login-panel{margin-top:25%}.flot-chart{height:400px}.flot-chart-content{width:100%;height:100%}.dataTables_wrapper{position:relative;clear:both}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background:0 0}table.dataTable thead .sorting_asc:after{content:\"\\f0de\";float:right;font-family:fontawesome}table.dataTable thead .sorting_desc:after{content:\"\\f0dd\";float:right;font-family:fontawesome}table.dataTable thead .sorting:after{content:\"\\f0dc\";float:right;font-family:fontawesome;color:rgba(50,50,50,.5)}.btn-circle{width:30px;height:30px;padding:6px 0;border-radius:15px;text-align:center;font-size:12px;line-height:1.428571429}.btn-circle.btn-lg{width:50px;height:50px;padding:10px 16px;border-radius:25px;font-size:18px;line-height:1.33}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 16px;border-radius:35px;font-size:24px;line-height:1.33}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;border:1px solid #ddd;background-color:#eee!important}.show-grid{margin:15px 0}.huge{font-size:40px}.panel-green{border-color:#5cb85c}.panel-green .panel-heading{border-color:#5cb85c;color:#fff;background-color:#5cb85c}.panel-green a{color:#5cb85c}.panel-green a:hover{color:#3d8b3d}.panel-red{border-color:#d9534f}.panel-red .panel-heading{border-color:#d9534f;color:#fff;background-color:#d9534f}.panel-red a{color:#d9534f}.panel-red a:hover{color:#b52b27}.panel-yellow{border-color:#f0ad4e}.panel-yellow .panel-heading{border-color:#f0ad4e;color:#fff;background-color:#f0ad4e}.panel-yellow a{color:#f0ad4e}.panel-yellow a:hover{color:#df8a13}.timeline{position:relative;padding:20px 0}.timeline:before{content:\" \";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background-color:#eee}.timeline>li{position:relative;margin-bottom:20px}.timeline>li:after,.timeline>li:before{content:\" \";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{float:left;position:relative;width:46%;padding:20px;border:1px solid #d4d4d4;border-radius:2px;-webkit-box-shadow:0 1px 6px rgba(0,0,0,.175);box-shadow:0 1px 6px rgba(0,0,0,.175)}.timeline>li>.timeline-panel:before{content:\" \";display:inline-block;position:absolute;top:26px;right:-15px;border-top:15px solid transparent;border-right:0 solid #ccc;border-bottom:15px solid transparent;border-left:15px solid #ccc}.timeline>li>.timeline-panel:after{content:\" \";display:inline-block;position:absolute;top:27px;right:-14px;border-top:14px solid transparent;border-right:0 solid #fff;border-bottom:14px solid transparent;border-left:14px solid #fff}.timeline>li>.timeline-badge{z-index:100;position:absolute;top:16px;left:50%;width:50px;height:50px;margin-left:-25px;border-radius:50%;text-align:center;font-size:1.4em;line-height:50px;color:#fff;background-color:#999}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline-badge.primary{background-color:#2e6da4!important}.timeline-badge.success{background-color:#3f903f!important}.timeline-badge.warning{background-color:#f0ad4e!important}.timeline-badge.danger{background-color:#d9534f!important}.timeline-badge.info{background-color:#5bc0de!important}.timeline-title{margin-top:0;color:inherit}.timeline-body>p,.timeline-body>ul{margin-bottom:0}.timeline-body>p+p{margin-top:5px}@media(max-width:767px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 90px);width:-moz-calc(100% - 90px);width:-webkit-calc(100% - 90px);float:right}ul.timeline>li>.timeline-badge{top:16px;left:15px;margin-left:0}ul.timeline>li>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}ul.timeline>li>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}}.header,.jumbotron{border-bottom:1px solid #e5e5e5}.btn{height:32px}.width-200{max-width:200px}.width-300,.witdh-300{max-width:300px}body{padding:0}.footer,.header,.marketing{padding-left:15px;padding-right:15px}.header{margin-bottom:10px}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.card .detail,.card .detail-brand{line-height:98px;text-align:center}.footer{padding-top:19px;color:#777;border-top:1px solid #e5e5e5}.container-narrow>hr{margin:30px 0}.jumbotron{text-align:center}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing{margin:40px 0}.marketing p+h4{margin-top:28px}@media screen and (min-width:768px){.container{width:inherit;margin-left:60px;margin-right:5px}.footer,.header,.marketing{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}.navbar-inverse .navbar-nav>li>a{color:#b0ddce;font-size:15px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{background-color:#1b926c}@media (min-width:900px){.navbar-right,.navbar-right~.navbar-right{margin-right:0}.navbar-left{float:left!important}.navbar-right{float:right!important}}.dropdown-menu{min-width:100px!important}.nav-sidebar li.active a{background:#DDD}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background:#1d9d74;color:#fff}.broadcast-message,.broadcast-message-preview{padding:10px;text-align:center;background:#555;color:#BBB;margin-top:50px}.card{position:relative;border:1px solid #d9d9d9;color:#666;background-color:#fff;width:100%;border-radius:5px}.card .card-header,.tools-header{border-top-left-radius:4px;border-top-right-radius:4px}.card .card-header{padding:9px 0;height:40px;background:#555;color:#fff;text-align:center}.card .card-body{padding:12px 10px}.card .card-footer{height:20px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .detail-brand{float:left;width:30%;font-size:30px;color:#fff}.card .default{background:#1d9d74}.card .info{background:#6EBEE7}.card .warn{background:#ED7F54}.card .danger{background:#6583BE}.card .detail .text-default{color:#1d9d74}.card .detail .text-info{color:#6EBEE7}.card .detail .text-warn{color:#ED7F54}.card .detail .text-danger{color:#6583BE}.card .detail{float:right;width:70%}.card .detail .text{font-size:12px}.card .detail .number{font-size:30px;font-weight:500}.h100{height:100px}.inline{display:inline}.separator{height:1px;background-color:#e5e5e5;margin-top:10px}.card>.card-body>table>tbody>tr>td,.card>.card-body>table>thead>tr>td{word-wrap:break-word;word-break:break-all}.card>.card-body>table>thead>tr>td{font-weight:500;font-size:13px;text-align:center}.card>.card-body>table>thead>tr>td>span{font-weight:500;font-size:10px}.card>.card-body>table>tbody>tr>td{font-size:12px;text-align:center}.card>.card-body>table>tbody>tr>td>a{color:#666}.thumbnails>.card>.card-body>table>tbody>tr>td,.thumbnails>.card>.card-body>table>thead>tr>td{font-size:12px;color:#777;word-wrap:break-word;word-break:break-all}.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(n+2){text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(n+2){font-weight:700;text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(1),.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(1){text-align:left}.tools-header{background:#f5f5f5;padding:9px 0;height:40px}.tools-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.tools-header .brand>a{color:#666}.tools-header>a,.tools-header>button,.tools-header>select{float:right;max-width:80px;margin:1px 10px;height:25px;padding:0 10px;line-height:25px;color:#666}.tools-header .paged{margin-right:0}.btn.btn-danger-tag{color:#fff;background-color:#d9534f;border-color:#d43f3a;line-height:1px;font-size:11px;padding:4px}.btn.btn-danger{color:#333;background-color:#fff;border-color:#ccc}.btn.btn-danger:active,.btn.btn-danger:focus,.btn.btn-danger:hover{color:#d9534f;border-color:#d9534f;background:#fff}.form-control{height:32px}.input-label:before{display:inline-block;content:\"*\";color:#f44336;font-family:SimSun;font-size:12px;-webkit-transform:TranslateX(-10px);-ms-transform:TranslateX(-10px);transform:TranslateX(-10px)}.badge-main,.label.label-main{color:#fff;background-color:#1d9d74;border-color:#1d9d74}.bootstrap-tagsinput{background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);display:inline-block;padding:4px 6px;color:#555;vertical-align:middle;border-radius:4px;width:85%;height:100px;line-height:20px;cursor:text}.bootstrap-tagsinput>.dropdown-menu{min-width:40px;font-size:12px}.bootstrap-tagsinput>.dropdown-menu>.active>a,.bootstrap-tagsinput>.dropdown-menu>.active>a:focus,.bootstrap-tagsinput>.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-o-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#1d9d74),to(#1d9d74));background-image:linear-gradient(to bottom,#1d9d74 0,#1d9d74 100%);filter:progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);background-repeat:repeat-x;color:#fff;text-decoration:none;outline:0;background-color:#1d9d74}.inputs-header{padding:9px 0;height:50px;border-top-left-radius:4px;border-top-right-radius:4px}.inputs-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.inputs-header .brand>a{color:#666}.inputs-header>input{float:right;margin:1px 10px;height:30px;padding:0 10px;color:#666}.inputs-header>a{float:right;margin:1px 10px;height:30px;padding:5 5px}.inputs-header>select{float:right;max-width:80px;margin:1px 10px;padding:0 10px;color:#666;height:25px;font-size:12px}.witdh-150{max-width:150px}.witdh-200{max-width:200px}.card.highlight{border-color:#d9534f}.card .pagination-footer{height:40px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .pagination-footer .tools{font-size:12px;margin:11px 20px 11px 0;float:right;display:inline}.card>.pagination-footer>.tools>span>input{height:25px;max-width:50px;display:inline}.pagination{display:inline-block;padding-left:0;margin:8px 0;float:right;border-radius:4px}.pagination>a{margin-right:5px;height:28px;width:28px;padding:5px 0}.datepicker>.table>tbody>tr>td,.datepicker>.table>thead>tr>td,.timepicker>.table>tbody>tr>td,.timepicker>.table>thead>tr>td{padding:5px 3px}.datepicker>.table>tbody>tr>td>.btn,.datepicker>.table>thead>tr>td>.btn,.timepicker>.table>tbody>tr>td>.btn,.timepicker>.table>thead>tr>td>.btn{border:1px solid #FFFDFD}.datepicker>.table>tbody>tr>td>.btn-default:active,.datepicker>.table>tbody>tr>td>.btn-default:focus,.datepicker>.table>tbody>tr>td>.btn-default:hover,.datepicker>.table>thead>tr>td>.btn-default:active,.datepicker>.table>thead>tr>td>.btn-default:focus,.datepicker>.table>thead>tr>td>.btn-default:hover,.timepicker>.table>tbody>tr>td>.btn-default:active,.timepicker>.table>tbody>tr>td>.btn-default:focus,.timepicker>.table>tbody>tr>td>.btn-default:hover,.timepicker>.table>thead>tr>td>.btn-default:active,.timepicker>.table>thead>tr>td>.btn-default:focus,.timepicker>.table>thead>tr>td>.btn-default:hover{color:#1d9d74;border-color:#1d9d74;background:#fff}.datepicker>.table>tbody>tr>td>a,.datepicker>.table>thead>tr>td>a,.timepicker>.table>tbody>tr>td>a,.timepicker>.table>thead>tr>td>a{height:25px;width:25px;padding:3px 0}.datepicker>.table>tbody>tr:first-child>td>a{padding:4px 0}.datepicker>.table>tbody>tr>td>a.btn.active,.datepicker>.table>thead>tr>td>a.btn.active,.timepicker>.table>tbody>tr>td>a.btn.active,.timepicker>.table>thead>tr>td>a.btn.active{color:#1d9d74;border-color:#1d9d74;background:#fff;box-shadow:inset 0 0 0 rgba(0,0,0,.125)}.datepicker>.table>thead>tr>td:not(:first-child):last-child>a,.timepicker>.table>thead>tr>td:not(:first-child):last-child>a{height:25px;width:50px;padding:5px 0}.datepicker>.table>tbody>tr>td>a,.timepicker>.table>tbody>tr>td>a{margin-left:8px}.sortorder:after{content:'\\25b2'}.sortorder.reverse:after{content:'\\25bc'}.input-control select{-moz-appearance:none;-webkit-appearance:none;appearance:none;position:relative;border:1px solid #d9d9d9;width:100%;height:100%;padding:.3125rem;z-index:0}.navbar-inverse{background-color:#337ab7;border-color:#337ab7}.sidebar{z-index:1;width:220px;top:0;left:0;height:100%}#page-wrapper{position:inherit;margin:70px 0 0 220px;padding:12px 30px;border-left:0 solid #e7e7e7}.sidebar .sidebar-nav.navbar-collapse{background-color:#F5F5F5;position:relative;color:#000;width:100%;padding:0;margin:0;list-style:none inside}.sidebar a{color:#555}.sidebar ul li:hover{color:red}.form-control{border-radius:8px}.form-control:focus,.highlight-border{border-color:#337ab7;box-shadow:0 0 0 rgba(0,0,0,.075) inset,0 0 0 rgba(29,157,116,1)}.btn-outline-primary.focus,.btn-outline-primary:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.browsehappy{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.btn.btn-main{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default-inverse,.btn-default-inverse:focus,.btn-default-inverse:hover,.btn-default:active{color:#337ab7;border-color:#337ab7;background:#fff}.btn-danger-inverse,.btn-danger-inverse:focus,.btn-danger-inverse:hover,.btn-danger:active{color:#d9534f;border-color:#d9534f;background:#fff}.btn-tab-active,.btn-tab-active:focus,.btn-tab-active:hover,.btn-tab-default:active,.btn-tab-default:focus,.btn-tab-default:hover{color:#337ab7;border-color:#337ab7;background:#fff;font-weight:600}.btn-tab-default{color:#777;background:#fff;font-weight:600}.pagination>.btn.active{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default:active,.btn-default:focus,.btn-default:hover{color:#337ab7;border-color:#337ab7;background:#fff}.bootstrap-switch.bootstrap-switch-on{border-color:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#337ab7}.selectize-input-200>.selectize-input{min-width:200px;border-color:#337ab7}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-secondary.focus,.btn-outline-secondary:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-success.focus,.btn-outline-success:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-info.focus,.btn-outline-info:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-warning.focus,.btn-outline-warning:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-danger.focus,.btn-outline-danger:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-light.focus,.btn-outline-light:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-dark.focus,.btn-outline-dark:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/dist/js/app.js",
    "content": "\"use strict\";var app;angular.module(\"sentinelDashboardApp\",[\"oc.lazyLoad\",\"ui.router\",\"ui.bootstrap\",\"angular-loading-bar\",\"ngDialog\",\"ui.bootstrap.datetimepicker\",\"ui-notification\",\"rzTable\",\"angular-clipboard\",\"selectize\",\"angularUtils.directives.dirPagination\"]).factory(\"AuthInterceptor\",[\"$window\",\"$state\",function(r,t){return{responseError:function(e){return 401==e.status&&(r.localStorage.removeItem(\"session_sentinel_admin\"),t.go(\"login\")),e},response:function(e){return e},request:function(e){return e},requestError:function(e){return e}}}]).config([\"$stateProvider\",\"$urlRouterProvider\",\"$ocLazyLoadProvider\",\"$httpProvider\",function(e,r,t,a){a.interceptors.push(\"AuthInterceptor\"),t.config({debug:!1,events:!0}),r.otherwise(\"/dashboard/home\"),e.state(\"login\",{url:\"/login\",templateUrl:\"app/views/login.html\",controller:\"LoginCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/login.js\"]})}]}}).state(\"dashboard\",{url:\"/dashboard\",templateUrl:\"app/views/dashboard/main.html\",resolve:{loadMyDirectives:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/directives/header/header.js\",\"app/scripts/directives/sidebar/sidebar.js\",\"app/scripts/directives/sidebar/sidebar-search/sidebar-search.js\"]})}]}}).state(\"dashboard.home\",{url:\"/home\",templateUrl:\"app/views/dashboard/home.html\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/main.js\"]})}]}}).state(\"dashboard.flowV1\",{templateUrl:\"app/views/flow_v1.html\",url:\"/flow/:app\",controller:\"FlowControllerV1\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/flow_v1.js\"]})}]}}).state(\"dashboard.flow\",{templateUrl:\"app/views/flow_v2.html\",url:\"/v2/flow/:app\",controller:\"FlowControllerV2\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/flow_v2.js\"]})}]}}).state(\"dashboard.paramFlow\",{templateUrl:\"app/views/param_flow.html\",url:\"/paramFlow/:app\",controller:\"ParamFlowController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/param_flow.js\"]})}]}}).state(\"dashboard.clusterAppAssignManage\",{templateUrl:\"app/views/cluster_app_assign_manage.html\",url:\"/cluster/assign_manage/:app\",controller:\"SentinelClusterAppAssignManageController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_assign_manage.js\"]})}]}}).state(\"dashboard.clusterAppServerList\",{templateUrl:\"app/views/cluster_app_server_list.html\",url:\"/cluster/server/:app\",controller:\"SentinelClusterAppServerListController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_server_list.js\"]})}]}}).state(\"dashboard.clusterAppClientList\",{templateUrl:\"app/views/cluster_app_client_list.html\",url:\"/cluster/client/:app\",controller:\"SentinelClusterAppTokenClientListController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_token_client_list.js\"]})}]}}).state(\"dashboard.clusterSingle\",{templateUrl:\"app/views/cluster_single_config.html\",url:\"/cluster/single/:app\",controller:\"SentinelClusterSingleController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_single.js\"]})}]}}).state(\"dashboard.authority\",{templateUrl:\"app/views/authority.html\",url:\"/authority/:app\",controller:\"AuthorityRuleController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/authority.js\"]})}]}}).state(\"dashboard.degrade\",{templateUrl:\"app/views/degrade.html\",url:\"/degrade/:app\",controller:\"DegradeCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/degrade.js\"]})}]}}).state(\"dashboard.system\",{templateUrl:\"app/views/system.html\",url:\"/system/:app\",controller:\"SystemCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/system.js\"]})}]}}).state(\"dashboard.machine\",{templateUrl:\"app/views/machine.html\",url:\"/app/:app\",controller:\"MachineCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/machine.js\"]})}]}}).state(\"dashboard.identity\",{templateUrl:\"app/views/identity.html\",url:\"/identity/:app\",controller:\"IdentityCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/identity.js\"]})}]}}).state(\"dashboard.metric\",{templateUrl:\"app/views/metric.html\",url:\"/metric/:app\",controller:\"MetricCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/metric.js\"]})}]}})}]),(app=angular.module(\"sentinelDashboardApp\")).filter(\"range\",[function(){return function(e,r){if(isNaN(r)||r<=0)return[];e=[];for(var t=1;t<=r;t++)e.push(t);return e}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"AuthService\",[\"$http\",function(r){this.login=function(e){return r({url:\"/auth/login\",params:e,method:\"POST\"})},this.logout=function(){return r({url:\"/auth/logout\",method:\"POST\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"AppService\",[\"$http\",function(e){this.getApps=function(){return e({url:\"app/briefinfos.json\",method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"FlowServiceV1\",[\"$http\",function(a){function r(e){return void 0===e||\"\"===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:\"/v1/flow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){e.resource,e.limitApp,e.grade,e.count,e.strategy,e.refResource,e.controlBehavior,e.warmUpPeriodSec,e.maxQueueingTimeMs,e.app,e.ip,e.port;return a({url:\"/v1/flow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,strategy:e.strategy,refResource:e.refResource,controlBehavior:e.controlBehavior,warmUpPeriodSec:e.warmUpPeriodSec,maxQueueingTimeMs:e.maxQueueingTimeMs};return a({url:\"/v1/flow/save.json\",params:r,method:\"PUT\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/v1/flow/delete.json\",params:r,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.count||e.count<0?(alert(\"限流阈值必须大于等于 0\"),!1):void 0===e.strategy||e.strategy<0?(alert(\"无效的流控模式\"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&\"\"!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert(\"无效的流控整形方式\"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert(\"预热时长必须大于 0\"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert(\"排队超时时间必须大于 0\"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert(\"集群限流配置不正确\"),!1):(alert(\"请填写关联资源或入口\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"FlowServiceV2\",[\"$http\",function(a){function r(e){return void 0===e||\"\"===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:\"/v2/flow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){return a({url:\"/v2/flow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/v2/flow/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/v2/flow/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.count||e.count<0?(alert(\"限流阈值必须大于等于 0\"),!1):void 0===e.strategy||e.strategy<0?(alert(\"无效的流控模式\"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&\"\"!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert(\"无效的流控整形方式\"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert(\"预热时长必须大于 0\"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert(\"排队超时时间必须大于 0\"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert(\"集群限流配置不正确\"),!1):(alert(\"请填写关联资源或入口\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"DegradeService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"degrade/rules.json\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,count:e.count,timeWindow:e.timeWindow,grade:e.grade,app:e.app,ip:e.ip,port:e.port};return a({url:\"/degrade/new.json\",params:r,method:\"GET\"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,timeWindow:e.timeWindow};return a({url:\"/degrade/save.json\",params:r,method:\"GET\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/degrade/delete.json\",params:r,method:\"GET\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.grade||e.grade<0?(alert(\"未知的降级策略\"),!1):void 0===e.count||\"\"===e.count||e.count<0?(alert(\"降级阈值不能为空或小于 0\"),!1):void 0===e.timeWindow||\"\"===e.timeWindow||e.timeWindow<=0?(alert(\"降级时间窗口必须大于 0\"),!1):!(1==e.grade&&1<e.count)||(alert(\"异常比率超出范围：[0.0 - 1.0]\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"SystemService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"system/rules.json\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){var r={app:e.app,ip:e.ip,port:e.port};return 0==e.grade?r.avgLoad=e.avgLoad:1==e.grade?r.avgRt=e.avgRt:2==e.grade?r.maxThread=e.maxThread:3==e.grade&&(r.qps=e.qps),a({url:\"/system/new.json\",params:r,method:\"GET\"})},this.saveRule=function(e){var r={id:e.id};return 0==e.grade?r.avgLoad=e.avgLoad:1==e.grade?r.avgRt=e.avgRt:2==e.grade?r.maxThread=e.maxThread:3==e.grade&&(r.qps=e.qps),a({url:\"/system/save.json\",params:r,method:\"GET\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/system/delete.json\",params:r,method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"MachineService\",[\"$http\",\"$httpParamSerializerJQLike\",function(a,o){this.getAppMachines=function(e){return a({url:\"app/\"+e+\"/machines.json\",method:\"GET\"})},this.removeAppMachine=function(e,r,t){return a({url:\"app/\"+e+\"/machine/remove.json\",method:\"POST\",headers:{\"Content-type\":\"application/x-www-form-urlencoded; charset=UTF-8\"},data:o({ip:r,port:t})})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"IdentityService\",[\"$http\",function(a){this.fetchIdentityOfMachine=function(e,r,t){return a({url:\"resource/machineResource.json\",params:{ip:e,port:r,searchKey:t},method:\"GET\"})},this.fetchClusterNodeOfMachine=function(e,r,t){return a({url:\"resource/machineResource.json\",params:{ip:e,port:r,type:\"cluster\",searchKey:t},method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"MetricService\",[\"$http\",function(l){this.queryAppSortedIdentities=function(e){return l({url:\"/metric/queryTopResourceMetric.json\",params:e,method:\"GET\"})},this.queryByAppAndIdentity=function(e){return l({url:\"/metric/queryByAppAndResource.json\",params:e,method:\"GET\"})},this.queryByMachineAndIdentity=function(e,r,t,a,o){var n={ip:e,port:r,identity:t,startTime:a.getTime(),endTime:o.getTime()};return l({url:\"/metric/queryByAppAndResource.json\",params:n,method:\"GET\"})}}]),angular.module(\"sentinelDashboardApp\").service(\"ParamFlowService\",[\"$http\",function(a){function o(e){return!(!function(e){return\"int\"===e||\"double\"===e||\"float\"===e||\"long\"===e||\"short\"===e}(e.classType)||!function(e){return void 0===e||\"\"===e||isNaN(e)}(e.object))||(!(!function(e){return\"byte\"===e}(e.classType)||!function(e,r,t){return void 0===e||\"\"===e||isNaN(e)||e<r||t<e}(e.object,-128,127))||(void 0===e.object||void 0===e.classType||function(e){return void 0===e||\"\"===e||isNaN(e)||e<0}(e.count)))}this.queryMachineRules=function(e,r,t){return a({url:\"/paramFlow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.addNewRule=function(e){return a({url:\"/paramFlow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/paramFlow/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/paramFlow/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){if(!e.resource||\"\"===e.resource)return alert(\"资源名称不能为空\"),!1;if(1!=e.grade)return alert(\"未知的限流模式\"),!1;if(e.count<0)return alert(\"限流阈值必须大于等于 0\"),!1;if(void 0===e.paramIdx||\"\"===e.paramIdx||isNaN(e.paramIdx)||e.paramIdx<0)return alert(\"热点参数索引必须大于等于 0\"),!1;if(void 0!==e.paramFlowItemList)for(var r=0;r<e.paramFlowItemList.length;r++){var t=e.paramFlowItemList[r];if(o(t))return alert(\"热点参数例外项不合法，请检查值和类型是否正确：参数为 \"+t.object+\", 类型为 \"+t.classType+\", 限流阈值为 \"+t.count),!1}return!0}}]),angular.module(\"sentinelDashboardApp\").service(\"AuthorityRuleService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"/authority/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.addNewRule=function(e){return a({url:\"/authority/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/authority/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/authority/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.limitApp||\"\"===e.limitApp?(alert(\"流控针对应用不能为空\"),!1):void 0!==e.strategy||(alert(\"必须选择黑白名单模式\"),!1)}}]),angular.module(\"sentinelDashboardApp\").service(\"ClusterStateService\",[\"$http\",function(a){this.fetchClusterUniversalStateSingle=function(e,r,t){return a({url:\"/cluster/state_single\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.fetchClusterUniversalStateOfApp=function(e){return a({url:\"/cluster/state/\"+e,method:\"GET\"})},this.fetchClusterServerStateOfApp=function(e){return a({url:\"/cluster/server_state/\"+e,method:\"GET\"})},this.fetchClusterClientStateOfApp=function(e){return a({url:\"/cluster/client_state/\"+e,method:\"GET\"})},this.modifyClusterConfig=function(e){return a({url:\"/cluster/config/modify_single\",data:e,method:\"POST\"})},this.applyClusterFullAssignOfApp=function(e,r){return a({url:\"/cluster/assign/all_server/\"+e,data:r,method:\"POST\"})},this.applyClusterSingleServerAssignOfApp=function(e,r){return a({url:\"/cluster/assign/single_server/\"+e,data:r,method:\"POST\"})},this.applyClusterServerBatchUnbind=function(e,r){return a({url:\"/cluster/assign/unbind_server/\"+e,data:r,method:\"POST\"})}}]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/dist/js/app.vendor.js",
    "content": "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?t(exports,require(\"angular\")):\"function\"==typeof define&&define.amd?define([\"exports\",\"angular\"],t):t((e=e||self)[\"@uirouter/angularjs\"]={},e.angular)}(this,function(g,e){\"use strict\";var t=angular,S=e&&e.module?e:t;function l(n){var e=[].slice.apply(arguments,[1]),r=n.length;return function e(t){return t.length>=r?n.apply(null,t):function(){return e(t.concat([].slice.apply(arguments)))}}(e)}function n(){var n=arguments,r=n.length-1;return function(){for(var e=r,t=n[r].apply(this,arguments);e--;)t=n[e].call(this,t);return t}}function u(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,[].slice.call(arguments).reverse())}var v=function(t){return function(e){return e&&e[t]}},y=l(function(e,t,n){return n&&n[e]===t}),C=function(e){return u.apply(null,e.split(\".\").map(v))},d=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}};function r(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,e)&&r.apply(null,e)}}function i(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,e)||r.apply(null,e)}}var a=function(n){return function(e){return e.reduce(function(e,t){return e&&!!n(t)},!0)}},s=function(n){return function(e){return e.reduce(function(e,t){return e||!!n(t)},!1)}},c=function(t){return function(e){return null!=e&&e.constructor===t||e instanceof t}},o=function(t){return function(e){return t===e}},p=function(e){return function(){return e}};function h(t,n){return function(e){return e[t].apply(e,n)}}function f(n){return function(e){for(var t=0;t<n.length;t++)if(n[t][0](e))return n[t][1](e)}}var m=Object.prototype.toString,w=function(t){return function(e){return typeof e===t}},b=w(\"undefined\"),k=d(b),$=function(e){return null===e},_=i($,b),D=w(\"function\"),x=w(\"number\"),O=w(\"string\"),T=function(e){return null!==e&&\"object\"==typeof e},A=Array.isArray,E=function(e){return\"[object Date]\"===m.call(e)},P=function(e){return\"[object RegExp]\"===m.call(e)};function M(e){if(A(e)&&e.length){var t=e.slice(0,-1),n=e.slice(-1);return!(t.filter(d(O)).length||n.filter(d(D)).length)}return D(e)}var R=r(T,u(v(\"then\"),D)),I=function(n,e){return e.reduce(function(e,t){return e[t]=function(e){return function(){throw new Error(\"No implementation for \"+e+\". The framework specific code did not implement this method.\")}}(n+\".\"+t+\"()\"),e},{})},V={$q:void 0,$injector:void 0},L=\"object\"==typeof self&&self.self===self&&self||\"object\"==typeof global&&global.global===global&&global||void 0,F=L.angular||{},j=F.fromJson||JSON.parse.bind(JSON),H=F.toJson||JSON.stringify.bind(JSON),N=F.forEach||function(t,n,e){if(A(t))return t.forEach(n,e);Object.keys(t).forEach(function(e){return n(t[e],e)})},Y=Object.assign||Oe,U=F.equals||Te;function q(e){return e}function z(){}function B(t,n,r,e,i){void 0===i&&(i=!1);var o=function(e){return t()[e].bind(r())};return(e=e||Object.keys(t())).reduce(function(e,t){return e[t]=i?function(e){return function(){return n[e]=o(e),n[e].apply(null,arguments)}}(t):o(t),e},n)}var W=function(e,t){return Y(Object.create(e),t)},G=l(K);function K(e,t){return-1!==e.indexOf(t)}var Q=l(Z);function Z(e,t){var n=e.indexOf(t);return 0<=n&&e.splice(n,1),e}var X=l(J);function J(e,t){return e.push(t),t}var ee=function(t){return t.slice().forEach(function(e){\"function\"==typeof e&&e(),Q(t,e)})};function te(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=Y.apply(void 0,[{}].concat(t.reverse()));return Y(r,ie(e||{},Object.keys(r)))}var ne=function(e,t){return Y(e,t)};function re(e,t){var n=[];for(var r in e.path){if(e.path[r]!==t.path[r])break;n.push(e.path[r])}return n}function ie(e,t){var n={};for(var r in e)-1!==t.indexOf(r)&&(n[r]=e[r]);return n}function oe(n,e){return Object.keys(n).filter(d(G(e))).reduce(function(e,t){return e[t]=n[t],e},{})}function ae(e,t){return ce(e,v(t))}function se(e,n){var t=A(e),r=t?[]:{},i=t?function(e){return r.push(e)}:function(e,t){return r[t]=e};return N(e,function(e,t){n(e,t)&&i(e,t)}),r}function ue(e,n){var r;return N(e,function(e,t){r||n(e,t)&&(r=e)}),r}var le=ce;function ce(e,n,r){return r=r||(A(e)?[]:{}),N(e,function(e,t){return r[t]=n(e,t)}),r}var de=function(t){return Object.keys(t).map(function(e){return t[e]})},pe=function(e,t){return e&&t},he=function(e,t){return e||t},fe=function(e,t){return e.concat(t)},ge=function(e,t){return A(t)?e.concat(t.reduce(ge,[])):me(e,t)};function me(e,t){return e.push(t),e}var ve=function(e,t){return G(e,t)?e:me(e,t)},ye=function(e){return e.reduce(fe,[])},we=function(e){return e.reduce(ge,[])},be=_e,$e=_e;function _e(n,r){return void 0===r&&(r=\"assert failure\"),function(e){var t=n(e);if(!t)throw new Error(D(r)?r(e):r);return t}}var Se=function(t){return Object.keys(t).map(function(e){return[e,t[e]]})};function Ce(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];if(0===e.length)return[];for(var n=e.reduce(function(e,t){return Math.min(t.length,e)},9007199254740991),r=[],i=function(t){switch(e.length){case 1:r.push([e[0][t]]);break;case 2:r.push([e[0][t],e[1][t]]);break;case 3:r.push([e[0][t],e[1][t],e[2][t]]);break;case 4:r.push([e[0][t],e[1][t],e[2][t],e[3][t]]);break;default:r.push(e.map(function(e){return e[t]}))}},o=0;o<n;o++)i(o);return r}function ke(e,t){var n,r;if(A(t)&&(n=t[0],r=t[1]),!O(n))throw new Error(\"invalid parameters to applyPairs\");return e[n]=r,e}function De(e){return e.length&&e[e.length-1]||void 0}function xe(e,t){return t&&Object.keys(t).forEach(function(e){return delete t[e]}),t||(t={}),Y(t,e)}function Oe(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];if(n)for(var r=Object.keys(n),i=0;i<r.length;i++)e[r[i]]=n[r[i]]}return e}function Te(e,t){if(e===t)return!0;if(null===e||null===t)return!1;if(e!=e&&t!=t)return!0;var n=typeof e;if(n!=typeof t||\"object\"!=n)return!1;var r=[e,t];if(a(A)(r))return function(e,t){return e.length===t.length&&Ce(e,t).reduce(function(e,t){return e&&Te(t[0],t[1])},!0)}(e,t);if(a(E)(r))return e.getTime()===t.getTime();if(a(P)(r))return e.toString()===t.toString();if(a(D)(r))return!0;if([D,A,E,P].map(s).reduce(function(e,t){return e||!!t(r)},!1))return!1;var i={};for(var o in e){if(!Te(e[o],t[o]))return!1;i[o]=!0}for(var o in t)if(!i[o])return!1;return!0}var Ae,Ee=function(e){return e.catch(function(e){return 0})&&e},Pe=function(e){return Ee(V.$q.reject(e))},Me=function(){function t(e){this.text=e,this.glob=e.split(\".\");var t=this.text.split(\".\").map(function(e){return\"**\"===e?\"(?:|(?:\\\\.[^.]*)*)\":\"*\"===e?\"\\\\.[^.]*\":\"\\\\.\"+e}).join(\"\");this.regexp=new RegExp(\"^\"+t+\"$\")}return t.is=function(e){return!!/[!,*]+/.exec(e)},t.fromString=function(e){return t.is(e)?new t(e):null},t.prototype.matches=function(e){return this.regexp.test(\".\"+e)},t}(),Re=function(){function e(e,t){void 0===e&&(e=[]),void 0===t&&(t=null),this._items=e,this._limit=t,this._evictListeners=[],this.onEvict=X(this._evictListeners)}return e.prototype.enqueue=function(e){var t=this._items;return t.push(e),this._limit&&t.length>this._limit&&this.evict(),e},e.prototype.evict=function(){var t=this._items.shift();return this._evictListeners.forEach(function(e){return e(t)}),t},e.prototype.dequeue=function(){if(this.size())return this._items.splice(0,1)[0]},e.prototype.clear=function(){var e=this._items;return this._items=[],e},e.prototype.size=function(){return this._items.length},e.prototype.remove=function(e){var t=this._items.indexOf(e);return-1<t&&this._items.splice(t,1)[0]},e.prototype.peekTail=function(){return this._items[this._items.length-1]},e.prototype.peekHead=function(){if(this.size())return this._items[0]},e}();(Ae=g.RejectType||(g.RejectType={}))[Ae.SUPERSEDED=2]=\"SUPERSEDED\",Ae[Ae.ABORTED=3]=\"ABORTED\",Ae[Ae.INVALID=4]=\"INVALID\",Ae[Ae.IGNORED=5]=\"IGNORED\",Ae[Ae.ERROR=6]=\"ERROR\";var Ie=0,Ve=function(){function r(e,t,n){this.$id=Ie++,this.type=e,this.message=t,this.detail=n}return r.isRejectionPromise=function(e){return e&&\"function\"==typeof e.then&&c(r)(e._transitionRejection)},r.superseded=function(e,t){var n=new r(g.RejectType.SUPERSEDED,\"The transition has been superseded by a different transition\",e);return t&&t.redirected&&(n.redirected=!0),n},r.redirected=function(e){return r.superseded(e,{redirected:!0})},r.invalid=function(e){return new r(g.RejectType.INVALID,\"This transition is invalid\",e)},r.ignored=function(e){return new r(g.RejectType.IGNORED,\"The transition was ignored\",e)},r.aborted=function(e){return new r(g.RejectType.ABORTED,\"The transition has been aborted\",e)},r.errored=function(e){return new r(g.RejectType.ERROR,\"The transition errored\",e)},r.normalize=function(e){return c(r)(e)?e:r.errored(e)},r.prototype.toString=function(){var e,t=(e=this.detail)&&e.toString!==Object.prototype.toString?e.toString():qe(e);return\"Transition Rejection($id: \"+this.$id+\" type: \"+this.type+\", message: \"+this.message+\", detail: \"+t+\")\"},r.prototype.toPromise=function(){return Y(Pe(this),{_transitionRejection:this})},r}();function Le(e,t){return t.length<=e?t:t.substr(0,e-3)+\"...\"}function Fe(e,t){for(;t.length<e;)t+=\" \";return t}function je(e){return e.replace(/^([A-Z])/,function(e){return e.toLowerCase()}).replace(/([A-Z])/g,function(e){return\"-\"+e.toLowerCase()})}function He(e){var t=Ne(e),n=t.match(/^(function [^ ]+\\([^)]*\\))/),r=n?n[1]:t,i=e.name||\"\";return i&&r.match(/function \\(/)?\"function \"+i+r.substr(9):r}function Ne(e){var t=A(e)?e.slice(-1)[0]:e;return t&&t.toString()||\"undefined\"}var Ye=Ve.isRejectionPromise,Ue=f([[b,p(\"undefined\")],[$,p(\"null\")],[R,p(\"[Promise]\")],[Ye,function(e){return e._transitionRejection.toString()}],[function(e){return T(e)&&!A(e)&&e.constructor!==Object&&D(e.toString)},function(e){return e.toString()}],[M,He],[p(!0),q]]);function qe(e){var t=[];function n(e){if(T(e)){if(-1!==t.indexOf(e))return\"[circular ref]\";t.push(e)}return Ue(e)}return b(e)?n(e):JSON.stringify(e,function(e,t){return n(t)}).replace(/\\\\\"/g,'\"')}var ze=function(n){return function(e){if(!e)return[\"\",\"\"];var t=e.indexOf(n);return-1===t?[e,\"\"]:[e.substr(0,t),e.substr(t+1)]}},Be=new RegExp(\"^(?:[a-z]+:)?//[^/]+/\"),We=function(e){return e.replace(/\\/[^/]*$/,\"\")},Ge=ze(\"#\"),Ke=ze(\"?\"),Qe=ze(\"=\"),Ze=function(e){return e?e.replace(/^#/,\"\"):\"\"};function Xe(e){var t=new RegExp(\"(\"+e+\")\",\"g\");return function(e){return e.split(t).filter(q)}}function Je(e,t){return O(De(e))&&O(t)?e.slice(0,-1).concat(De(e)+t):me(e,t)}function et(e){if(!e)return\"ui-view (defunct)\";var t=e.creationContext?e.creationContext.name||\"(root)\":\"(none)\";return\"[ui-view#\"+e.id+\" \"+e.$type+\":\"+e.fqn+\" (\"+e.name+\"@\"+t+\")]\"}function tt(e){return x(e)?g.Category[e]:g.Category[g.Category[e]]}var nt,rt=Function.prototype.bind.call(console.log,console),it=D(console.table)?console.table.bind(console):rt.bind(console);(nt=g.Category||(g.Category={}))[nt.RESOLVE=0]=\"RESOLVE\",nt[nt.TRANSITION=1]=\"TRANSITION\",nt[nt.HOOK=2]=\"HOOK\",nt[nt.UIVIEW=3]=\"UIVIEW\",nt[nt.VIEWCONFIG=4]=\"VIEWCONFIG\";var ot=C(\"$id\"),at=C(\"router.$id\"),st=function(e){return\"Transition #\"+ot(e)+\"-\"+at(e)},ut=function(){function e(){this._enabled={},this.approximateDigests=0}return e.prototype._set=function(t,e){var n=this;e.length||(e=Object.keys(g.Category).map(function(e){return parseInt(e,10)}).filter(function(e){return!isNaN(e)}).map(function(e){return g.Category[e]})),e.map(tt).forEach(function(e){return n._enabled[e]=t})},e.prototype.enable=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._set(!0,e)},e.prototype.disable=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._set(!1,e)},e.prototype.enabled=function(e){return!!this._enabled[tt(e)]},e.prototype.traceTransitionStart=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+\": Started  -> \"+qe(e))},e.prototype.traceTransitionIgnored=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+\": Ignored  <> \"+qe(e))},e.prototype.traceHookInvocation=function(e,t,n){if(this.enabled(g.Category.HOOK)){var r=C(\"traceData.hookType\")(n)||\"internal\",i=C(\"traceData.context.state.name\")(n)||C(\"traceData.context\")(n)||\"unknown\",o=He(e.registeredHook.callback);console.log(st(t)+\":   Hook -> \"+r+\" context: \"+i+\", \"+Le(200,o))}},e.prototype.traceHookResult=function(e,t,n){this.enabled(g.Category.HOOK)&&console.log(st(t)+\":   <- Hook returned: \"+Le(200,qe(e)))},e.prototype.traceResolvePath=function(e,t,n){this.enabled(g.Category.RESOLVE)&&console.log(st(n)+\":         Resolving \"+e+\" (\"+t+\")\")},e.prototype.traceResolvableResolved=function(e,t){this.enabled(g.Category.RESOLVE)&&console.log(st(t)+\":               <- Resolved  \"+e+\" to: \"+Le(200,qe(e.data)))},e.prototype.traceError=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+\": <- Rejected \"+qe(t)+\", reason: \"+e)},e.prototype.traceSuccess=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+\": <- Success  \"+qe(t)+\", final state: \"+e.name)},e.prototype.traceUIViewEvent=function(e,t,n){void 0===n&&(n=\"\"),this.enabled(g.Category.UIVIEW)&&console.log(\"ui-view: \"+Fe(30,e)+\" \"+et(t)+n)},e.prototype.traceUIViewConfigUpdated=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent(\"Updating\",e,\" with ViewConfig from context='\"+t+\"'\")},e.prototype.traceUIViewFill=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent(\"Fill\",e,\" with: \"+Le(200,t))},e.prototype.traceViewSync=function(e){if(this.enabled(g.Category.VIEWCONFIG)){var a=\"uiview component fqn\",t=e.map(function(e){var t,n=e.uiView,r=e.viewConfig,i=n&&n.fqn,o=r&&r.viewDecl.$context.name+\": (\"+r.viewDecl.$name+\")\";return(t={})[a]=i,t[\"view config state (view name)\"]=o,t}).sort(function(e,t){return(e[a]||\"\").localeCompare(t[a]||\"\")});it(t)}},e.prototype.traceViewServiceEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log(\"VIEWCONFIG: \"+e+\" \"+function(e){var t=e.viewDecl,n=t.$context.name||\"(root)\";return\"[View#\"+e.$id+\" from '\"+n+\"' state]: target ui-view: '\"+t.$uiViewName+\"@\"+t.$uiViewContextAnchor+\"'\"}(t))},e.prototype.traceViewServiceUIViewEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log(\"VIEWCONFIG: \"+e+\" \"+et(t))},e}(),lt=new ut,ct=function(){function e(e){this.pattern=/.*/,this.inherit=!0,Y(this,e)}return e.prototype.is=function(e,t){return!0},e.prototype.encode=function(e,t){return e},e.prototype.decode=function(e,t){return e},e.prototype.equals=function(e,t){return e==t},e.prototype.$subPattern=function(){var e=this.pattern.toString();return e.substr(1,e.length-2)},e.prototype.toString=function(){return\"{ParamType:\"+this.name+\"}\"},e.prototype.$normalize=function(e){return this.is(e)?e:this.decode(e)},e.prototype.$asArray=function(e,t){if(!e)return this;if(\"auto\"===e&&!t)throw new Error(\"'auto' array mode is for query parameters only\");return new dt(this,e)},e}();function dt(r,i){var o=this;function a(e){return A(e)?e:k(e)?[e]:[]}function s(n,r){return function(e){if(A(e)&&0===e.length)return e;var t=ce(a(e),n);return!0===r?0===se(t,function(e){return!e}).length:function(e){switch(e.length){case 0:return;case 1:return\"auto\"===i?e[0]:e;default:return e}}(t)}}function u(o){return function(e,t){var n=a(e),r=a(t);if(n.length!==r.length)return!1;for(var i=0;i<n.length;i++)if(!o(n[i],r[i]))return!1;return!0}}[\"encode\",\"decode\",\"equals\",\"$normalize\"].forEach(function(e){var t=r[e].bind(r),n=\"equals\"===e?u:s;o[e]=n(t)}),Y(this,{dynamic:r.dynamic,name:r.name,pattern:r.pattern,inherit:r.inherit,raw:r.raw,is:s(r.is.bind(r),!0),$arrayMode:i})}var pt,ht=Object.prototype.hasOwnProperty,ft=function(e){return 0===[\"value\",\"type\",\"squash\",\"array\",\"dynamic\"].filter(ht.bind(e||{})).length};function gt(e,t,n){var r=!1===n.reloadOnSearch&&t===g.DefType.SEARCH||void 0,i=ue([n.dynamic,r],k),o=k(i)?{dynamic:i}:{},a=function(e){function t(){return e.value}e=ft(e)?{value:e}:e,t.__cacheable=!0;var n=M(e.value)?e.value:t;return Y(e,{$$fn:n})}(n&&n.params&&n.params[e]);return Y(o,a)}(pt=g.DefType||(g.DefType={}))[pt.PATH=0]=\"PATH\",pt[pt.SEARCH=1]=\"SEARCH\",pt[pt.CONFIG=2]=\"CONFIG\";var mt,vt=function(){function r(e,t,n,r,i){var o=gt(e,n,i);t=function(e,t,n,r,i){if(e.type&&t&&\"string\"!==t.name)throw new Error(\"Param '\"+r+\"' has two type configurations.\");if(e.type&&t&&\"string\"===t.name&&i.type(e.type))return i.type(e.type);if(t)return t;if(e.type)return e.type instanceof ct?e.type:i.type(e.type);var o=n===g.DefType.CONFIG?\"any\":n===g.DefType.PATH?\"path\":n===g.DefType.SEARCH?\"query\":\"string\";return i.type(o)}(o,t,n,e,r.paramTypes);var a,s,u=(a={array:n===g.DefType.SEARCH&&\"auto\"},s=e.match(/\\[\\]$/)?{array:!0}:{},Y(a,s,o).array);t=u?t.$asArray(u,n===g.DefType.SEARCH):t;var l=void 0!==o.value||n===g.DefType.SEARCH,c=k(o.dynamic)?!!o.dynamic:!!t.dynamic,d=k(o.raw)?!!o.raw:!!t.raw,p=function(e,t,n){var r=e.squash;if(!t||!1===r)return!1;if(!k(r)||null==r)return n;if(!0===r||O(r))return r;throw new Error(\"Invalid squash policy: '\"+r+\"'. Valid policies: false, true, or arbitrary string\")}(o,l,r.defaultSquashPolicy()),h=function(e,t,n,r){var i=[{from:\"\",to:n||t?void 0:\"\"},{from:null,to:n||t?void 0:\"\"}],o=A(e.replace)?e.replace:[];O(r)&&o.push({from:r,to:void 0});var a=ce(o,v(\"from\"));return se(i,function(e){return-1===a.indexOf(e.from)}).concat(o)}(o,u,l,p),f=k(o.inherit)?!!o.inherit:!!t.inherit;Y(this,{id:e,type:t,location:n,isOptional:l,dynamic:c,raw:d,squash:p,replace:h,inherit:f,array:u,config:o})}return r.values=function(e,t){void 0===t&&(t={});for(var n={},r=0,i=e;r<i.length;r++){var o=i[r];n[o.id]=o.value(t[o.id])}return n},r.changed=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),e.filter(function(e){return!e.type.equals(t[e.id],n[e.id])})},r.equals=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),0===r.changed(e,t,n).length},r.validates=function(e,t){return void 0===t&&(t={}),e.map(function(e){return e.validates(t[e.id])}).reduce(pe,!0)},r.prototype.isDefaultValue=function(e){return this.isOptional&&this.type.equals(this.value(),e)},r.prototype.value=function(e){var i=this;return e=function(e){for(var t=0,n=i.replace;t<n.length;t++){var r=n[t];if(r.from===e)return r.to}return e}(e),b(e)?function(){if(i._defaultValueCache)return i._defaultValueCache.defaultValue;if(!V.$injector)throw new Error(\"Injectable functions cannot be called at configuration time\");var e=V.$injector.invoke(i.config.$$fn);if(null!=e&&!i.type.is(e))throw new Error(\"Default value (\"+e+\") for parameter '\"+i.id+\"' is not an instance of ParamType (\"+i.type.name+\")\");return i.config.$$fn.__cacheable&&(i._defaultValueCache={defaultValue:e}),e}():this.type.$normalize(e)},r.prototype.isSearch=function(){return this.location===g.DefType.SEARCH},r.prototype.validates=function(e){if((b(e)||null===e)&&this.isOptional)return!0;var t=this.type.$normalize(e);if(!this.type.is(t))return!1;var n=this.type.encode(t);return!(O(n)&&!this.type.pattern.exec(n))},r.prototype.toString=function(){return\"{Param:\"+this.id+\" \"+this.type+\" squash: '\"+this.squash+\"' optional: \"+this.isOptional+\"}\"},r}(),yt=function(){function e(){this.enqueue=!0,this.typeQueue=[],this.defaultTypes=ie(e.prototype,[\"hash\",\"string\",\"query\",\"path\",\"int\",\"bool\",\"date\",\"json\",\"any\"]);this.types=W(ce(this.defaultTypes,function(e,t){return new ct(Y({name:t},e))}),{})}return e.prototype.dispose=function(){this.types={}},e.prototype.type=function(e,t,n){if(!k(t))return this.types[e];if(this.types.hasOwnProperty(e))throw new Error(\"A type named '\"+e+\"' has already been defined.\");return this.types[e]=new ct(Y({name:e},t)),n&&(this.typeQueue.push({name:e,def:n}),this.enqueue||this._flushTypeQueue()),this},e.prototype._flushTypeQueue=function(){for(;this.typeQueue.length;){var e=this.typeQueue.shift();if(e.pattern)throw new Error(\"You cannot override a type's .pattern at runtime.\");Y(this.types[e.name],V.$injector.invoke(e.def))}},e}();mt=function(e){var t=function(e){return null!=e?e.toString():e},n={encode:t,decode:t,is:c(String),pattern:/.*/,equals:function(e,t){return e==t}};return Y({},n,e)},Y(yt.prototype,{string:mt({}),path:mt({pattern:/[^/]*/}),query:mt({}),hash:mt({inherit:!1}),int:mt({decode:function(e){return parseInt(e,10)},is:function(e){return!_(e)&&this.decode(e.toString())===e},pattern:/-?\\d+/}),bool:mt({encode:function(e){return e?1:0},decode:function(e){return 0!==parseInt(e,10)},is:c(Boolean),pattern:/0|1/}),date:mt({encode:function(e){return this.is(e)?[e.getFullYear(),(\"0\"+(e.getMonth()+1)).slice(-2),(\"0\"+e.getDate()).slice(-2)].join(\"-\"):void 0},decode:function(e){if(this.is(e))return e;var t=this.capture.exec(e);return t?new Date(t[1],t[2]-1,t[3]):void 0},is:function(e){return e instanceof Date&&!isNaN(e.valueOf())},equals:function(n,r){return[\"getFullYear\",\"getMonth\",\"getDate\"].reduce(function(e,t){return e&&n[t]()===r[t]()},!0)},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/}),json:mt({encode:H,decode:j,is:c(Object),equals:U,pattern:/[^/]*/}),any:mt({encode:q,decode:q,is:function(){return!0},equals:U})});var wt=function(){function e(e){void 0===e&&(e={}),Y(this,e)}return e.prototype.$inherit=function(e,t,n){var r,i=re(t,n),o={},a=[];for(var s in i)if(i[s]&&i[s].params&&(r=Object.keys(i[s].params)).length)for(var u in r)0<=a.indexOf(r[u])||(a.push(r[u]),o[r[u]]=this[r[u]]);return Y({},o,e)},e}(),bt=function(){function r(e){if(e instanceof r){var t=e;this.state=t.state,this.paramSchema=t.paramSchema.slice(),this.paramValues=Y({},t.paramValues),this.resolvables=t.resolvables.slice(),this.views=t.views&&t.views.slice()}else{var n=e;this.state=n,this.paramSchema=n.parameters({inherit:!1}),this.paramValues={},this.resolvables=n.resolvables.map(function(e){return e.clone()})}}return r.prototype.clone=function(){return new r(this)},r.prototype.applyRawParams=function(n){return this.paramValues=this.paramSchema.reduce(function(e,t){return ke(e,function(e){return[e.id,e.value(n[e.id])]}(t))},{}),this},r.prototype.parameter=function(e){return ue(this.paramSchema,y(\"id\",e))},r.prototype.equals=function(e,t){var n=this.diff(e,t);return n&&0===n.length},r.prototype.diff=function(e,t){if(this.state!==e.state)return!1;var n=t?t(this):this.paramSchema;return vt.changed(n,this.paramValues,e.paramValues)},r.clone=function(e){return e.clone()},r}(),$t=function(){function r(e,t,n,r){this._stateRegistry=e,this._identifier=t,this._identifier=t,this._params=Y({},n||{}),this._options=Y({},r||{}),this._definition=e.matcher.find(t,this._options.relative)}return r.prototype.name=function(){return this._definition&&this._definition.name||this._identifier},r.prototype.identifier=function(){return this._identifier},r.prototype.params=function(){return this._params},r.prototype.$state=function(){return this._definition},r.prototype.state=function(){return this._definition&&this._definition.self},r.prototype.options=function(){return this._options},r.prototype.exists=function(){return!(!this._definition||!this._definition.self)},r.prototype.valid=function(){return!this.error()},r.prototype.error=function(){var e=this.options().relative;if(this._definition||!e)return this._definition?this._definition.self?void 0:\"State '\"+this.name()+\"' has an invalid definition\":\"No such state '\"+this.name()+\"'\";var t=e.name?e.name:e;return\"Could not resolve '\"+this.name()+\"' from state '\"+t+\"'\"},r.prototype.toString=function(){return\"'\"+this.name()+\"'\"+qe(this.params())},r.prototype.withState=function(e){return new r(this._stateRegistry,e,this._params,this._options)},r.prototype.withParams=function(e,t){void 0===t&&(t=!1);var n=t?e:Y({},this._params,e);return new r(this._stateRegistry,this._identifier,n,this._options)},r.prototype.withOptions=function(e,t){void 0===t&&(t=!1);var n=t?e:Y({},this._options,e);return new r(this._stateRegistry,this._identifier,this._params,n)},r.isDef=function(e){return e&&e.state&&(O(e.state)||O(e.state.name))},r}(),_t=function(){function p(){}return p.makeTargetState=function(e,t){var n=De(t).state;return new $t(e,n,t.map(v(\"paramValues\")).reduce(ne,{}),{})},p.buildPath=function(e){var t=e.params();return e.$state().path.map(function(e){return new bt(e).applyRawParams(t)})},p.buildToPath=function(e,t){var n=p.buildPath(t);return t.options().inherit?p.inheritParams(e,n,Object.keys(t.params())):n},p.applyViewConfigs=function(i,o,t){o.filter(function(e){return G(t,e.state)}).forEach(function(t){var e=de(t.state.views||{}),n=p.subPath(o,function(e){return e===t}),r=e.map(function(e){return i.createViewConfig(n,e)});t.views=r.reduce(fe,[])})},p.inheritParams=function(o,e,a){void 0===a&&(a=[]);var s=o.map(function(e){return e.paramSchema}).reduce(fe,[]).filter(function(e){return!e.inherit}).map(v(\"id\"));return e.map(function(e){var t=Y({},e&&e.paramValues),n=ie(t,a);t=oe(t,a);var r=oe(function(e,t){var n=ue(e,y(\"state\",t));return Y({},n&&n.paramValues)}(o,e.state)||{},s),i=Y(t,r,n);return new bt(e.state).applyRawParams(i)})},p.treeChanges=function(e,r,t){for(var n,i,o,a,s,u,l=Math.min(e.length,r.length),c=0;c<l&&e[c].state!==t&&(n=e[c],i=r[c],n.equals(i,p.nonDynamicParams));)c++;a=(o=e).slice(0,c),s=o.slice(c);var d=a.map(function(e,t){var n=e.clone();return n.paramValues=r[t].paramValues,n});return u=r.slice(c),{from:o,to:d.concat(u),retained:a,retainedWithToParams:d,exiting:s,entering:u}},p.matching=function(e,t,i){var o=!1;return Ce(e,t).reduce(function(e,t){var n=t[0],r=t[1];return(o=o||!n.equals(r,i))?e:e.concat(n)},[])},p.equals=function(e,t,n){return e.length===t.length&&p.matching(e,t,n).length===e.length},p.subPath=function(e,t){var n=ue(e,t),r=e.indexOf(n);return-1===r?void 0:e.slice(0,r+1)},p.nonDynamicParams=function(e){return e.state.parameters({inherit:!1}).filter(function(e){return!e.dynamic})},p.paramValues=function(e){return e.reduce(function(e,t){return Y(e,t.paramValues)},{})},p}(),St={when:{LAZY:\"LAZY\",EAGER:\"EAGER\"},async:{WAIT:\"WAIT\",NOWAIT:\"NOWAIT\",RXWAIT:\"RXWAIT\"}},Ct={when:\"LAZY\",async:\"WAIT\"},kt=function(){function a(e,t,n,r,i){if(this.resolved=!1,this.promise=void 0,e instanceof a)Y(this,e);else if(D(t)){if(_(e))throw new Error(\"new Resolvable(): token argument is required\");if(!D(t))throw new Error(\"new Resolvable(): resolveFn argument must be a function\");this.token=e,this.policy=r,this.resolveFn=t,this.deps=n||[],this.data=i,this.resolved=void 0!==i,this.promise=this.resolved?V.$q.when(this.data):void 0}else if(T(e)&&e.token&&(e.hasOwnProperty(\"resolveFn\")||e.hasOwnProperty(\"data\"))){var o=e;return new a(o.token,o.resolveFn,o.deps,o.policy,o.data)}}return a.prototype.getPolicy=function(e){var t=this.policy||{},n=e&&e.resolvePolicy||{};return{when:t.when||n.when||Ct.when,async:t.async||n.async||Ct.async}},a.prototype.resolve=function(t,n){var r=this,e=V.$q,i=t.findNode(this),o=i&&i.state,a=\"RXWAIT\"===this.getPolicy(o).async?function(e){var t=e.cache(1);return t.take(1).toPromise().then(function(){return t})}:q;return this.promise=e.when().then(function(){return e.all(t.getDependencies(r).map(function(e){return e.get(t,n)}))}).then(function(e){return r.resolveFn.apply(null,e)}).then(a).then(function(e){return r.data=e,r.resolved=!0,r.resolveFn=null,lt.traceResolvableResolved(r,n),r.data})},a.prototype.get=function(e,t){return this.promise||this.resolve(e,t)},a.prototype.toString=function(){return\"Resolvable(token: \"+qe(this.token)+\", requires: [\"+this.deps.map(qe)+\"])\"},a.prototype.clone=function(){return new a(this)},a.fromData=function(e,t){return new a(e,function(){return t},null,null,t)},a}(),Dt=St.when,xt=[Dt.EAGER,Dt.LAZY],Ot=[Dt.EAGER],Tt=\"Native Injector\",At=function(){function e(e){this._path=e}return e.prototype.getTokens=function(){return this._path.reduce(function(e,t){return e.concat(t.resolvables.map(function(e){return e.token}))},[]).reduce(ve,[])},e.prototype.getResolvable=function(t){return De(this._path.map(function(e){return e.resolvables}).reduce(fe,[]).filter(function(e){return e.token===t}))},e.prototype.getPolicy=function(e){var t=this.findNode(e);return e.getPolicy(t.state)},e.prototype.subContext=function(t){return new e(_t.subPath(this._path,function(e){return e.state===t}))},e.prototype.addResolvables=function(e,t){var n=ue(this._path,y(\"state\",t)),r=e.map(function(e){return e.token});n.resolvables=n.resolvables.filter(function(e){return-1===r.indexOf(e.token)}).concat(e)},e.prototype.resolvePath=function(e,s){var u=this;void 0===e&&(e=\"LAZY\");var l=(G(xt,e)?e:\"LAZY\")===St.when.EAGER?Ot:xt;lt.traceResolvePath(this._path,e,s);var c=function(t,n){return function(e){return G(t,u.getPolicy(e)[n])}},t=this._path.reduce(function(e,t){var n=t.resolvables.filter(c(l,\"when\")),r=n.filter(c([\"NOWAIT\"],\"async\")),i=n.filter(d(c([\"NOWAIT\"],\"async\"))),o=u.subContext(t.state),a=function(t){return t.get(o,s).then(function(e){return{token:t.token,value:e}})};return r.forEach(a),e.concat(i.map(a))},[]);return V.$q.all(t)},e.prototype.injector=function(){return this._injector||(this._injector=new Et(this))},e.prototype.findNode=function(t){return ue(this._path,function(e){return G(e.resolvables,t)})},e.prototype.getDependencies=function(t){var r=this,n=this.findNode(t),i=(_t.subPath(this._path,function(e){return e===n})||this._path).reduce(function(e,t){return e.concat(t.resolvables)},[]).filter(function(e){return e!==t});return t.deps.map(function(t){var e=i.filter(function(e){return e.token===t});if(e.length)return De(e);var n=r.injector().getNative(t);if(b(n))throw new Error(\"Could not find Dependency Injection token: \"+qe(t));return new kt(t,function(){return n},[],n)})},e}(),Et=function(){function e(e){this.context=e,this.native=this.get(Tt)||V.$injector}return e.prototype.get=function(e){var t=this.context.getResolvable(e);if(t){if(\"NOWAIT\"===this.context.getPolicy(t).async)return t.get(this.context);if(!t.resolved)throw new Error(\"Resolvable async .get() not complete:\"+qe(t.token));return t.data}return this.getNative(e)},e.prototype.getAsync=function(e){var t=this.context.getResolvable(e);return t?t.get(this.context):V.$q.when(this.native.get(e))},e.prototype.getNative=function(e){return this.native&&this.native.get(e)},e}();function Pt(e){return e.name}function Mt(e){return e.self.$$state=function(){return e},e.self}function Rt(e){return e.parent&&e.parent.data&&(e.data=e.self.data=W(e.parent.data,e.data)),e.data}var It=function(a,s){return function(e){var t=e.self;if(t&&t.url&&t.name&&t.name.match(/\\.\\*\\*$/)){var n={};xe(t,n),n.url+=\"{remainder:any}\",t=n}var r=e.parent,i=function(e){if(!O(e))return!1;var t=\"^\"===e.charAt(0);return{val:t?e.substring(1):e,root:t}}(t.url),o=i?a.compile(i.val,{state:t}):t.url;if(!o)return null;if(!a.isMatcher(o))throw new Error(\"Invalid url '\"+o+\"' in state '\"+e+\"'\");return i&&i.root?o:(r&&r.navigable||s()).url.append(o)}};function Vt(e){return e.parent?e.parent.path.concat(e):[e]}function Lt(e){var t=e.parent?Y({},e.parent.includes):{};return t[e.name]=!0,t}function Ft(e){var t,n,r=function(e){return e.provide||e.token},i=f([[v(\"resolveFn\"),function(e){return new kt(r(e),e.resolveFn,e.deps,e.policy)}],[v(\"useFactory\"),function(e){return new kt(r(e),e.useFactory,e.deps||e.dependencies,e.policy)}],[v(\"useClass\"),function(e){return new kt(r(e),function(){return new e.useClass},[],e.policy)}],[v(\"useValue\"),function(e){return new kt(r(e),function(){return e.useValue},[],e.policy,e.useValue)}],[v(\"useExisting\"),function(e){return new kt(r(e),q,[e.useExisting],e.policy)}]]),o=f([[u(v(\"val\"),O),function(e){return new kt(e.token,q,[e.val],e.policy)}],[u(v(\"val\"),A),function(e){return new kt(e.token,De(e.val),e.val.slice(0,-1),e.policy)}],[u(v(\"val\"),D),function(e){return new kt(e.token,e.val,function(e){var t=V.$injector;return e.$inject||t&&t.annotate(e,t.strictDi)||\"deferred\"}(e.val),e.policy)}]]),a=f([[c(kt),function(e){return e}],[function(e){return!(!e.token||!e.resolveFn)},i],[function(e){return!(!e.provide&&!e.token||!(e.useValue||e.useFactory||e.useExisting||e.useClass))},i],[function(e){return!!(e&&e.val&&(O(e.val)||A(e.val)||D(e.val)))},o],[p(!0),function(e){throw new Error(\"Invalid resolve value: \"+qe(e))}]]),s=e.resolve;return(A(s)?s:(t=s,n=e.resolvePolicy||{},Object.keys(t||{}).map(function(e){return{token:e,val:t[e],deps:void 0,policy:n[e]}}))).map(a)}var jt,Ht,Nt=function(){function e(t,e){this.matcher=t;var n=this,r=function(){return t.find(\"\")},i=function(e){return\"\"===e.name};this.builders={name:[Pt],self:[Mt],parent:[function(e){return i(e)?null:t.find(n.parentName(e))||r()}],data:[Rt],url:[It(e,r)],navigable:[function(t){return function(e){return!t(e)&&e.url?e:e.parent?e.parent.navigable:null}}(i)],params:[function(r){return function(n){var e=n.url&&n.url.parameters({inherit:!1})||[],t=de(le(oe(n.params||{},e.map(v(\"id\"))),function(e,t){return r.fromConfig(t,null,n.self)}));return e.concat(t).map(function(e){return[e.id,e]}).reduce(ke,{})}}(e.paramFactory)],views:[],path:[Vt],includes:[Lt],resolvables:[Ft]}}return e.prototype.builder=function(e,t){var n=this.builders,r=n[e]||[];return O(e)&&!k(t)?1<r.length?r:r[0]:O(e)&&D(t)?(n[e]=r,n[e].push(t),function(){return n[e].splice(n[e].indexOf(t,1))&&null}):void 0},e.prototype.build=function(e){var t=this.matcher,n=this.builders,r=this.parentName(e);if(r&&!t.find(r,void 0,!1))return null;for(var i in n)if(n.hasOwnProperty(i)){var o=n[i].reduce(function(t,n){return function(e){return n(e,t)}},z);e[i]=o(e)}return e},e.prototype.parentName=function(e){var t=e.name||\"\",n=t.split(\".\");if(\"**\"===n.pop()&&n.pop(),n.length){if(e.parent)throw new Error(\"States that specify the 'parent:' property should not have a '.' in their name (\"+t+\")\");return n.join(\".\")}return e.parent?O(e.parent)?e.parent:e.parent.name:\"\"},e.prototype.name=function(e){var t=e.name;if(-1!==t.indexOf(\".\")||!e.parent)return t;var n=O(e.parent)?e.parent:e.parent.name;return n?n+\".\"+t:t},e}(),Yt=function(){function n(e){return n.create(e||{})}return n.create=function(e){e=n.isStateClass(e)?new e:e;var t=W(W(e,n.prototype));return e.$$state=function(){return t},t.self=e,t.__stateObjectCache={nameGlob:Me.fromString(t.name)},t},n.prototype.is=function(e){return this===e||this.self===e||this.fqn()===e},n.prototype.fqn=function(){if(!(this.parent&&this.parent instanceof this.constructor))return this.name;var e=this.parent.fqn();return e?e+\".\"+this.name:this.name},n.prototype.root=function(){return this.parent&&this.parent.root()||this},n.prototype.parameters=function(t){return((t=te(t,{inherit:!0,matchingKeys:null})).inherit&&this.parent&&this.parent.parameters()||[]).concat(de(this.params)).filter(function(e){return!t.matchingKeys||t.matchingKeys.hasOwnProperty(e.id)})},n.prototype.parameter=function(e,t){return void 0===t&&(t={}),this.url&&this.url.parameter(e,t)||ue(de(this.params),y(\"id\",e))||t.inherit&&this.parent&&this.parent.parameter(e)},n.prototype.toString=function(){return this.fqn()},n.isStateClass=function(e){return D(e)&&!0===e.__uiRouterState},n.isState=function(e){return T(e.__stateObjectCache)},n}(),Ut=function(){function e(e){this._states=e}return e.prototype.isRelative=function(e){return 0===(e=e||\"\").indexOf(\".\")||0===e.indexOf(\"^\")},e.prototype.find=function(e,t,n){if(void 0===n&&(n=!0),e||\"\"===e){var r=O(e),i=r?e:e.name;this.isRelative(i)&&(i=this.resolvePath(i,t));var o=this._states[i];if(o&&(r||!(r||o!==e&&o.self!==e)))return o;if(r&&n){var a=de(this._states).filter(function(e){return e.__stateObjectCache.nameGlob&&e.__stateObjectCache.nameGlob.matches(i)});return 1<a.length&&console.log(\"stateMatcher.find: Found multiple matches for \"+i+\" using glob: \",a.map(function(e){return e.name})),a[0]}}},e.prototype.resolvePath=function(e,t){if(!t)throw new Error(\"No reference point given for path '\"+e+\"'\");for(var n=this.find(t),r=e.split(\".\"),i=r.length,o=0,a=n;o<i;o++)if(\"\"!==r[o]||0!==o){if(\"^\"!==r[o])break;if(!a.parent)throw new Error(\"Path '\"+e+\"' not valid for state '\"+n.name+\"'\");a=a.parent}else a=n;var s=r.slice(o).join(\".\");return a.name+(a.name&&s?\".\":\"\")+s},e}(),qt=function(){function e(e,t,n,r){this.router=e,this.states=t,this.builder=n,this.listeners=r,this.queue=[]}return e.prototype.dispose=function(){this.queue=[]},e.prototype.register=function(e){var t=this.queue,n=Yt.create(e),r=n.name;if(!O(r))throw new Error(\"State must have a valid name\");if(this.states.hasOwnProperty(r)||G(t.map(v(\"name\")),r))throw new Error(\"State '\"+r+\"' is already defined\");return t.push(n),this.flush(),n},e.prototype.flush=function(){for(var t=this,e=this.queue,n=this.states,r=this.builder,i=[],o=[],a={},s=function(e){return t.states.hasOwnProperty(e)&&t.states[e]},u=function(){i.length&&t.listeners.forEach(function(e){return e(\"registered\",i.map(function(e){return e.self}))})};0<e.length;){var l=e.shift(),c=l.name,d=r.build(l),p=o.indexOf(l);if(d){var h=s(c);if(h&&h.name===c)throw new Error(\"State '\"+c+\"' is already defined\");var f=s(c+\".**\");f&&this.router.stateRegistry.deregister(f),n[c]=l,this.attachRoute(l),0<=p&&o.splice(p,1),i.push(l)}else{var g=a[c];if(a[c]=e.length,0<=p&&g===e.length)return e.push(l),u(),n;p<0&&o.push(l),e.push(l)}}return u(),n},e.prototype.attachRoute=function(e){if(!e.abstract&&e.url){var t=this.router.urlService.rules;t.rule(t.urlRuleFactory.create(e))}},e}(),zt=function(){function e(e){this.router=e,this.states={},this.listeners=[],this.matcher=new Ut(this.states),this.builder=new Nt(this.matcher,e.urlMatcherFactory),this.stateQueue=new qt(e,this.states,this.builder,this.listeners),this._registerRoot()}return e.prototype._registerRoot=function(){(this._root=this.stateQueue.register({name:\"\",url:\"^\",views:null,params:{\"#\":{value:null,type:\"hash\",dynamic:!0}},abstract:!0})).navigable=null},e.prototype.dispose=function(){var t=this;this.stateQueue.dispose(),this.listeners=[],this.get().forEach(function(e){return t.get(e)&&t.deregister(e)})},e.prototype.onStatesChanged=function(e){return this.listeners.push(e),function(){Q(this.listeners)(e)}.bind(this)},e.prototype.root=function(){return this._root},e.prototype.register=function(e){return this.stateQueue.register(e)},e.prototype._deregisterTree=function(e){var n=this,r=this.get().map(function(e){return e.$$state()}),i=function(t){var e=r.filter(function(e){return-1!==t.indexOf(e.parent)});return 0===e.length?e:e.concat(i(e))},t=i([e]),o=[e].concat(t).reverse();return o.forEach(function(e){var t=n.router.urlService.rules;t.rules().filter(y(\"state\",e)).forEach(function(e){return t.removeRule(e)}),delete n.states[e.name]}),o},e.prototype.deregister=function(e){var t=this.get(e);if(!t)throw new Error(\"Can't deregister state; not found: \"+e);var n=this._deregisterTree(t.$$state());return this.listeners.forEach(function(e){return e(\"deregistered\",n.map(function(e){return e.self}))}),n},e.prototype.get=function(e,t){var n=this;if(0===arguments.length)return Object.keys(this.states).map(function(e){return n.states[e].self});var r=this.matcher.find(e,t);return r&&r.self||null},e.prototype.decorator=function(e,t){return this.builder.builder(e,t)},e}();(jt=g.TransitionHookPhase||(g.TransitionHookPhase={}))[jt.CREATE=0]=\"CREATE\",jt[jt.BEFORE=1]=\"BEFORE\",jt[jt.RUN=2]=\"RUN\",jt[jt.SUCCESS=3]=\"SUCCESS\",jt[jt.ERROR=4]=\"ERROR\",(Ht=g.TransitionHookScope||(g.TransitionHookScope={}))[Ht.TRANSITION=0]=\"TRANSITION\",Ht[Ht.STATE=1]=\"STATE\";var Bt={current:z,transition:null,traceData:{},bind:null},Wt=function(){function o(e,t,n,r){var i=this;this.transition=e,this.stateContext=t,this.registeredHook=n,this.options=r,this.isSuperseded=function(){return i.type.hookPhase===g.TransitionHookPhase.RUN&&!i.options.transition.isActive()},this.options=te(r,Bt),this.type=n.eventType}return o.chain=function(e,t){return e.reduce(function(e,t){return e.then(function(){return t.invokeHook()})},t||V.$q.when())},o.invokeHooks=function(e,t){for(var n=0;n<e.length;n++){var r=e[n].invokeHook();if(R(r)){var i=e.slice(n+1);return o.chain(i,r).then(t)}}return t()},o.runAllHooks=function(e){e.forEach(function(e){return e.invokeHook()})},o.prototype.logError=function(e){this.transition.router.stateService.defaultErrorHandler()(e)},o.prototype.invokeHook=function(){var t=this,n=this.registeredHook;if(!n._deregistered){var e=this.getNotCurrentRejection();if(e)return e;var r=this.options;lt.traceHookInvocation(this,this.transition,r);var i=function(e){return n.eventType.getErrorHandler(t)(e)},o=function(e){return n.eventType.getResultHandler(t)(e)};try{var a=n.callback.call(r.bind,t.transition,t.stateContext);return!this.type.synchronous&&R(a)?a.catch(function(e){return Ve.normalize(e).toPromise()}).then(o,i):o(a)}catch(e){return i(Ve.normalize(e))}finally{n.invokeLimit&&++n.invokeCount>=n.invokeLimit&&n.deregister()}}},o.prototype.handleHookResult=function(e){var t=this,n=this.getNotCurrentRejection();return n||(R(e)?e.then(function(e){return t.handleHookResult(e)}):(lt.traceHookResult(e,this.transition,this.options),!1===e?Ve.aborted(\"Hook aborted transition\").toPromise():c($t)(e)?Ve.redirected(e).toPromise():void 0))},o.prototype.getNotCurrentRejection=function(){var e=this.transition.router;return e._disposed?Ve.aborted(\"UIRouter instance #\"+e.$id+\" has been stopped (disposed)\").toPromise():this.transition._aborted?Ve.aborted().toPromise():this.isSuperseded()?Ve.superseded(this.options.current()).toPromise():void 0},o.prototype.toString=function(){var e=this.options,t=this.registeredHook;return(C(\"traceData.hookType\")(e)||\"internal\")+\" context: \"+(C(\"traceData.context.state.name\")(e)||C(\"traceData.context\")(e)||\"unknown\")+\", \"+Le(200,Ne(t.callback))},o.HANDLE_RESULT=function(t){return function(e){return t.handleHookResult(e)}},o.LOG_REJECTED_RESULT=function(t){return function(e){R(e)&&e.catch(function(e){return t.logError(Ve.normalize(e))})}},o.LOG_ERROR=function(t){return function(e){return t.logError(e)}},o.REJECT_ERROR=function(e){return function(e){return Pe(e)}},o.THROW_ERROR=function(e){return function(e){throw e}},o}();function Gt(e,t,n){var i=O(t)?[t]:t;return!!(D(i)?i:function(e){for(var t=i,n=0;n<t.length;n++){var r=new Me(t[n]);if(r&&r.matches(e.name)||!r&&t[n]===e.name)return!0}return!1})(e,n)}var Kt=function(){function e(e,t,n,r,i,o){void 0===o&&(o={}),this.tranSvc=e,this.eventType=t,this.callback=n,this.matchCriteria=r,this.removeHookFromRegistry=i,this.invokeCount=0,this._deregistered=!1,this.priority=o.priority||0,this.bind=o.bind||null,this.invokeLimit=o.invokeLimit}return e.prototype._matchingNodes=function(e,t,n){if(!0===t)return e;var r=e.filter(function(e){return Gt(e.state,t,n)});return r.length?r:null},e.prototype._getDefaultMatchCriteria=function(){return le(this.tranSvc._pluginapi._getPathTypes(),function(){return!0})},e.prototype._getMatchingNodes=function(o,a){var s=this,u=Y(this._getDefaultMatchCriteria(),this.matchCriteria);return de(this.tranSvc._pluginapi._getPathTypes()).reduce(function(e,t){var n=t.scope===g.TransitionHookScope.STATE,r=o[t.name]||[],i=n?r:[De(r)];return e[t.name]=s._matchingNodes(i,u[t.name],a),e},{})},e.prototype.matches=function(e,t){var n=this._getMatchingNodes(e,t);return de(n).every(q)?n:null},e.prototype.deregister=function(){this.removeHookFromRegistry(this),this._deregistered=!0},e}();function Qt(e,i,o){var a=(e._registeredHooks=e._registeredHooks||{})[o.name]=[],s=Q(a);function t(e,t,n){void 0===n&&(n={});var r=new Kt(i,o,t,e,s,n);return a.push(r),r.deregister.bind(r)}return e[o.name]=t}var Zt=function(){function e(e){this.transition=e}return e.prototype.buildHooksForPhase=function(e){var t=this;return this.transition.router.transitionService._pluginapi._getEvents(e).map(function(e){return t.buildHooks(e)}).reduce(fe,[]).filter(q)},e.prototype.buildHooks=function(o){var a=this.transition,e=a.treeChanges(),t=this.getMatchingHooks(o,e,a);if(!t)return[];var s={transition:a,current:a.options().current};return t.map(function(i){return i.matches(e,a)[o.criteriaMatchPath.name].map(function(e){var t=Y({bind:i.bind,traceData:{hookType:o.name,context:e}},s),n=o.criteriaMatchPath.scope===g.TransitionHookScope.STATE?e.state.self:null,r=new Wt(a,n,i,t);return{hook:i,node:e,transitionHook:r}})}).reduce(fe,[]).sort(function(i){void 0===i&&(i=!1);return function(e,t){var n=i?-1:1,r=(e.node.state.path.length-t.node.state.path.length)*n;return 0!=r?r:t.hook.priority-e.hook.priority}}(o.reverseSort)).map(function(e){return e.transitionHook})},e.prototype.getMatchingHooks=function(t,n,r){var e=t.hookPhase===g.TransitionHookPhase.CREATE,i=this.transition.router.transitionService;return(e?[i]:[this.transition,i]).map(function(e){return e.getHooks(t.name)}).filter(be(A,\"broken event named: \"+t.name)).reduce(fe,[]).filter(function(e){return e.matches(n,r)})},e}();var Xt=v(\"self\"),Jt=function(){function t(e,t,n){var r=this;if(this._deferred=V.$q.defer(),this.promise=this._deferred.promise,this._registeredHooks={},this._hookBuilder=new Zt(this),this.isActive=function(){return r.router.globals.transition===r},this.router=n,!(this._targetState=t).valid())throw new Error(t.error());this._options=Y({current:p(this)},t.options()),this.$id=n.transitionService._transitionCount++;var i=_t.buildToPath(e,t);this._treeChanges=_t.treeChanges(e,i,this._options.reloadState),this.createTransitionHookRegFns();var o=this._hookBuilder.buildHooksForPhase(g.TransitionHookPhase.CREATE);Wt.invokeHooks(o,function(){return null}),this.applyViewConfigs(n)}return t.prototype.onBefore=function(e,t,n){},t.prototype.onStart=function(e,t,n){},t.prototype.onExit=function(e,t,n){},t.prototype.onRetain=function(e,t,n){},t.prototype.onEnter=function(e,t,n){},t.prototype.onFinish=function(e,t,n){},t.prototype.onSuccess=function(e,t,n){},t.prototype.onError=function(e,t,n){},t.prototype.createTransitionHookRegFns=function(){var t=this;this.router.transitionService._pluginapi._getEvents().filter(function(e){return e.hookPhase!==g.TransitionHookPhase.CREATE}).forEach(function(e){return Qt(t,t.router.transitionService,e)})},t.prototype.getHooks=function(e){return this._registeredHooks[e]},t.prototype.applyViewConfigs=function(e){var t=this._treeChanges.entering.map(function(e){return e.state});_t.applyViewConfigs(e.transitionService.$view,this._treeChanges.to,t)},t.prototype.$from=function(){return De(this._treeChanges.from).state},t.prototype.$to=function(){return De(this._treeChanges.to).state},t.prototype.from=function(){return this.$from().self},t.prototype.to=function(){return this.$to().self},t.prototype.targetState=function(){return this._targetState},t.prototype.is=function(e){return e instanceof t?this.is({to:e.$to().name,from:e.$from().name}):!(e.to&&!Gt(this.$to(),e.to,this)||e.from&&!Gt(this.$from(),e.from,this))},t.prototype.params=function(e){return void 0===e&&(e=\"to\"),Object.freeze(this._treeChanges[e].map(v(\"paramValues\")).reduce(ne,{}))},t.prototype.paramsChanged=function(){var e=this.params(\"from\"),n=this.params(\"to\"),t=[].concat(this._treeChanges.to).concat(this._treeChanges.from).map(function(e){return e.paramSchema}).reduce(ge,[]).reduce(ve,[]);return vt.changed(t,e,n).reduce(function(e,t){return e[t.id]=n[t.id],e},{})},t.prototype.injector=function(t,e){void 0===e&&(e=\"to\");var n=this._treeChanges[e];return t&&(n=_t.subPath(n,function(e){return e.state===t||e.state.name===t})),new At(n).injector()},t.prototype.getResolveTokens=function(e){return void 0===e&&(e=\"to\"),new At(this._treeChanges[e]).getTokens()},t.prototype.addResolvable=function(e,t){void 0===t&&(t=\"\"),e=c(kt)(e)?e:new kt(e);var n=\"string\"==typeof t?t:t.name,r=this._treeChanges.to,i=ue(r,function(e){return e.state.name===n});new At(r).addResolvables([e],i.state)},t.prototype.redirectedFrom=function(){return this._options.redirectedFrom||null},t.prototype.originalTransition=function(){var e=this.redirectedFrom();return e&&e.originalTransition()||this},t.prototype.options=function(){return this._options},t.prototype.entering=function(){return ce(this._treeChanges.entering,v(\"state\")).map(Xt)},t.prototype.exiting=function(){return ce(this._treeChanges.exiting,v(\"state\")).map(Xt).reverse()},t.prototype.retained=function(){return ce(this._treeChanges.retained,v(\"state\")).map(Xt)},t.prototype.views=function(e,t){void 0===e&&(e=\"entering\");var n=this._treeChanges[e];return(n=t?n.filter(y(\"state\",t)):n).map(v(\"views\")).filter(q).reduce(fe,[])},t.prototype.treeChanges=function(e){return e?this._treeChanges[e]:this._treeChanges},t.prototype.redirect=function(e){for(var t=1,n=this;null!=(n=n.redirectedFrom());)if(20<++t)throw new Error(\"Too many consecutive Transition redirects (20+)\");var r={redirectedFrom:this,source:\"redirect\"};\"url\"===this.options().source&&!1!==e.options().location&&(r.location=\"replace\");var i=Y({},this.options(),e.options(),r);e=e.withOptions(i,!0);var o,a=this.router.transitionService.create(this._treeChanges.from,e),s=this._treeChanges.entering,u=a._treeChanges.entering;return _t.matching(u,s,_t.nonDynamicParams).filter(d((o=e.options().reloadState,function(e){return o&&e.state.includes[o.name]}))).forEach(function(e,t){e.resolvables=s[t].resolvables}),a},t.prototype._changedParams=function(){var e=this._treeChanges;if(!this._options.reload&&(!e.exiting.length&&!e.entering.length&&e.to.length===e.from.length&&!Ce(e.to,e.from).map(function(e){return e[0].state!==e[1].state}).reduce(he,!1))){var t=e.to.map(function(e){return e.paramSchema}),n=[e.to,e.from].map(function(e){return e.map(function(e){return e.paramValues})});return Ce(t,n[0],n[1]).map(function(e){var t=e[0],n=e[1],r=e[2];return vt.changed(t,n,r)}).reduce(fe,[])}},t.prototype.dynamic=function(){var e=this._changedParams();return!!e&&e.map(function(e){return e.dynamic}).reduce(he,!1)},t.prototype.ignored=function(){return!!this._ignoredReason()},t.prototype._ignoredReason=function(){var e=this.router.globals.transition,r=this._options.reloadState,t=function(e,t){if(e.length!==t.length)return!1;var n=_t.matching(e,t);return e.length===n.filter(function(e){return!r||!e.state.includes[r.name]}).length},n=this.treeChanges(),i=e&&e.treeChanges();return i&&t(i.to,n.to)&&t(i.exiting,n.exiting)?\"SameAsPending\":0===n.exiting.length&&0===n.entering.length&&t(n.from,n.to)?\"SameAsCurrent\":void 0},t.prototype.run=function(){var t=this,n=Wt.runAllHooks,r=function(e){return t._hookBuilder.buildHooksForPhase(e)},e=r(g.TransitionHookPhase.BEFORE);return Wt.invokeHooks(e,function(){var e=t.router.globals;return e.lastStartedTransitionId=t.$id,e.transition=t,e.transitionHistory.enqueue(t),lt.traceTransitionStart(t),V.$q.when(void 0)}).then(function(){var e=r(g.TransitionHookPhase.RUN);return Wt.invokeHooks(e,function(){return V.$q.when(void 0)})}).then(function(){lt.traceSuccess(t.$to(),t),t.success=!0,t._deferred.resolve(t.to()),n(r(g.TransitionHookPhase.SUCCESS))},function(e){lt.traceError(e,t),t.success=!1,t._deferred.reject(e),t._error=e,n(r(g.TransitionHookPhase.ERROR))}),this.promise},t.prototype.valid=function(){return!this.error()||void 0!==this.success},t.prototype.abort=function(){b(this.success)&&(this._aborted=!0)},t.prototype.error=function(){var e=this.$to();if(e.self.abstract)return Ve.invalid(\"Cannot transition to abstract state '\"+e.name+\"'\");var t=e.parameters(),n=this.params(),r=t.filter(function(e){return!e.validates(n[e.id])});if(r.length){var i=r.map(function(e){return\"[\"+e.id+\":\"+qe(n[e.id])+\"]\"}).join(\", \"),o=\"The following parameter values are not valid for state '\"+e.name+\"': \"+i;return Ve.invalid(o)}return!1===this.success?this._error:void 0},t.prototype.toString=function(){var e=this.from(),t=this.to(),n=function(e){return null!==e[\"#\"]&&void 0!==e[\"#\"]?e:oe(e,[\"#\"])};return\"Transition#\"+this.$id+\"( '\"+(T(e)?e.name:e)+\"'\"+qe(n(this._treeChanges.from.map(v(\"paramValues\")).reduce(ne,{})))+\" -> \"+(this.valid()?\"\":\"(X) \")+\"'\"+(T(t)?t.name:t)+\"'\"+qe(n(this.params()))+\" )\"},t.diToken=t}();function en(e,t){var n=[\"\",\"\"],r=e.replace(/[\\\\\\[\\]\\^$*+?.()|{}]/g,\"\\\\$&\");if(!t)return r;switch(t.squash){case!1:n=[\"(\",\")\"+(t.isOptional?\"?\":\"\")];break;case!0:r=r.replace(/\\/$/,\"\"),n=[\"(?:/(\",\")|/)?\"];break;default:n=[\"(\"+t.squash+\"|\",\")?\"]}return r+n[0]+t.type.pattern.source+n[1]}var tn=Xe(\"/\"),nn={state:{params:{}},strict:!0,caseInsensitive:!0},rn=function(){function m(o,a,e,t){var s=this;this._cache={path:[this]},this._children=[],this._params=[],this._segments=[],this._compiled=[],this.config=t=te(t,nn),this.pattern=o;for(var n,r,i,u=/([:*])([\\w\\[\\]]+)|\\{([\\w\\[\\]]+)(?:\\:\\s*((?:[^{}\\\\]+|\\\\.|\\{(?:[^{}\\\\]+|\\\\.)*\\})+))?\\}/g,l=/([:]?)([\\w\\[\\].-]+)|\\{([\\w\\[\\].-]+)(?:\\:\\s*((?:[^{}\\\\]+|\\\\.|\\{(?:[^{}\\\\]+|\\\\.)*\\})+))?\\}/g,c=[],d=0,p=function(e){if(!m.nameValidator.test(e))throw new Error(\"Invalid parameter name '\"+e+\"' in pattern '\"+o+\"'\");if(ue(s._params,y(\"id\",e)))throw new Error(\"Duplicate parameter name '\"+e+\"' in pattern '\"+o+\"'\")},h=function(e,t){var n,r=e[2]||e[3],i=t?e[4]:e[4]||(\"*\"===e[1]?\"[\\\\s\\\\S]*\":null);return{id:r,regexp:i,segment:o.substring(d,e.index),type:i?a.type(i)||(n=i,W(a.type(t?\"query\":\"path\"),{pattern:new RegExp(n,s.config.caseInsensitive?\"i\":void 0)})):null}};(n=u.exec(o))&&!(0<=(r=h(n,!1)).segment.indexOf(\"?\"));)p(r.id),this._params.push(e.fromPath(r.id,r.type,t.state)),this._segments.push(r.segment),c.push([r.segment,De(this._params)]),d=u.lastIndex;var f=(i=o.substring(d)).indexOf(\"?\");if(0<=f){var g=i.substring(f);if(i=i.substring(0,f),0<g.length)for(d=0;n=l.exec(g);)p((r=h(n,!0)).id),this._params.push(e.fromSearch(r.id,r.type,t.state)),d=u.lastIndex}this._segments.push(i),this._compiled=c.map(function(e){return en.apply(null,e)}).concat(en(i))}return m.encodeDashes=function(e){return encodeURIComponent(e).replace(/-/g,function(e){return\"%5C%\"+e.charCodeAt(0).toString(16).toUpperCase()})},m.pathSegmentsAndParams=function(e){return Ce(e._segments,e._params.filter(function(e){return e.location===g.DefType.PATH}).concat(void 0)).reduce(fe,[]).filter(function(e){return\"\"!==e&&k(e)})},m.queryParams=function(e){return e._params.filter(function(e){return e.location===g.DefType.SEARCH})},m.compare=function(e,t){var n=function(e){return e._cache.weights=e._cache.weights||function(e){return e._cache.segments=e._cache.segments||e._cache.path.map(m.pathSegmentsAndParams).reduce(fe,[]).reduce(Je,[]).map(function(e){return O(e)?tn(e):e}).reduce(fe,[])}(e).map(function(e){return\"/\"===e?1:O(e)?2:e instanceof vt?3:void 0})},r=n(e),i=n(t);!function(e,t,n){for(var r=Math.max(e.length,t.length);e.length<r;)e.push(n);for(;t.length<r;)t.push(n)}(r,i,0);var o,a,s=Ce(r,i);for(a=0;a<s.length;a++)if(0!=(o=s[a][0]-s[a][1]))return o;return 0},m.prototype.append=function(e){return this._children.push(e),e._cache={path:this._cache.path.concat(e),parent:this,pattern:null},e},m.prototype.isRoot=function(){return this._cache.path[0]===this},m.prototype.toString=function(){return this.pattern},m.prototype.exec=function(e,r,t,n){var i=this;void 0===r&&(r={}),void 0===n&&(n={});var o=function(e,t,n){return e[t]=e[t]||n()}(this._cache,\"pattern\",function(){return new RegExp([\"^\",ye(i._cache.path.map(v(\"_compiled\"))).join(\"\"),!1===i.config.strict?\"/?\":\"\",\"$\"].join(\"\"),i.config.caseInsensitive?\"i\":void 0)}).exec(e);if(!o)return null;var a,s=this.parameters(),u=s.filter(function(e){return!e.isSearch()}),l=s.filter(function(e){return e.isSearch()}),c=this._cache.path.map(function(e){return e._segments.length-1}).reduce(function(e,t){return e+t}),d={};if(c!==o.length-1)throw new Error(\"Unbalanced capture group in route '\"+this.pattern+\"'\");for(var p=0;p<c;p++){for(var h=u[p],f=o[p+1],g=0;g<h.replace.length;g++)h.replace[g].from===f&&(f=h.replace[g].to);f&&!0===h.array&&(void 0,f=ce(ce((a=function(e){return e.split(\"\").reverse().join(\"\")})(f).split(/-(?!\\\\)/),a),function(e){return e.replace(/\\\\-/g,\"-\")}).reverse()),k(f)&&(f=h.type.decode(f)),d[h.id]=h.value(f)}return l.forEach(function(e){for(var t=r[e.id],n=0;n<e.replace.length;n++)e.replace[n].from===t&&(t=e.replace[n].to);k(t)&&(t=e.type.decode(t)),d[e.id]=e.value(t)}),t&&(d[\"#\"]=t),d},m.prototype.parameters=function(e){return void 0===e&&(e={}),!1===e.inherit?this._params:ye(this._cache.path.map(function(e){return e._params}))},m.prototype.parameter=function(r,e){var i=this;void 0===e&&(e={});var t=this._cache.parent;return function(){for(var e=0,t=i._params;e<t.length;e++){var n=t[e];if(n.id===r)return n}}()||!1!==e.inherit&&t&&t.parameter(r,e)||null},m.prototype.validates=function(t){return t=t||{},this.parameters().filter(function(e){return t.hasOwnProperty(e.id)}).map(function(e){return function(e,t){return!e||e.validates(t)}(e,t[e.id])}).reduce(pe,!0)},m.prototype.format=function(a){void 0===a&&(a={});var e=this._cache.path,t=e.map(m.pathSegmentsAndParams).reduce(fe,[]).map(function(e){return O(e)?e:r(e)}),n=e.map(m.queryParams).reduce(fe,[]).map(r);if(t.concat(n).filter(function(e){return!1===e.isValid}).length)return null;function r(e){var t=e.value(a[e.id]),n=e.validates(t),r=e.isDefaultValue(t),i=!!r&&e.squash,o=e.type.encode(t);return{param:e,value:t,isValid:n,isDefaultValue:r,squash:i,encoded:o}}var i=t.reduce(function(e,t){if(O(t))return e+t;var n=t.squash,r=t.encoded,i=t.param;return!0===n?e.match(/\\/$/)?e.slice(0,-1):e:O(n)?e+n:!1!==n?e:null==r?e:A(r)?e+ce(r,m.encodeDashes).join(\"-\"):i.raw?e+r:e+encodeURIComponent(r)},\"\"),o=n.map(function(e){var t=e.param,n=e.squash,r=e.encoded,i=e.isDefaultValue;if(!(null==r||i&&!1!==n)&&(A(r)||(r=[r]),0!==r.length))return t.raw||(r=ce(r,encodeURIComponent)),r.map(function(e){return t.id+\"=\"+e})}).filter(q).reduce(fe,[]).join(\"&\");return i+(o?\"?\"+o:\"\")+(a[\"#\"]?\"#\"+a[\"#\"]:\"\")},m.nameValidator=/^\\w+([-.]+\\w+)*(?:\\[\\])?$/,m}(),on=function(){return(on=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},an=function(){function e(e){this.router=e}return e.prototype.fromConfig=function(e,t,n){return new vt(e,t,g.DefType.CONFIG,this.router.urlService.config,n)},e.prototype.fromPath=function(e,t,n){return new vt(e,t,g.DefType.PATH,this.router.urlService.config,n)},e.prototype.fromSearch=function(e,t,n){return new vt(e,t,g.DefType.SEARCH,this.router.urlService.config,n)},e}(),sn=function(){function e(e){var r=this;this.router=e,this.paramFactory=new an(this.router),this.caseInsensitive=function(e){return r.router.urlService.config.caseInsensitive(e)},this.defaultSquashPolicy=function(e){return r.router.urlService.config.defaultSquashPolicy(e)},this.strictMode=function(e){return r.router.urlService.config.strictMode(e)},this.type=function(e,t,n){return r.router.urlService.config.type(e,t,n)||r},Y(this,{UrlMatcher:rn,Param:vt})}return e.prototype.compile=function(e,t){var n=this.router.urlService.config,r=t&&!t.state&&t.params;t=r?on({state:{params:r}},t):t;var i={strict:n._isStrictMode,caseInsensitive:n._isCaseInsensitive};return new rn(e,n.paramTypes,this.paramFactory,Y(i,t))},e.prototype.isMatcher=function(n){if(!T(n))return!1;var r=!0;return N(rn.prototype,function(e,t){D(e)&&(r=r&&k(n[t])&&D(n[t]))}),r},e.prototype.$get=function(){var e=this.router.urlService.config;return e.paramTypes.enqueue=!1,e.paramTypes._flushTypeQueue(),this},e}(),un=function(){function e(e){this.router=e}return e.prototype.compile=function(e){return this.router.urlMatcherFactory.compile(e)},e.prototype.create=function(e,t){var n=this,r=Yt.isState,i=f([[O,function(e){return i(n.compile(e))}],[c(rn),function(e){return n.fromUrlMatcher(e,t)}],[r,function(e){return n.fromState(e,n.router)}],[c(RegExp),function(e){return n.fromRegExp(e,t)}],[D,function(e){return new ln(e,t)}]]),o=i(e);if(!o)throw new Error(\"invalid 'what' in when()\");return o},e.prototype.fromUrlMatcher=function(n,t){var e=t;O(t)&&(t=this.router.urlMatcherFactory.compile(t)),c(rn)(t)&&(e=function(e){return t.format(e)});var r={urlMatcher:n,matchPriority:function(t){var e=n.parameters().filter(function(e){return e.isOptional});return e.length?e.filter(function(e){return t[e.id]}).length/e.length:1e-6},type:\"URLMATCHER\"};return Y(new ln(function(e){var t=n.exec(e.path,e.search,e.hash);return n.validates(t)&&t},e),r)},e.prototype.fromState=function(r,i){var e={state:r,type:\"STATE\"};return Y(this.fromUrlMatcher(r.url,function(e){var t=i.stateService,n=i.globals;t.href(r,e)!==t.href(n.current,n.params)&&t.transitionTo(r,e,{inherit:!0,source:\"url\"})}),e)},e.prototype.fromRegExp=function(t,e){if(t.global||t.sticky)throw new Error(\"Rule RegExp must not be global or sticky\");var n=O(e)?function(n){return e.replace(/\\$(\\$|\\d{1,2})/,function(e,t){return n[\"$\"===t?0:Number(t)]})}:e,r={regexp:t,type:\"REGEXP\"};return Y(new ln(function(e){return t.exec(e.path)},n),r)},e.isUrlRule=function(t){return t&&[\"type\",\"match\",\"handler\"].every(function(e){return k(t[e])})},e}(),ln=function(e,t){var n=this;this.match=e,this.type=\"RAW\",this.matchPriority=function(e){return 0-n.$id},this.handler=t||q};var cn,dn=function(){function e(e){var r=this;this.router=e,this.sync=function(e){return r.router.urlService.sync(e)},this.listen=function(e){return r.router.urlService.listen(e)},this.deferIntercept=function(e){return r.router.urlService.deferIntercept(e)},this.match=function(e){return r.router.urlService.match(e)},this.initial=function(e){return r.router.urlService.rules.initial(e)},this.otherwise=function(e){return r.router.urlService.rules.otherwise(e)},this.removeRule=function(e){return r.router.urlService.rules.removeRule(e)},this.rule=function(e){return r.router.urlService.rules.rule(e)},this.rules=function(){return r.router.urlService.rules.rules()},this.sort=function(e){return r.router.urlService.rules.sort(e)},this.when=function(e,t,n){return r.router.urlService.rules.when(e,t,n)},this.urlRuleFactory=new un(e)}return e.prototype.update=function(e){var t=this.router.locationService;e?this.location=t.url():t.url()!==this.location&&t.url(this.location,!0)},e.prototype.push=function(e,t,n){var r=n&&!!n.replace;this.router.urlService.url(e.format(t||{}),r)},e.prototype.href=function(e,t,n){var r=e.format(t);if(null==r)return null;n=n||{absolute:!1};var i=this.router.urlService.config,o=i.html5Mode();if(o||null===r||(r=\"#\"+i.hashPrefix()+r),r=function(e,t,n,r){return\"/\"===r?e:t?We(r)+e:n?r.slice(1)+e:e}(r,o,n.absolute,i.baseHref()),!n.absolute||!r)return r;var a=!o&&r?\"/\":\"\",s=i.port(),u=80===s||443===s?\"\":\":\"+s;return[i.protocol(),\"://\",i.host(),u,a,r].join(\"\")},Object.defineProperty(e.prototype,\"interceptDeferred\",{get:function(){return this.router.urlService.interceptDeferred},enumerable:!0,configurable:!0}),e}(),pn=function(){function u(e){var n=this;this.router=e,this._uiViews=[],this._viewConfigs=[],this._viewConfigFactories={},this._listeners=[],this._pluginapi={_rootViewContext:this._rootViewContext.bind(this),_viewConfigFactory:this._viewConfigFactory.bind(this),_registeredUIView:function(t){return ue(n._uiViews,function(e){return n.router.$id+\".\"+e.id===t})},_registeredUIViews:function(){return n._uiViews},_activeViewConfigs:function(){return n._viewConfigs},_onSync:function(e){return n._listeners.push(e),function(){return Q(n._listeners,e)}}}}return u.normalizeUIViewTarget=function(e,t){void 0===t&&(t=\"\");var n=t.split(\"@\"),r=n[0]||\"$default\",i=O(n[1])?n[1]:\"^\",o=/^(\\^(?:\\.\\^)*)\\.(.*$)/.exec(r);o&&(i=o[1],r=o[2]),\"!\"===r.charAt(0)&&(r=r.substr(1),i=\"\");/^(\\^(?:\\.\\^)*)$/.exec(i)?i=i.split(\".\").reduce(function(e,t){return e.parent},e).name:\".\"===i&&(i=e.name);return{uiViewName:r,uiViewContextAnchor:i}},u.prototype._rootViewContext=function(e){return this._rootContext=e||this._rootContext},u.prototype._viewConfigFactory=function(e,t){this._viewConfigFactories[e]=t},u.prototype.createViewConfig=function(e,t){var n=this._viewConfigFactories[t.$type];if(!n)throw new Error(\"ViewService: No view config factory registered for type \"+t.$type);var r=n(e,t);return A(r)?r:[r]},u.prototype.deactivateViewConfig=function(e){lt.traceViewServiceEvent(\"<- Removing\",e),Q(this._viewConfigs,e)},u.prototype.activateViewConfig=function(e){lt.traceViewServiceEvent(\"-> Registering\",e),this._viewConfigs.push(e)},u.prototype.sync=function(){var n=this,r=this._uiViews.map(function(e){return[e.fqn,e]}).reduce(ke,{});function i(e){for(var t=e.viewDecl.$context,n=0;++n&&t.parent;)t=t.parent;return n}var o=l(function(e,t,n,r){return t*(e(n)-e(r))}),e=this._uiViews.sort(o(function(e){var t=function(e){return e&&e.parent?t(e.parent)+1:1};return 1e4*e.fqn.split(\".\").length+t(e.creationContext)},1)).map(function(e){var t=n._viewConfigs.filter(u.matches(r,e));return 1<t.length&&t.sort(o(i,-1)),{uiView:e,viewConfig:t[0]}}),t=e.map(function(e){return e.viewConfig}),a=this._viewConfigs.filter(function(e){return!G(t,e)}).map(function(e){return{uiView:void 0,viewConfig:e}});e.forEach(function(e){-1!==n._uiViews.indexOf(e.uiView)&&e.uiView.configUpdated(e.viewConfig)});var s=e.concat(a);this._listeners.forEach(function(e){return e(s)}),lt.traceViewSync(s)},u.prototype.registerUIView=function(t){lt.traceViewServiceUIViewEvent(\"-> Registering\",t);var e=this._uiViews;return e.filter(function(e){return e.fqn===t.fqn&&e.$type===t.$type}).length&&lt.traceViewServiceUIViewEvent(\"!!!! duplicate uiView named:\",t),e.push(t),this.sync(),function(){-1!==e.indexOf(t)?(lt.traceViewServiceUIViewEvent(\"<- Deregistering\",t),Q(e)(t)):lt.traceViewServiceUIViewEvent(\"Tried removing non-registered uiView\",t)}},u.prototype.available=function(){return this._uiViews.map(v(\"fqn\"))},u.prototype.active=function(){return this._uiViews.filter(v(\"$config\")).map(v(\"name\"))},u.matches=function(s,u){return function(e){if(u.$type!==e.viewDecl.$type)return!1;var t=e.viewDecl,n=t.$uiViewName.split(\".\"),r=u.fqn.split(\".\");if(!U(n,r.slice(0-n.length)))return!1;var i=1-n.length||void 0,o=r.slice(0,i).join(\".\"),a=s[o].creationContext;return t.$uiViewContextAnchor===(a&&a.name)}},u}(),hn=function(){function e(){this.params=new wt,this.lastStartedTransitionId=-1,this.transitionHistory=new Re([],1),this.successfulTransitions=new Re([],1)}return e.prototype.dispose=function(){this.transitionHistory.clear(),this.successfulTransitions.clear(),this.transition=null},e}();function fn(e){if(!(D(e)||O(e)||c($t)(e)||$t.isDef(e)))throw new Error(\"'handler' must be a string, function, TargetState, or have a state: 'newtarget' property\");return D(e)?e:p(e)}cn=function(e,t){var n=function(e,t){return(t.priority||0)-(e.priority||0)}(e,t);return 0!==n?n:0!==(n=function(e,t){var n={STATE:4,URLMATCHER:4,REGEXP:3,RAW:2,OTHER:1};return(n[e.type]||0)-(n[t.type]||0)}(e,t))?n:0!==(n=function(e,t){return e.urlMatcher&&t.urlMatcher?rn.compare(e.urlMatcher,t.urlMatcher):0}(e,t))?n:function(e,t){var n={STATE:!0,URLMATCHER:!0};return n[e.type]&&n[t.type]?0:(e.$id||0)-(t.$id||0)}(e,t)};var gn=function(){function e(e){this.router=e,this._sortFn=cn,this._rules=[],this._id=0,this.urlRuleFactory=new un(e)}return e.prototype.dispose=function(e){this._rules=[],delete this._otherwiseFn},e.prototype.initial=function(e){var t=fn(e);this.rule(this.urlRuleFactory.create(function(e,t){return 0===t.globals.transitionHistory.size()&&!!/^\\/?$/.exec(e.path)},t))},e.prototype.otherwise=function(e){var t=fn(e);this._otherwiseFn=this.urlRuleFactory.create(p(!0),t),this._sorted=!1},e.prototype.removeRule=function(e){Q(this._rules,e)},e.prototype.rule=function(e){var t=this;if(!un.isUrlRule(e))throw new Error(\"invalid rule\");return e.$id=this._id++,e.priority=e.priority||0,this._rules.push(e),this._sorted=!1,function(){return t.removeRule(e)}},e.prototype.rules=function(){return this.ensureSorted(),this._rules.concat(this._otherwiseFn?[this._otherwiseFn]:[])},e.prototype.sort=function(e){for(var t=this.stableSort(this._rules,this._sortFn=e||this._sortFn),n=0,r=0;r<t.length;r++)t[r]._group=n,r<t.length-1&&0!==this._sortFn(t[r],t[r+1])&&n++;this._rules=t,this._sorted=!0},e.prototype.ensureSorted=function(){this._sorted||this.sort()},e.prototype.stableSort=function(e,r){var t=e.map(function(e,t){return{elem:e,idx:t}});return t.sort(function(e,t){var n=r(e.elem,t.elem);return 0===n?e.idx-t.idx:n}),t.map(function(e){return e.elem})},e.prototype.when=function(e,t,n){var r=this.urlRuleFactory.create(e,t);return k(n&&n.priority)&&(r.priority=n.priority),this.rule(r),r},e}(),mn=function(){function e(e){var t=this;this.router=e,this.paramTypes=new yt,this._isCaseInsensitive=!1,this._isStrictMode=!0,this._defaultSquashPolicy=!1,this.dispose=function(){return t.paramTypes.dispose()},this.baseHref=function(){return t.router.locationConfig.baseHref()},this.hashPrefix=function(e){return t.router.locationConfig.hashPrefix(e)},this.host=function(){return t.router.locationConfig.host()},this.html5Mode=function(){return t.router.locationConfig.html5Mode()},this.port=function(){return t.router.locationConfig.port()},this.protocol=function(){return t.router.locationConfig.protocol()}}return e.prototype.caseInsensitive=function(e){return this._isCaseInsensitive=k(e)?e:this._isCaseInsensitive},e.prototype.defaultSquashPolicy=function(e){if(k(e)&&!0!==e&&!1!==e&&!O(e))throw new Error(\"Invalid squash policy: \"+e+\". Valid policies: false, true, arbitrary-string\");return this._defaultSquashPolicy=k(e)?e:this._defaultSquashPolicy},e.prototype.strictMode=function(e){return this._isStrictMode=k(e)?e:this._isStrictMode},e.prototype.type=function(e,t,n){var r=this.paramTypes.type(e,t,n);return k(t)?this:r},e}(),vn=function(){function e(e){var r=this;this.router=e,this.interceptDeferred=!1,this.rules=new gn(this.router),this.config=new mn(this.router),this.url=function(e,t,n){return r.router.locationService.url(e,t,n)},this.path=function(){return r.router.locationService.path()},this.search=function(){return r.router.locationService.search()},this.hash=function(){return r.router.locationService.hash()},this.onChange=function(e){return r.router.locationService.onChange(e)}}return e.prototype.dispose=function(){this.listen(!1),this.rules.dispose()},e.prototype.parts=function(){return{path:this.path(),search:this.search(),hash:this.hash()}},e.prototype.sync=function(e){if(!e||!e.defaultPrevented){var t=this.router,n=t.urlService,r=t.stateService,i={path:n.path(),search:n.search(),hash:n.hash()},o=this.match(i);f([[O,function(e){return n.url(e,!0)}],[$t.isDef,function(e){return r.go(e.state,e.params,e.options)}],[c($t),function(e){return r.go(e.state(),e.params(),e.options())}]])(o&&o.rule.handler(o.match,i,this.router))}},e.prototype.listen=function(e){var t=this;if(!1!==e)return this._stopListeningFn=this._stopListeningFn||this.router.urlService.onChange(function(e){return t.sync(e)});this._stopListeningFn&&this._stopListeningFn(),delete this._stopListeningFn},e.prototype.deferIntercept=function(e){void 0===e&&(e=!0),this.interceptDeferred=e},e.prototype.match=function(e){var t=this;e=Y({path:\"\",search:{},hash:\"\"},e);for(var n,r,i,o=this.rules.rules(),a=0;a<o.length&&(!n||n.rule._group===o[a]._group);a++){var s=(r=o[a],void 0,(i=r.match(e,t.router))&&{match:i,rule:r,weight:r.matchPriority(i)});n=!n||s&&s.weight>n.weight?s:n}return n},e}(),yn=0,wn=I(\"LocationServices\",[\"url\",\"path\",\"search\",\"hash\",\"onChange\"]),bn=I(\"LocationConfig\",[\"port\",\"protocol\",\"host\",\"baseHref\",\"html5Mode\",\"hashPrefix\"]),$n=function(){function e(e,t){void 0===e&&(e=wn),void 0===t&&(t=bn),this.locationService=e,this.locationConfig=t,this.$id=yn++,this._disposed=!1,this._disposables=[],this.trace=lt,this.viewService=new pn(this),this.globals=new hn,this.transitionService=new Un(this),this.urlMatcherFactory=new sn(this),this.urlRouter=new dn(this),this.urlService=new vn(this),this.stateRegistry=new zt(this),this.stateService=new qn(this),this._plugins={},this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()),this.globals.$current=this.stateRegistry.root(),this.globals.current=this.globals.$current.self,this.disposable(this.globals),this.disposable(this.stateService),this.disposable(this.stateRegistry),this.disposable(this.transitionService),this.disposable(this.urlService),this.disposable(e),this.disposable(t)}return e.prototype.disposable=function(e){this._disposables.push(e)},e.prototype.dispose=function(e){var t=this;e&&D(e.dispose)?e.dispose(this):(this._disposed=!0,this._disposables.slice().forEach(function(e){try{\"function\"==typeof e.dispose&&e.dispose(t),Q(t._disposables,e)}catch(e){}}))},e.prototype.plugin=function(e,t){void 0===t&&(t={});var n=new e(this,t);if(!n.name)throw new Error(\"Required property `name` missing on plugin: \"+n);return this._disposables.push(n),this._plugins[n.name]=n},e.prototype.getPlugin=function(e){return e?this._plugins[e]:de(this._plugins)},e}();function _n(t){t.addResolvable(kt.fromData($n,t.router),\"\"),t.addResolvable(kt.fromData(Jt,t),\"\"),t.addResolvable(kt.fromData(\"$transition$\",t),\"\"),t.addResolvable(kt.fromData(\"$stateParams\",t.params()),\"\"),t.entering().forEach(function(e){t.addResolvable(kt.fromData(\"$state$\",e),e)})}var Sn=G([\"$transition$\",Jt]),Cn=function(e){var t=de(e.treeChanges()).reduce(fe,[]).reduce(ve,[]),n=function(e){return Sn(e.token)?kt.fromData(e.token,null):e};t.forEach(function(e){e.resolvables=e.resolvables.map(n)})},kn=function(t){var e=t.to().redirectTo;if(e){var n=t.router.stateService;return D(e)?V.$q.when(e(t)).then(r):r(e)}function r(e){if(e)return e instanceof $t?e:O(e)?n.target(e,t.params(),t.options()):e.state||e.params?n.target(e.state||t.to(),e.params||t.params(),t.options()):void 0}};function Dn(n){return function(e,t){return(0,t.$$state()[n])(e,t)}}var xn=Dn(\"onExit\"),On=Dn(\"onRetain\"),Tn=Dn(\"onEnter\"),An=function(e){return new At(e.treeChanges().to).resolvePath(\"EAGER\",e).then(z)},En=function(e,t){return new At(e.treeChanges().to).subContext(t.$$state()).resolvePath(\"LAZY\",e).then(z)},Pn=function(e){return new At(e.treeChanges().to).resolvePath(\"LAZY\",e).then(z)},Mn=function(e){var t=V.$q,n=e.views(\"entering\");if(n.length)return t.all(n.map(function(e){return t.when(e.load())})).then(z)},Rn=function(e){var t=e.views(\"entering\"),n=e.views(\"exiting\");if(t.length||n.length){var r=e.router.viewService;n.forEach(function(e){return r.deactivateViewConfig(e)}),t.forEach(function(e){return r.activateViewConfig(e)}),r.sync()}},In=function(e){var t=e.router.globals,n=function(){t.transition===e&&(t.transition=null)};e.onSuccess({},function(){t.successfulTransitions.enqueue(e),t.$current=e.$to(),t.current=t.$current.self,xe(e.params(),t.params)},{priority:1e4}),e.promise.then(n,n)},Vn=function(e){var t=e.options(),n=e.router.stateService,r=e.router.urlRouter;if(\"url\"!==t.source&&t.location&&n.$current.navigable){var i={replace:\"replace\"===t.location};r.push(n.$current.navigable.url,n.params,i)}r.update(!0)},Ln=function(a){var s=a.router;var e=a.entering().filter(function(e){return!!e.$$state().lazyLoad}).map(function(e){return Fn(a,e)});return V.$q.all(e).then(function(){if(\"url\"!==a.originalTransition().options().source){var e=a.targetState();return s.stateService.target(e.identifier(),e.params(),e.options())}var t=s.urlService,n=t.match(t.parts()),r=n&&n.rule;if(r&&\"STATE\"===r.type){var i=r.state,o=n.match;return s.stateService.target(i,o,a.options())}s.urlService.sync()})};function Fn(t,n){var r=n.$$state().lazyLoad,e=r._promise;if(!e){e=r._promise=V.$q.when(r(t,n)).then(function(e){e&&Array.isArray(e.states)&&e.states.forEach(function(e){return t.router.stateRegistry.register(e)});return e}).then(function(e){return delete n.lazyLoad,delete n.$$state().lazyLoad,delete r._promise,e},function(e){return delete r._promise,V.$q.reject(e)})}return e}var jn=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1),this.name=e,this.hookPhase=t,this.hookOrder=n,this.criteriaMatchPath=r,this.reverseSort=i,this.getResultHandler=o,this.getErrorHandler=a,this.synchronous=s};function Hn(e){var t=e._ignoredReason();if(t){lt.traceTransitionIgnored(e);var n=e.router.globals.transition;return\"SameAsCurrent\"===t&&n&&n.abort(),Ve.ignored().toPromise()}}function Nn(e){if(!e.valid())throw new Error(e.error().toString())}var Yn={location:!0,relative:null,inherit:!1,notify:!0,reload:!1,custom:{},current:function(){return null},source:\"unknown\"},Un=function(){function e(e){this._transitionCount=0,this._eventTypes=[],this._registeredHooks={},this._criteriaPaths={},this._router=e,this.$view=e.viewService,this._deregisterHookFns={},this._pluginapi=B(p(this),{},p(this),[\"_definePathType\",\"_defineEvent\",\"_getPathTypes\",\"_getEvents\",\"getHooks\"]),this._defineCorePaths(),this._defineCoreEvents(),this._registerCoreTransitionHooks(),e.globals.successfulTransitions.onEvict(Cn)}return e.prototype.onCreate=function(e,t,n){},e.prototype.onBefore=function(e,t,n){},e.prototype.onStart=function(e,t,n){},e.prototype.onExit=function(e,t,n){},e.prototype.onRetain=function(e,t,n){},e.prototype.onEnter=function(e,t,n){},e.prototype.onFinish=function(e,t,n){},e.prototype.onSuccess=function(e,t,n){},e.prototype.onError=function(e,t,n){},e.prototype.dispose=function(e){de(this._registeredHooks).forEach(function(t){return t.forEach(function(e){e._deregistered=!0,Q(t,e)})})},e.prototype.create=function(e,t){return new Jt(e,t,this._router)},e.prototype._defineCoreEvents=function(){var e=g.TransitionHookPhase,t=Wt,n=this._criteriaPaths;this._defineEvent(\"onCreate\",e.CREATE,0,n.to,!1,t.LOG_REJECTED_RESULT,t.THROW_ERROR,!0),this._defineEvent(\"onBefore\",e.BEFORE,0,n.to),this._defineEvent(\"onStart\",e.RUN,0,n.to),this._defineEvent(\"onExit\",e.RUN,100,n.exiting,!0),this._defineEvent(\"onRetain\",e.RUN,200,n.retained),this._defineEvent(\"onEnter\",e.RUN,300,n.entering),this._defineEvent(\"onFinish\",e.RUN,400,n.to),this._defineEvent(\"onSuccess\",e.SUCCESS,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0),this._defineEvent(\"onError\",e.ERROR,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0)},e.prototype._defineCorePaths=function(){var e=g.TransitionHookScope.STATE,t=g.TransitionHookScope.TRANSITION;this._definePathType(\"to\",t),this._definePathType(\"from\",t),this._definePathType(\"exiting\",e),this._definePathType(\"retained\",e),this._definePathType(\"entering\",e)},e.prototype._defineEvent=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1);var u=new jn(e,t,n,r,i,o,a,s);this._eventTypes.push(u),Qt(this,this,u)},e.prototype._getEvents=function(t){return(k(t)?this._eventTypes.filter(function(e){return e.hookPhase===t}):this._eventTypes.slice()).sort(function(e,t){var n=e.hookPhase-t.hookPhase;return 0==n?e.hookOrder-t.hookOrder:n})},e.prototype._definePathType=function(e,t){this._criteriaPaths[e]={name:e,scope:t}},e.prototype._getPathTypes=function(){return this._criteriaPaths},e.prototype.getHooks=function(e){return this._registeredHooks[e]},e.prototype._registerCoreTransitionHooks=function(){var e=this._deregisterHookFns;e.addCoreResolves=function(e){return e.onCreate({},_n)}(this),e.ignored=function(e){return e.onBefore({},Hn,{priority:-9999})}(this),e.invalid=function(e){return e.onBefore({},Nn,{priority:-1e4})}(this),e.redirectTo=function(e){return e.onStart({to:function(e){return!!e.redirectTo}},kn)}(this),e.onExit=function(e){return e.onExit({exiting:function(e){return!!e.onExit}},xn)}(this),e.onRetain=function(e){return e.onRetain({retained:function(e){return!!e.onRetain}},On)}(this),e.onEnter=function(e){return e.onEnter({entering:function(e){return!!e.onEnter}},Tn)}(this),e.eagerResolve=function(e){return e.onStart({},An,{priority:1e3})}(this),e.lazyResolve=function(e){return e.onEnter({entering:p(!0)},En,{priority:1e3})}(this),e.resolveAll=function(e){return e.onFinish({},Pn,{priority:1e3})}(this),e.loadViews=function(e){return e.onFinish({},Mn)}(this),e.activateViews=function(e){return e.onSuccess({},Rn)}(this),e.updateGlobals=function(e){return e.onCreate({},In)}(this),e.updateUrl=function(e){return e.onSuccess({},Vn,{priority:9999})}(this),e.lazyLoad=function(e){return e.onBefore({entering:function(e){return!!e.lazyLoad}},Ln)}(this)},e}(),qn=function(){function n(e){this.router=e,this.invalidCallbacks=[],this._defaultErrorHandler=function(e){e instanceof Error&&e.stack?(console.error(e),console.error(e.stack)):e instanceof Ve?(console.error(e.toString()),e.detail&&e.detail.stack&&console.error(e.detail.stack)):console.error(e)};var t=Object.keys(n.prototype).filter(d(G([\"current\",\"$current\",\"params\",\"transition\"])));B(p(n.prototype),this,p(this),t)}return Object.defineProperty(n.prototype,\"transition\",{get:function(){return this.router.globals.transition},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"params\",{get:function(){return this.router.globals.params},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"current\",{get:function(){return this.router.globals.current},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"$current\",{get:function(){return this.router.globals.$current},enumerable:!0,configurable:!0}),n.prototype.dispose=function(){this.defaultErrorHandler(z),this.invalidCallbacks=[]},n.prototype._handleInvalidTargetState=function(e,n){var r=this,i=_t.makeTargetState(this.router.stateRegistry,e),t=this.router.globals,o=function(){return t.transitionHistory.peekTail()},a=o(),s=new Re(this.invalidCallbacks.slice()),u=new At(e).injector(),l=function(e){if(e instanceof $t){var t=e;return(t=r.target(t.identifier(),t.params(),t.options())).valid()?o()!==a?Ve.superseded().toPromise():r.transitionTo(t.identifier(),t.params(),t.options()):Ve.invalid(t.error()).toPromise()}};return function t(){var e=s.dequeue();return void 0===e?Ve.invalid(n.error()).toPromise():V.$q.when(e(n,i,u)).then(l).then(function(e){return e||t()})}()},n.prototype.onInvalid=function(e){return this.invalidCallbacks.push(e),function(){Q(this.invalidCallbacks)(e)}.bind(this)},n.prototype.reload=function(e){return this.transitionTo(this.current,this.params,{reload:!k(e)||e,inherit:!1,notify:!1})},n.prototype.go=function(e,t,n){var r=te(n,{relative:this.$current,inherit:!0},Yn);return this.transitionTo(e,t,r)},n.prototype.target=function(e,t,n){if(void 0===n&&(n={}),T(n.reload)&&!n.reload.name)throw new Error(\"Invalid reload state object\");var r=this.router.stateRegistry;if(n.reloadState=!0===n.reload?r.root():r.matcher.find(n.reload,n.relative),n.reload&&!n.reloadState)throw new Error(\"No such reload state '\"+(O(n.reload)?n.reload:n.reload.name)+\"'\");return new $t(this.router.stateRegistry,e,t,n)},n.prototype.getCurrentPath=function(){var e=this,t=this.router.globals.successfulTransitions.peekTail();return t?t.treeChanges().to:[new bt(e.router.stateRegistry.root())]},n.prototype.transitionTo=function(e,t,n){var o=this;void 0===t&&(t={}),void 0===n&&(n={});var a=this.router,s=a.globals;n=te(n,Yn);n=Y(n,{current:function(){return s.transition}});var r=this.target(e,t,n),i=this.getCurrentPath();if(!r.exists())return this._handleInvalidTargetState(i,r);if(!r.valid())return Pe(r.error());var u=function(i){return function(e){if(e instanceof Ve){var t=a.globals.lastStartedTransitionId<=i.$id;if(e.type===g.RejectType.IGNORED)return t&&a.urlRouter.update(),V.$q.when(s.current);var n=e.detail;if(e.type===g.RejectType.SUPERSEDED&&e.redirected&&n instanceof $t){var r=i.redirect(n);return r.run().catch(u(r))}if(e.type===g.RejectType.ABORTED)return t&&a.urlRouter.update(),V.$q.reject(e)}return o.defaultErrorHandler()(e),V.$q.reject(e)}},l=this.router.transitionService.create(i,r),c=l.run().catch(u(l));return Ee(c),Y(c,{transition:l})},n.prototype.is=function(e,t,n){n=te(n,{relative:this.$current});var r=this.router.stateRegistry.matcher.find(e,n.relative);if(k(r)){if(this.$current!==r)return!1;if(!t)return!0;var i=r.parameters({inherit:!0,matchingKeys:t});return vt.equals(i,vt.values(i,t),this.params)}},n.prototype.includes=function(e,t,n){n=te(n,{relative:this.$current});var r=O(e)&&Me.fromString(e);if(r){if(!r.matches(this.$current.name))return!1;e=this.$current.name}var i=this.router.stateRegistry.matcher.find(e,n.relative),o=this.$current.includes;if(k(i)){if(!k(o[i.name]))return!1;if(!t)return!0;var a=i.parameters({inherit:!0,matchingKeys:t});return vt.equals(a,vt.values(a,t),this.params)}},n.prototype.href=function(e,t,n){n=te(n,{lossy:!0,inherit:!0,absolute:!1,relative:this.$current}),t=t||{};var r=this.router.stateRegistry.matcher.find(e,n.relative);if(!k(r))return null;n.inherit&&(t=this.params.$inherit(t,this.$current,r));var i=r&&n.lossy?r.navigable:r;return i&&void 0!==i.url&&null!==i.url?this.router.urlRouter.href(i.url,t,{absolute:n.absolute}):null},n.prototype.defaultErrorHandler=function(e){return this._defaultErrorHandler=e||this._defaultErrorHandler},n.prototype.get=function(e,t){var n=this.router.stateRegistry;return 0===arguments.length?n.get():n.get(e,t||this.$current)},n.prototype.lazyLoad=function(e,t){var n=this.get(e);if(!n||!n.lazyLoad)throw new Error(\"Can not lazy load \"+e);var r=this.getCurrentPath(),i=_t.makeTargetState(this.router.stateRegistry,r);return Fn(t=t||this.router.transitionService.create(r,i),n)},n}(),zn={when:function(n){return new Promise(function(e,t){return e(n)})},reject:function(n){return new Promise(function(e,t){t(n)})},defer:function(){var n={};return n.promise=new Promise(function(e,t){n.resolve=e,n.reject=t}),n},all:function(e){if(A(e))return Promise.all(e);if(T(e)){var t=Object.keys(e).map(function(t){return e[t].then(function(e){return{key:t,val:e}})});return zn.all(t).then(function(e){return e.reduce(function(e,t){return e[t.key]=t.val,e},{})})}}},Bn={},Wn=/((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm,Gn=/([^\\s,]+)/g,Kn={get:function(e){return Bn[e]},has:function(e){return null!=Kn.get(e)},invoke:function(e,t,n){var r=Y({},Bn,n||{}),i=Kn.annotate(e),o=be(function(e){return r.hasOwnProperty(e)},function(e){return\"DI can't find injectable: '\"+e+\"'\"}),a=i.filter(o).map(function(e){return r[e]});return D(e)?e.apply(t,a):e.slice(-1)[0].apply(t,a)},annotate:function(e){if(!M(e))throw new Error(\"Not an injectable function: \"+e);if(e&&e.$inject)return e.$inject;if(A(e))return e.slice(0,-1);var t=e.toString().replace(Wn,\"\");return t.slice(t.indexOf(\"(\")+1,t.indexOf(\")\")).match(Gn)||[]}},Qn=function(e,t){var n=t[0],r=t[1];return e.hasOwnProperty(n)?A(e[n])?e[n].push(r):e[n]=[e[n],r]:e[n]=r,e},Zn=function(e){return e.split(\"&\").filter(q).map(Qe).reduce(Qn,{})};function Xn(e){var t=function(e){return e||\"\"},n=Ge(e).map(t),r=n[0],i=n[1],o=Ke(r).map(t);return{path:o[0],search:o[1],hash:i,url:e}}var Jn=function(e){var t=e.path(),n=e.search(),r=e.hash(),i=Object.keys(n).map(function(t){var e=n[t];return(A(e)?e:[e]).map(function(e){return t+\"=\"+e})}).reduce(fe,[]).join(\"&\");return t+(i?\"?\"+i:\"\")+(r?\"#\"+r:\"\")};function er(r,i,o,a){return function(e){var t=e.locationService=new o(e),n=e.locationConfig=new a(e,i);return{name:r,service:t,configuration:n,dispose:function(e){e.dispose(t),e.dispose(n)}}}}var tr,nr,rr,ir=function(){function e(e,t){var n=this;this.fireAfterUpdate=t,this._listeners=[],this._listener=function(t){return n._listeners.forEach(function(e){return e(t)})},this.hash=function(){return Xn(n._get()).hash},this.path=function(){return Xn(n._get()).path},this.search=function(){return Zn(Xn(n._get()).search)},this._location=L.location,this._history=L.history}return e.prototype.url=function(t,e){return void 0===e&&(e=!0),k(t)&&t!==this._get()&&(this._set(null,null,t,e),this.fireAfterUpdate&&this._listeners.forEach(function(e){return e({url:t})})),Jn(this)},e.prototype.onChange=function(e){var t=this;return this._listeners.push(e),function(){return Q(t._listeners,e)}},e.prototype.dispose=function(e){ee(this._listeners)},e}(),or=(tr=function(e,t){return(tr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}tr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ar=function(n){function e(e){var t=n.call(this,e,!1)||this;return L.addEventListener(\"hashchange\",t._listener,!1),t}return or(e,n),e.prototype._get=function(){return Ze(this._location.hash)},e.prototype._set=function(e,t,n,r){this._location.hash=n},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener(\"hashchange\",this._listener)},e}(ir),sr=(nr=function(e,t){return(nr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}nr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ur=function(t){function e(e){return t.call(this,e,!0)||this}return sr(e,t),e.prototype._get=function(){return this._url},e.prototype._set=function(e,t,n,r){this._url=n},e}(ir),lr=(rr=function(e,t){return(rr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}rr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),cr=function(n){function e(e){var t=n.call(this,e,!0)||this;return t._config=e.urlService.config,L.addEventListener(\"popstate\",t._listener,!1),t}return lr(e,n),e.prototype._getBasePrefix=function(){return We(this._config.baseHref())},e.prototype._get=function(){var e=this._location,t=e.pathname,n=e.hash,r=e.search;r=Ke(r)[1],n=Ge(n)[1];var i=this._getBasePrefix(),o=t===this._config.baseHref(),a=t.substr(0,i.length)===i;return(t=o?\"/\":a?t.substring(i.length):t)+(r?\"?\"+r:\"\")+(n?\"#\"+n:\"\")},e.prototype._set=function(e,t,n,r){var i=this._getBasePrefix(),o=n&&\"/\"!==n[0]?\"/\":\"\",a=\"\"===n||\"/\"===n?this._config.baseHref():i+o+n;r?this._history.replaceState(e,t,a):this._history.pushState(e,t,a)},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener(\"popstate\",this._listener)},e}(ir),dr=function(){var t=this;this.dispose=z,this._baseHref=\"\",this._port=80,this._protocol=\"http\",this._host=\"localhost\",this._hashPrefix=\"\",this.port=function(){return t._port},this.protocol=function(){return t._protocol},this.host=function(){return t._host},this.baseHref=function(){return t._baseHref},this.html5Mode=function(){return!1},this.hashPrefix=function(e){return k(e)?t._hashPrefix=e:t._hashPrefix}},pr=function(){function e(e,t){void 0===t&&(t=!1),this._isHtml5=t,this._baseHref=void 0,this._hashPrefix=\"\"}return e.prototype.port=function(){return location.port?Number(location.port):\"https\"===this.protocol()?443:80},e.prototype.protocol=function(){return location.protocol.replace(/:/g,\"\")},e.prototype.host=function(){return location.hostname},e.prototype.html5Mode=function(){return this._isHtml5},e.prototype.hashPrefix=function(e){return k(e)?this._hashPrefix=e:this._hashPrefix},e.prototype.baseHref=function(e){return k(e)&&(this._baseHref=e),b(this._baseHref)&&(this._baseHref=this.getBaseHref()),this._baseHref},e.prototype.getBaseHref=function(){var e=document.getElementsByTagName(\"base\")[0];return e&&e.href?e.href.replace(/^([^/:]*:)?\\/\\/[^/]*/,\"\"):this._isHtml5?\"/\":location.pathname||\"/\"},e.prototype.dispose=function(){},e}();function hr(e){return V.$injector=Kn,{name:\"vanilla.services\",$q:V.$q=zn,$injector:Kn,dispose:function(){return null}}}var fr=er(\"vanilla.hashBangLocation\",!1,ar,pr),gr=er(\"vanilla.pushStateLocation\",!0,cr,pr),mr=er(\"vanilla.memoryLocation\",!1,ur,dr),vr=function(){function e(){}return e.prototype.dispose=function(e){},e}(),yr=Object.freeze({root:L,fromJson:j,toJson:H,forEach:N,extend:Y,equals:U,identity:q,noop:z,createProxyFunctions:B,inherit:W,inArray:G,_inArray:K,removeFrom:Q,_removeFrom:Z,pushTo:X,_pushTo:J,deregAll:ee,defaults:te,mergeR:ne,ancestors:re,pick:ie,omit:oe,pluck:ae,filter:se,find:ue,mapObj:le,map:ce,values:de,allTrueR:pe,anyTrueR:he,unnestR:fe,flattenR:ge,pushR:me,uniqR:ve,unnest:ye,flatten:we,assertPredicate:be,assertMap:$e,assertFn:_e,pairs:Se,arrayTuples:Ce,applyPairs:ke,tail:De,copy:xe,_extend:Oe,silenceUncaughtInPromise:Ee,silentRejection:Pe,makeStub:I,services:V,Glob:Me,curry:l,compose:n,pipe:u,prop:v,propEq:y,parse:C,not:d,and:r,or:i,all:a,any:s,is:c,eq:o,val:p,invoke:h,pattern:f,isUndefined:b,isDefined:k,isNull:$,isNullOrUndefined:_,isFunction:D,isNumber:x,isString:O,isObject:T,isArray:A,isDate:E,isRegExp:P,isInjectable:M,isPromise:R,Queue:Re,maxLength:Le,padString:Fe,kebobString:je,functionToString:He,fnToString:Ne,stringify:qe,beforeAfterSubstr:ze,hostRegex:Be,stripLastPathElement:We,splitHash:Ge,splitQuery:Ke,splitEqual:Qe,trimHashVal:Ze,splitOnDelim:Xe,joinNeighborsR:Je,get Category(){return g.Category},Trace:ut,trace:lt,get DefType(){return g.DefType},Param:vt,ParamTypes:yt,StateParams:wt,ParamType:ct,PathNode:bt,PathUtils:_t,resolvePolicies:St,defaultResolvePolicy:Ct,Resolvable:kt,NATIVE_INJECTOR_TOKEN:Tt,ResolveContext:At,resolvablesBuilder:Ft,StateBuilder:Nt,StateObject:Yt,StateMatcher:Ut,StateQueueManager:qt,StateRegistry:zt,StateService:qn,TargetState:$t,get TransitionHookPhase(){return g.TransitionHookPhase},get TransitionHookScope(){return g.TransitionHookScope},HookBuilder:Zt,matchState:Gt,RegisteredHook:Kt,makeEvent:Qt,get RejectType(){return g.RejectType},Rejection:Ve,Transition:Jt,TransitionHook:Wt,TransitionEventType:jn,defaultTransOpts:Yn,TransitionService:Un,UrlRules:gn,UrlConfig:mn,UrlMatcher:rn,ParamFactory:an,UrlMatcherFactory:sn,UrlRouter:dn,UrlRuleFactory:un,BaseUrlRule:ln,UrlService:vn,ViewService:pn,UIRouterGlobals:hn,UIRouter:$n,$q:zn,$injector:Kn,BaseLocationServices:ir,HashLocationService:ar,MemoryLocationService:ur,PushStateLocationService:cr,MemoryLocationConfig:dr,BrowserLocationConfig:pr,keyValsToObjectR:Qn,getParams:Zn,parseUrl:Xn,buildUrl:Jn,locationPluginFactory:er,servicesPlugin:hr,hashLocationPlugin:fr,pushStateLocationPlugin:gr,memoryLocationPlugin:mr,UIRouterPluginBase:vr});function wr(){var n=null;return function(e,t){return n=n||V.$injector.get(\"$templateFactory\"),[new Sr(e,t,n)]}}var br=function(e,n){return e.reduce(function(e,t){return e||k(n[t])},!1)};function $r(r){if(!r.parent)return{};var i=[\"component\",\"bindings\",\"componentProvider\"],o=[\"templateProvider\",\"templateUrl\",\"template\",\"notify\",\"async\"].concat([\"controller\",\"controllerProvider\",\"controllerAs\",\"resolveAs\"]),e=i.concat(o);if(k(r.views)&&br(e,r))throw new Error(\"State '\"+r.name+\"' has a 'views' object. It cannot also have \\\"view properties\\\" at the state level.  Move the following properties into a view (in the 'views' object):  \"+e.filter(function(e){return k(r[e])}).join(\", \"));var a={},t=r.views||{$default:ie(r,e)};return N(t,function(e,t){if(t=t||\"$default\",O(e)&&(e={component:e}),e=Y({},e),br(i,e)&&br(o,e))throw new Error(\"Cannot combine: \"+i.join(\"|\")+\" with: \"+o.join(\"|\")+\" in stateview: '\"+t+\"@\"+r.name+\"'\");e.resolveAs=e.resolveAs||\"$resolve\",e.$type=\"ng1\",e.$context=r,e.$name=t;var n=pn.normalizeUIViewTarget(e.$context,e.$name);e.$uiViewName=n.uiViewName,e.$uiViewContextAnchor=n.uiViewContextAnchor,a[t]=e}),a}var _r=0,Sr=function(){function e(e,t,n){var r=this;this.path=e,this.viewDecl=t,this.factory=n,this.$id=_r++,this.loaded=!1,this.getTemplate=function(e,t){return r.component?r.factory.makeComponentTemplate(e,t,r.component,r.viewDecl.bindings):r.template}}return e.prototype.load=function(){var t=this,e=V.$q,n=new At(this.path),r=this.path.reduce(function(e,t){return Y(e,t.paramValues)},{}),i={template:e.when(this.factory.fromConfig(this.viewDecl,r,n)),controller:e.when(this.getController(n))};return e.all(i).then(function(e){return lt.traceViewServiceEvent(\"Loaded\",t),t.controller=e.controller,Y(t,e.template),t})},e.prototype.getController=function(e){var t=this.viewDecl.controllerProvider;if(!M(t))return this.viewDecl.controller;var n=V.$injector.annotate(t),r=A(t)?De(t):t;return new kt(\"\",r,n).get(e)},e}(),Cr=function(){function e(){var r=this;this._useHttp=S.version.minor<3,this.$get=[\"$http\",\"$templateCache\",\"$injector\",function(e,t,n){return r.$templateRequest=n.has&&n.has(\"$templateRequest\")&&n.get(\"$templateRequest\"),r.$http=e,r.$templateCache=t,r}]}return e.prototype.useHttpService=function(e){this._useHttp=e},e.prototype.fromConfig=function(e,t,n){var r=function(e){return V.$q.when(e).then(function(e){return{template:e}})},i=function(e){return V.$q.when(e).then(function(e){return{component:e}})};return k(e.template)?r(this.fromString(e.template,t)):k(e.templateUrl)?r(this.fromUrl(e.templateUrl,t)):k(e.templateProvider)?r(this.fromProvider(e.templateProvider,t,n)):k(e.component)?i(e.component):k(e.componentProvider)?i(this.fromComponentProvider(e.componentProvider,t,n)):r(\"<ui-view></ui-view>\")},e.prototype.fromString=function(e,t){return D(e)?e(t):e},e.prototype.fromUrl=function(e,t){return D(e)&&(e=e(t)),null==e?null:this._useHttp?this.$http.get(e,{cache:this.$templateCache,headers:{Accept:\"text/html\"}}).then(function(e){return e.data}):this.$templateRequest(e)},e.prototype.fromProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt(\"\",i,r).get(n)},e.prototype.fromComponentProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt(\"\",i,r).get(n)},e.prototype.makeComponentTemplate=function(u,l,e,c){c=c||{};var d=3<=S.version.minor?\"::\":\"\",p=function(e){var t=je(e);return/^(x|data)-/.exec(t)?\"x-\"+t:t},t=function(e){var t=V.$injector.get(e+\"Directive\");if(!t||!t.length)throw new Error(\"Unable to find component named '\"+e+\"'\");return t.map(kr).reduce(fe,[])}(e).map(function(e){var t=e.name,n=e.type,r=p(t);if(u.attr(r)&&!c[t])return r+\"='\"+u.attr(r)+\"'\";var i=c[t]||t;if(\"@\"===n)return r+\"='{{\"+d+\"$resolve.\"+i+\"}}'\";if(\"&\"!==n)return r+\"='\"+d+\"$resolve.\"+i+\"'\";var o=l.getResolvable(i),a=o&&o.data,s=a&&V.$injector.annotate(a)||[];return r+\"='$resolve.\"+i+(A(a)?\"[\"+(a.length-1)+\"]\":\"\")+\"(\"+s.join(\",\")+\")'\"}).join(\" \"),n=p(e);return\"<\"+n+\" \"+t+\"></\"+n+\">\"},e}();var kr=function(e){return T(e.bindToController)?Dr(e.bindToController):Dr(e.scope)},Dr=function(t){return Object.keys(t||{}).map(function(e){return[e,/^([=<@&])[?]?(.*)/.exec(t[e])]}).filter(function(e){return k(e)&&A(e[1])}).map(function(e){return{name:e[1][2]||e[0],type:e[1][1]}})},xr=function(){function n(e,t){this.stateRegistry=e,this.stateService=t,B(p(n.prototype),this,p(this))}return n.prototype.decorator=function(e,t){return this.stateRegistry.decorator(e,t)||this},n.prototype.state=function(e,t){return T(e)?t=e:t.name=e,this.stateRegistry.register(t),this},n.prototype.onInvalid=function(e){return this.stateService.onInvalid(e)},n}(),Or=function(n){return function(e,t){var i=e[n],o=\"onExit\"===n?\"from\":\"to\";return i?function(e,t){var n=new At(e.treeChanges(o)).subContext(t.$$state()),r=Y(zr(n),{$state$:t,$transition$:e});return V.$injector.invoke(i,this,r)}:void 0}},Tr=function(){function e(e){this._urlListeners=[],this.$locationProvider=e;var t=p(e);B(t,this,t,[\"hashPrefix\"])}return e.monkeyPatchPathParameterType=function(e){var t=e.urlMatcherFactory.type(\"path\");t.encode=function(e){return null!=e?e.toString().replace(/(~|\\/)/g,function(e){return{\"~\":\"~~\",\"/\":\"~2F\"}[e]}):e},t.decode=function(e){return null!=e?e.toString().replace(/(~~|~2F)/g,function(e){return{\"~~\":\"~\",\"~2F\":\"/\"}[e]}):e}},e.prototype.dispose=function(){},e.prototype.onChange=function(e){var t=this;return this._urlListeners.push(e),function(){return Q(t._urlListeners)(e)}},e.prototype.html5Mode=function(){var e=this.$locationProvider.html5Mode();return(e=T(e)?e.enabled:e)&&this.$sniffer.history},e.prototype.baseHref=function(){return this._baseHref||(this._baseHref=this.$browser.baseHref()||this.$window.location.pathname)},e.prototype.url=function(e,t,n){return void 0===t&&(t=!1),k(e)&&this.$location.url(e),t&&this.$location.replace(),n&&this.$location.state(n),this.$location.url()},e.prototype._runtimeServices=function(e,t,n,r,i){var o=this;this.$location=t,this.$sniffer=n,this.$browser=r,this.$window=i,e.$on(\"$locationChangeSuccess\",function(t){return o._urlListeners.forEach(function(e){return e(t)})});var a=p(t);B(a,this,a,[\"replace\",\"path\",\"search\",\"hash\"]),B(a,this,a,[\"port\",\"protocol\",\"host\"])},e}(),Ar=function(){function n(e){this.router=e}return n.injectableHandler=function(t,n){return function(e){return V.$injector.invoke(n,null,{$match:e,$stateParams:t.globals.params})}},n.prototype.$get=function(){var e=this.router.urlService;return this.router.urlRouter.update(!0),e.interceptDeferred||e.listen(),this.router.urlRouter},n.prototype.rule=function(e){var t=this;if(!D(e))throw new Error(\"'rule' must be a function\");var n=new ln(function(){return e(V.$injector,t.router.locationService)},q);return this.router.urlService.rules.rule(n),this},n.prototype.otherwise=function(e){var t=this,n=this.router.urlService.rules;if(O(e))n.otherwise(e);else{if(!D(e))throw new Error(\"'rule' must be a string or function\");n.otherwise(function(){return e(V.$injector,t.router.locationService)})}return this},n.prototype.when=function(e,t){return(A(t)||D(t))&&(t=n.injectableHandler(this.router,t)),this.router.urlService.rules.when(e,t),this},n.prototype.deferIntercept=function(e){this.router.urlService.deferIntercept(e)},n}();S.module(\"ui.router.angular1\",[]);var Er=S.module(\"ui.router.init\",[\"ng\"]),Pr=S.module(\"ui.router.util\",[\"ui.router.init\"]),Mr=S.module(\"ui.router.router\",[\"ui.router.util\"]),Rr=S.module(\"ui.router.state\",[\"ui.router.router\",\"ui.router.util\",\"ui.router.angular1\"]),Ir=S.module(\"ui.router\",[\"ui.router.init\",\"ui.router.state\",\"ui.router.angular1\"]),Vr=(S.module(\"ui.router.compat\",[\"ui.router\"]),null);function Lr(e){(Vr=this.router=new $n).stateProvider=new xr(Vr.stateRegistry,Vr.stateService),Vr.stateRegistry.decorator(\"views\",$r),Vr.stateRegistry.decorator(\"onExit\",Or(\"onExit\")),Vr.stateRegistry.decorator(\"onRetain\",Or(\"onRetain\")),Vr.stateRegistry.decorator(\"onEnter\",Or(\"onEnter\")),Vr.viewService._pluginapi._viewConfigFactory(\"ng1\",wr());var s=Vr.locationService=Vr.locationConfig=new Tr(e);function t(e,t,n,r,i,o,a){return s._runtimeServices(i,e,r,t,n),delete Vr.router,delete Vr.$get,Vr}return Tr.monkeyPatchPathParameterType(Vr),((Vr.router=Vr).$get=t).$inject=[\"$location\",\"$browser\",\"$window\",\"$sniffer\",\"$rootScope\",\"$http\",\"$templateCache\"],Vr}Lr.$inject=[\"$locationProvider\"];var Fr=function(n){return[\"$uiRouterProvider\",function(e){var t=e.router[n];return t.$get=function(){return t},t}]};function jr(t,e,n){if(V.$injector=t,V.$q=e,!t.hasOwnProperty(\"strictDi\"))try{t.invoke(function(e){})}catch(e){t.strictDi=!!/strict mode/.exec(e&&e.toString())}n.stateRegistry.get().map(function(e){return e.$$state().resolvables}).reduce(fe,[]).filter(function(e){return\"deferred\"===e.deps}).forEach(function(e){return e.deps=t.annotate(e.resolveFn,t.strictDi)})}jr.$inject=[\"$injector\",\"$q\",\"$uiRouter\"];function Hr(e){e.$watch(function(){lt.approximateDigests++})}Hr.$inject=[\"$rootScope\"],Er.provider(\"$uiRouter\",Lr),Mr.provider(\"$urlRouter\",[\"$uiRouterProvider\",function(e){return e.urlRouterProvider=new Ar(e)}]),Pr.provider(\"$urlService\",Fr(\"urlService\")),Pr.provider(\"$urlMatcherFactory\",[\"$uiRouterProvider\",function(){return Vr.urlMatcherFactory}]),Pr.provider(\"$templateFactory\",function(){return new Cr}),Rr.provider(\"$stateRegistry\",Fr(\"stateRegistry\")),Rr.provider(\"$uiRouterGlobals\",Fr(\"globals\")),Rr.provider(\"$transitions\",Fr(\"transitionService\")),Rr.provider(\"$state\",[\"$uiRouterProvider\",function(){return Y(Vr.stateProvider,{$get:function(){return Vr.stateService}})}]),Rr.factory(\"$stateParams\",[\"$uiRouter\",function(e){return e.globals.params}]),Ir.factory(\"$view\",function(){return Vr.viewService}),Ir.service(\"$trace\",function(){return lt}),Ir.run(Hr),Pr.run([\"$urlMatcherFactory\",function(e){}]),Rr.run([\"$state\",function(e){}]),Mr.run([\"$urlRouter\",function(e){}]),Er.run(jr);var Nr,Yr,Ur,qr,zr=function(n){return n.getTokens().filter(O).map(function(e){var t=n.getResolvable(e);return[e,\"NOWAIT\"===n.getPolicy(t).async?t.promise:t.data]}).reduce(ke,{})};function Br(e){var t,n=e.match(/^\\s*({[^}]*})\\s*$/);if(n&&(e=\"(\"+n[1]+\")\"),!(t=e.replace(/\\n/g,\" \").match(/^\\s*([^(]*?)\\s*(\\((.*)\\))?\\s*$/))||4!==t.length)throw new Error(\"Invalid state ref '\"+e+\"'\");return{state:t[1]||null,paramExpr:t[3]||null}}function Wr(e){var t=e.parent().inheritedData(\"$uiView\"),n=C(\"$cfg.path\")(t);return n?De(n).state.name:void 0}function Gr(e,t,n){var r=n.uiState||e.current.name,i=Y(function(e,t){return{relative:Wr(e)||t.$current,inherit:!0,source:\"sref\"}}(t,e),n.uiStateOpts||{}),o=e.href(r,n.uiStateParams,i);return{uiState:r,uiStateParams:n.uiStateParams,uiStateOpts:i,href:o}}function Kr(e){var t=\"[object SVGAnimatedString]\"===Object.prototype.toString.call(e.prop(\"href\")),n=\"FORM\"===e[0].nodeName;return{attr:n?\"action\":t?\"xlink:href\":\"href\",isAnchor:\"A\"===e.prop(\"tagName\").toUpperCase(),clickable:!n}}function Qr(o,a,s,u,l){return function(e){var t=e.which||e.button,n=l();if(!(1<t||e.ctrlKey||e.metaKey||e.shiftKey||o.attr(\"target\"))){var r=s(function(){o.attr(\"disabled\")||a.go(n.uiState,n.uiStateParams,n.uiStateOpts)});e.preventDefault();var i=u.isAnchor&&!n.href?1:0;e.preventDefault=function(){i--<=0&&s.cancel(r)}}}}function Zr(i,e,o,t){var a;t&&(a=t.events),A(a)||(a=[\"click\"]);for(var n=i.on?\"on\":\"bind\",r=0,s=a;r<s.length;r++){var u=s[r];i[n](u,o)}e.$on(\"$destroy\",function(){for(var e=i.off?\"off\":\"unbind\",t=0,n=a;t<n.length;t++){var r=n[t];i[e](r,o)}})}function Xr(r){var e=function(e,t,n){return r.is(e,t,n)};return e.$stateful=!0,e}function Jr(r){var e=function(e,t,n){return r.includes(e,t,n)};return e.$stateful=!0,e}function ei(g,m,v,e,y,t){var w=C(\"viewDecl.controllerAs\"),b=C(\"viewDecl.resolveAs\");return{restrict:\"ECA\",priority:-400,compile:function(e){var f=e.html();return e.empty(),function(t,n){var e=n.data(\"$uiView\");if(!e)return n.html(f),void g(n.contents())(t);var r=e.$cfg||{viewDecl:{},getTemplate:z},i=r.path&&new At(r.path);n.html(r.getTemplate(n,i)||f),lt.traceUIViewFill(e.$uiView,n.html());var o=g(n.contents()),a=r.controller,s=w(r),u=b(r),l=i&&zr(i);if(t[u]=l,a){var c=m(a,Y({},l,{$scope:t,$element:n}));s&&(t[s]=c,t[s][u]=l),n.data(\"$ngControllerController\",c),n.children().data(\"$ngControllerController\",c),ri(y,v,c,t,r)}if(O(r.component))var d=je(r.component),p=new RegExp(\"^(x-|data-)?\"+d+\"$\",\"i\"),h=t.$watch(function(){var e=[].slice.call(n[0].children).filter(function(e){return e&&e.tagName&&p.exec(e.tagName)});return e&&S.element(e).data(\"$\"+r.component+\"Controller\")},function(e){e&&(ri(y,v,e,t,r),h())});o(t)}}}}Nr=[\"$uiRouter\",\"$timeout\",function(p,h){var f=p.stateService;return{restrict:\"A\",require:[\"?^uiSrefActive\",\"?^uiSrefActiveEq\"],link:function(e,t,n,r){var i,o=Kr(t),a=r[1]||r[0],s=null,u={},l=function(){return Gr(f,t,u)},c=Br(n.uiSref);function d(){var e=l();s&&s(),a&&(s=a.$$addStateInfo(e.uiState,e.uiStateParams)),null!=e.href&&n.$set(o.attr,e.href)}u.uiState=c.state,u.uiStateOpts=n.uiSrefOpts?e.$eval(n.uiSrefOpts):{},c.paramExpr&&(e.$watch(c.paramExpr,function(e){u.uiStateParams=Y({},e),d()},!0),u.uiStateParams=Y({},e.$eval(c.paramExpr))),d(),e.$on(\"$destroy\",p.stateRegistry.onStatesChanged(d)),e.$on(\"$destroy\",p.transitionService.onSuccess({},d)),o.clickable&&(i=Qr(t,f,h,o,l),Zr(t,e,i,u.uiStateOpts))}}}],Yr=[\"$uiRouter\",\"$timeout\",function(h,f){var g=h.stateService;return{restrict:\"A\",require:[\"?^uiSrefActive\",\"?^uiSrefActiveEq\"],link:function(n,e,r,t){var i,o=Kr(e),a=t[1]||t[0],s=null,u={},l=function(){return Gr(g,e,u)},c=[\"uiState\",\"uiStateParams\",\"uiStateOpts\"],d=c.reduce(function(e,t){return e[t]=z,e},{});function p(){var e=l();s&&s(),a&&(s=a.$$addStateInfo(e.uiState,e.uiStateParams)),null!=e.href&&r.$set(o.attr,e.href)}c.forEach(function(t){u[t]=r[t]?n.$eval(r[t]):null,r.$observe(t,function(e){d[t](),d[t]=n.$watch(e,function(e){u[t]=e,p()},!0)})}),p(),n.$on(\"$destroy\",h.stateRegistry.onStatesChanged(p)),n.$on(\"$destroy\",h.transitionService.onSuccess({},p)),o.clickable&&(i=Qr(e,g,f,o,l),Zr(e,n,i,u.uiStateOpts))}}}],Ur=[\"$state\",\"$stateParams\",\"$interpolate\",\"$uiRouter\",function(f,e,g,m){return{restrict:\"A\",controller:[\"$scope\",\"$element\",\"$attrs\",function(s,u,e){var l,r,t,n,i,c=[];l=g(e.uiSrefActiveEq||\"\",!1)(s);try{r=s.$eval(e.uiSrefActive)}catch(e){}function o(e){e.promise.then(h,z)}function a(){d(r)}function d(e){T(e)&&(c=[],N(e,function(e,t){var n=function(e,t){var n=Br(e);p(n.state,s.$eval(n.paramExpr),t)};O(e)?n(e,t):A(e)&&N(e,function(e){n(e,t)})}))}function p(e,t,n){var r={state:f.get(e,Wr(u))||{name:e},params:t,activeClass:n};return c.push(r),function(){Q(c)(r)}}function h(){var t=function(e){return e.split(/\\s/).filter(q)},e=function(e){return e.map(function(e){return e.activeClass}).map(t).reduce(fe,[])},n=e(c).concat(t(l)).reduce(ve,[]),r=e(c.filter(function(e){return f.includes(e.state.name,e.params)})),i=!!c.filter(function(e){return f.is(e.state.name,e.params)}).length?t(l):[],o=r.concat(i).reduce(ve,[]),a=n.filter(function(e){return!G(o,e)});s.$evalAsync(function(){o.forEach(function(e){return u.addClass(e)}),a.forEach(function(e){return u.removeClass(e)})})}d(r=r||g(e.uiSrefActive||\"\",!1)(s)),this.$$addStateInfo=function(e,t){if(!(T(r)&&0<c.length)){var n=p(e,t,r);return h(),n}},s.$on(\"$destroy\",(t=m.stateRegistry.onStatesChanged(a),n=m.transitionService.onStart({},o),i=s.$on(\"$stateChangeSuccess\",h),function(){t(),n(),i()})),m.globals.transition&&o(m.globals.transition),h()}]}}],S.module(\"ui.router.state\").directive(\"uiSref\",Nr).directive(\"uiSrefActive\",Ur).directive(\"uiSrefActiveEq\",Ur).directive(\"uiState\",Yr),Xr.$inject=[\"$state\"],Jr.$inject=[\"$state\"],S.module(\"ui.router.state\").filter(\"isState\",Xr).filter(\"includedByState\",Jr),qr=[\"$view\",\"$animate\",\"$uiViewScroll\",\"$interpolate\",\"$q\",function(i,o,y,w,b){var $={$cfg:{viewDecl:{$context:i._pluginapi._rootViewContext()}},$uiView:{}},_={count:0,restrict:\"ECA\",terminal:!0,priority:400,transclude:\"element\",compile:function(e,t,v){return function(a,s,e){var u,l,c,d,t,p=e.onload||\"\",h=e.autoscroll,f={enter:function(e,t,n){2<S.version.minor?o.enter(e,null,t).then(n):o.enter(e,null,t,n)},leave:function(e,t){2<S.version.minor?o.leave(e).then(t):o.leave(e,t)}},n=s.inheritedData(\"$uiView\")||$,g=w(e.uiView||e.name||\"\")(a)||\"$default\",m={$type:\"ng1\",id:_.count++,name:g,fqn:n.$uiView.fqn?n.$uiView.fqn+\".\"+g:g,config:null,configUpdated:function(e){if(e&&!(e instanceof Sr))return;if(function(e,t){return e===t}(d,e))return;lt.traceUIViewConfigUpdated(m,e&&e.viewDecl&&e.viewDecl.$context),r(d=e)},get creationContext(){var e=C(\"$cfg.viewDecl.$context\")(n),t=C(\"$uiView.creationContext\")(n);return e||t}};function r(e){var t=a.$new(),n=b.defer(),r=b.defer(),i={$cfg:e,$uiView:m},o={$animEnter:n.promise,$animLeave:r.promise,$$animLeave:r};t.$emit(\"$viewContentLoading\",g),l=v(t,function(e){e.data(\"$uiViewAnim\",o),e.data(\"$uiView\",i),f.enter(e,s,function(){n.resolve(),c&&c.$emit(\"$viewContentAnimationEnded\"),(k(h)&&!h||a.$eval(h))&&y(e)}),function(){if(u&&(lt.traceUIViewEvent(\"Removing (previous) el\",u.data(\"$uiView\")),u.remove(),u=null),c&&(lt.traceUIViewEvent(\"Destroying scope\",m),c.$destroy(),c=null),l){var e=l.data(\"$uiViewAnim\");lt.traceUIViewEvent(\"Animate out\",e),f.leave(l,function(){e.$$animLeave.resolve(),u=null}),u=l,l=null}}()}),(c=t).$emit(\"$viewContentLoaded\",e||d),c.$eval(p)}lt.traceUIViewEvent(\"Linking\",m),s.data(\"$uiView\",{$uiView:m}),r(),t=i.registerUIView(m),a.$on(\"$destroy\",function(){lt.traceUIViewEvent(\"Destroying/Unregistering\",m),t()})}}};return _}],ei.$inject=[\"$compile\",\"$controller\",\"$transitions\",\"$view\",\"$q\",\"$timeout\"];var ti=\"function\"==typeof S.module(\"ui.router\").component,ni=0;function ri(r,e,l,t,n){!D(l.$onInit)||n.viewDecl.component&&ti||l.$onInit();var c=De(n.path).state.self,i={bind:l};if(D(l.uiOnParamsChanged)){var d=new At(n.path).getResolvable(\"$transition$\").data;t.$on(\"$destroy\",e.onSuccess({},function(e){if(e!==d&&-1===e.exiting().indexOf(c)){var n=e.params(\"to\"),r=e.params(\"from\"),t=function(e){return e.paramSchema},i=e.treeChanges(\"to\").map(t).reduce(fe,[]),o=e.treeChanges(\"from\").map(t).reduce(fe,[]),a=i.filter(function(e){var t=o.indexOf(e);return-1===t||!o[t].type.equals(n[e.id],r[e.id])});if(a.length){var s=a.map(function(e){return e.id}),u=se(n,function(e,t){return-1!==s.indexOf(t)});l.uiOnParamsChanged(u,e)}}},i))}if(D(l.uiCanExit)){var o=ni++,a=\"_uiCanExitIds\",s=function(e){return!!e&&(e[a]&&!0===e[a][o]||s(e.redirectedFrom()))},u={exiting:c.name};t.$on(\"$destroy\",e.onBefore(u,function(e){var t,n=e[a]=e[a]||{};return s(e)||(t=r.when(l.uiCanExit(e))).then(function(e){return n[o]=!1!==e}),t},i))}}S.module(\"ui.router.state\").directive(\"uiView\",qr),S.module(\"ui.router.state\").directive(\"uiView\",ei),S.module(\"ui.router.state\").provider(\"$uiViewScroll\",function(){var n=!1;this.useAnchorScroll=function(){n=!0},this.$get=[\"$anchorScroll\",\"$timeout\",function(e,t){return n?e:function(e){return t(function(){e[0].scrollIntoView()},0,!1)}}]});g.default=\"ui.router\",g.core=yr,g.watchDigests=Hr,g.getLocals=zr,g.getNg1ViewConfigFactory=wr,g.ng1ViewsBuilder=$r,g.Ng1ViewConfig=Sr,g.StateProvider=xr,g.UrlRouterProvider=Ar,g.root=L,g.fromJson=j,g.toJson=H,g.forEach=N,g.extend=Y,g.equals=U,g.identity=q,g.noop=z,g.createProxyFunctions=B,g.inherit=W,g.inArray=G,g._inArray=K,g.removeFrom=Q,g._removeFrom=Z,g.pushTo=X,g._pushTo=J,g.deregAll=ee,g.defaults=te,g.mergeR=ne,g.ancestors=re,g.pick=ie,g.omit=oe,g.pluck=ae,g.filter=se,g.find=ue,g.mapObj=le,g.map=ce,g.values=de,g.allTrueR=pe,g.anyTrueR=he,g.unnestR=fe,g.flattenR=ge,g.pushR=me,g.uniqR=ve,g.unnest=ye,g.flatten=we,g.assertPredicate=be,g.assertMap=$e,g.assertFn=_e,g.pairs=Se,g.arrayTuples=Ce,g.applyPairs=ke,g.tail=De,g.copy=xe,g._extend=Oe,g.silenceUncaughtInPromise=Ee,g.silentRejection=Pe,g.makeStub=I,g.services=V,g.Glob=Me,g.curry=l,g.compose=n,g.pipe=u,g.prop=v,g.propEq=y,g.parse=C,g.not=d,g.and=r,g.or=i,g.all=a,g.any=s,g.is=c,g.eq=o,g.val=p,g.invoke=h,g.pattern=f,g.isUndefined=b,g.isDefined=k,g.isNull=$,g.isNullOrUndefined=_,g.isFunction=D,g.isNumber=x,g.isString=O,g.isObject=T,g.isArray=A,g.isDate=E,g.isRegExp=P,g.isInjectable=M,g.isPromise=R,g.Queue=Re,g.maxLength=Le,g.padString=Fe,g.kebobString=je,g.functionToString=He,g.fnToString=Ne,g.stringify=qe,g.beforeAfterSubstr=ze,g.hostRegex=Be,g.stripLastPathElement=We,g.splitHash=Ge,g.splitQuery=Ke,g.splitEqual=Qe,g.trimHashVal=Ze,g.splitOnDelim=Xe,g.joinNeighborsR=Je,g.Trace=ut,g.trace=lt,g.Param=vt,g.ParamTypes=yt,g.StateParams=wt,g.ParamType=ct,g.PathNode=bt,g.PathUtils=_t,g.resolvePolicies=St,g.defaultResolvePolicy=Ct,g.Resolvable=kt,g.NATIVE_INJECTOR_TOKEN=Tt,g.ResolveContext=At,g.resolvablesBuilder=Ft,g.StateBuilder=Nt,g.StateObject=Yt,g.StateMatcher=Ut,g.StateQueueManager=qt,g.StateRegistry=zt,g.StateService=qn,g.TargetState=$t,g.HookBuilder=Zt,g.matchState=Gt,g.RegisteredHook=Kt,g.makeEvent=Qt,g.Rejection=Ve,g.Transition=Jt,g.TransitionHook=Wt,g.TransitionEventType=jn,g.defaultTransOpts=Yn,g.TransitionService=Un,g.UrlRules=gn,g.UrlConfig=mn,g.UrlMatcher=rn,g.ParamFactory=an,g.UrlMatcherFactory=sn,g.UrlRouter=dn,g.UrlRuleFactory=un,g.BaseUrlRule=ln,g.UrlService=vn,g.ViewService=pn,g.UIRouterGlobals=hn,g.UIRouter=$n,g.$q=zn,g.$injector=Kn,g.BaseLocationServices=ir,g.HashLocationService=ar,g.MemoryLocationService=ur,g.PushStateLocationService=cr,g.MemoryLocationConfig=dr,g.BrowserLocationConfig=pr,g.keyValsToObjectR=Qn,g.getParams=Zn,g.parseUrl=Xn,g.buildUrl=Jn,g.locationPluginFactory=er,g.servicesPlugin=hr,g.hashLocationPlugin=fr,g.pushStateLocationPlugin=gr,g.memoryLocationPlugin=mr,g.UIRouterPluginBase=vr,Object.defineProperty(g,\"__esModule\",{value:!0})}),function(b,l){\"use strict\";var $=[\"ng\",\"oc.lazyLoad\"],_={},S=[],C=[],k=[],D=[],x=b.noop,O={},T=[];b.module(\"oc.lazyLoad\",[\"ng\"]).provider(\"$ocLazyLoad\",[\"$controllerProvider\",\"$provide\",\"$compileProvider\",\"$filterProvider\",\"$injector\",\"$animateProvider\",function(e,t,n,r,i,o){function c(e,t,n){if(t){var r,i,o,a=[];for(r=t.length-1;0<=r;r--)if(i=t[r],b.isString(i)||(i=h(i)),i&&-1===T.indexOf(i)&&(!f[i]||-1!==k.indexOf(i))){var s=-1===$.indexOf(i);if(o=E(i),s&&($.push(i),c(e,o.requires,n)),0<o._runBlocks.length)for(O[i]=[];0<o._runBlocks.length;)O[i].push(o._runBlocks.shift());b.isDefined(O[i])&&(s||n.rerun)&&(a=a.concat(O[i])),d(e,o._invokeQueue,i,n.reconfig),d(e,o._configBlocks,i,n.reconfig),x(s?\"ocLazyLoad.moduleLoaded\":\"ocLazyLoad.moduleReloaded\",i),t.pop(),T.push(i)}var u=e.getInstanceInjector();b.forEach(a,function(e){u.invoke(e)})}}function p(e,n){function i(e){return b.isArray(e)?u(e.toString()):b.isObject(e)?u(w(e)):b.isDefined(e)&&null!==e?u(e.toString()):e}var r=e[2][0],o=e[1],a=!1;b.isUndefined(_[n])&&(_[n]={}),b.isUndefined(_[n][o])&&(_[n][o]={});var s=function(e,t){_[n][o].hasOwnProperty(e)||(_[n][o][e]=[]),function(e,t){var n,r=!0;return t.length&&(n=i(e),b.forEach(t,function(e){r=r&&i(e)!==n})),r}(t,_[n][o][e])&&(a=!0,_[n][o][e].push(t),x(\"ocLazyLoad.componentLoaded\",[n,o,e]))};if(b.isString(r))s(r,e[2][1]);else{if(!b.isObject(r))return!1;b.forEach(r,function(e,t){b.isString(e)?s(e,r[1]):s(t,e)})}return a}function d(e,t,n,r){var i,o,a,s;if(t)for(i=0,o=t.length;i<o;i++)if(a=t[i],b.isArray(a)){if(null!==e){if(!e.hasOwnProperty(a[0]))throw new Error(\"unsupported provider \"+a[0]);s=e[a[0]]}var u=p(a,n);if(\"invoke\"!==a[1])u&&b.isDefined(s)&&s[a[1]].apply(s,a[2]);else{var l=function(e){var t=S.indexOf(n+\"-\"+e);(-1===t||r)&&(-1===t&&S.push(n+\"-\"+e),b.isDefined(s)&&s[a[1]].apply(s,a[2]))};if(b.isFunction(a[2][0]))l(a[2][0]);else if(b.isArray(a[2][0]))for(var c=0,d=a[2][0].length;c<d;c++)b.isFunction(a[2][0][c])&&l(a[2][0][c])}}}function h(e){var t=null;return b.isString(e)?t=e:b.isObject(e)&&e.hasOwnProperty(\"name\")&&b.isString(e.name)&&(t=e.name),t}function a(e){if(!b.isString(e))return!1;try{return E(e)}catch(e){if(/No module/.test(e)||-1<e.message.indexOf(\"$injector:nomod\"))return!1}}var f={},s={$controllerProvider:e,$compileProvider:n,$filterProvider:r,$provide:t,$injector:i,$animateProvider:o},g=!1,m=!1,v=[],y={};v.push=function(e){-1===this.indexOf(e)&&Array.prototype.push.apply(this,arguments)},this.config=function(e){b.isDefined(e.modules)&&(b.isArray(e.modules)?b.forEach(e.modules,function(e){f[e.name]=e}):f[e.modules.name]=e.modules),b.isDefined(e.debug)&&(g=e.debug),b.isDefined(e.events)&&(m=e.events)},this._init=function(r){if(0===C.length){var t=[r],i=[\"ng:app\",\"ng-app\",\"x-ng-app\",\"data-ng-app\"],o=/\\sng[:\\-]app(:\\s*([\\w\\d_]+);?)?\\s/,n=function(e){return e&&t.push(e)};b.forEach(i,function(e){i[e]=!0,n(document.getElementById(e)),e=e.replace(\":\",\"\\\\:\"),void 0!==r[0]&&r[0].querySelectorAll&&(b.forEach(r[0].querySelectorAll(\".\"+e),n),b.forEach(r[0].querySelectorAll(\".\"+e+\"\\\\:\"),n),b.forEach(r[0].querySelectorAll(\"[\"+e+\"]\"),n))}),b.forEach(t,function(e){if(0===C.length){var t=\" \"+r.className+\" \",n=o.exec(t);n?C.push((n[2]||\"\").replace(/\\s+/g,\",\")):b.forEach(e.attributes,function(e){0===C.length&&i[e.name]&&C.push(e.value)})}})}0!==C.length||(l.jasmine||l.mocha)&&b.isDefined(b.mock)||console.error(\"No module found during bootstrap, unable to init ocLazyLoad. You should always use the ng-app directive or angular.boostrap when you use ocLazyLoad.\");b.forEach(C,function(e){!function e(t){if(-1===$.indexOf(t)){$.push(t);var n=b.module(t);d(null,n._invokeQueue,t),d(null,n._configBlocks,t),b.forEach(n.requires,e)}}(e)}),C=[],D.pop()};var w=function(t){try{return JSON.stringify(t)}catch(e){var n=[];return JSON.stringify(t,function(e,t){if(b.isObject(t)&&null!==t){if(-1!==n.indexOf(t))return;n.push(t)}return t})}},u=function(e){var t,n,r=0;if(0==e.length)return r;for(t=0,n=e.length;t<n;t++)r=(r<<5)-r+e.charCodeAt(t),r|=0;return r};this.$get=[\"$log\",\"$rootElement\",\"$rootScope\",\"$cacheFactory\",\"$q\",function(n,e,r,t,u){function l(e){var t=u.defer();return n.error(e.message),t.reject(e),t.promise}var i,o=t(\"ocLazyLoad\");return g||((n={}).error=b.noop,n.warn=b.noop,n.info=b.noop),s.getInstanceInjector=function(){return i||(i=e.data(\"$injector\")||b.injector())},{_broadcast:x=function(e,t){m&&r.$broadcast(e,t),g&&n.info(e,t)},_$log:n,_getFilesCache:function(){return o},toggleWatch:function(e){e?D.push(!0):D.pop()},getModuleConfig:function(e){if(!b.isString(e))throw new Error(\"You need to give the name of the module to get\");return f[e]?b.copy(f[e]):null},setModuleConfig:function(e){if(!b.isObject(e))throw new Error(\"You need to give the module config object to set\");return f[e.name]=e},getModules:function(){return $},isLoaded:function(e){var t,n;if(b.isString(e)&&(e=[e]),b.isArray(e)){var r,i;for(r=0,i=e.length;r<i;r++)if(t=e[r],n=void 0,(n=-1<$.indexOf(t))||(n=!!a(t)),!n)return!1;return!0}throw new Error(\"You need to define the module(s) name(s)\")},_getModuleName:h,_getModule:function(t){try{return E(t)}catch(e){throw(/No module/.test(e)||-1<e.message.indexOf(\"$injector:nomod\"))&&(e.message='The module \"'+w(t)+'\" that you are trying to load does not exist. '+e.message),e}},moduleExists:a,_loadDependencies:function(r,i){var e,t,o,a=[],s=this;if(null===(r=s._getModuleName(r)))return u.when();try{e=s._getModule(r)}catch(e){return l(e)}return t=s.getRequires(e),b.forEach(t,function(t){if(b.isString(t)){var e=s.getModuleConfig(t);if(null===e)return void v.push(t);(t=e).name=void 0}if(s.moduleExists(t.name))return 0!==(o=t.files.filter(function(e){return s.getModuleConfig(t.name).files.indexOf(e)<0})).length&&s._$log.warn('Module \"',r,'\" attempted to redefine configuration for dependency. \"',t.name,'\"\\n Additional Files Loaded:',o),b.isDefined(s.filesLoader)?void a.push(s.filesLoader(t,i).then(function(){return s._loadDependencies(t)})):l(new Error(\"Error: New dependencies need to be loaded from external files (\"+t.files+\"), but no loader has been defined.\"));if(b.isArray(t)){var n=[];b.forEach(t,function(e){var t=s.getModuleConfig(e);null===t?n.push(e):t.files&&(n=n.concat(t.files))}),0<n.length&&(t={files:n})}else b.isObject(t)&&t.hasOwnProperty(\"name\")&&t.name&&(s.setModuleConfig(t),v.push(t.name));if(b.isDefined(t.files)&&0!==t.files.length){if(!b.isDefined(s.filesLoader))return l(new Error('Error: the module \"'+t.name+'\" is defined in external files ('+t.files+\"), but no loader has been defined.\"));a.push(s.filesLoader(t,i).then(function(){return s._loadDependencies(t)}))}}),u.all(a)},inject:function(e){var n=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],t=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],r=this,i=u.defer();if(b.isDefined(e)&&null!==e){if(b.isArray(e)){var o=[];return b.forEach(e,function(e){o.push(r.inject(e,n,t))}),u.all(o)}r._addToLoadList(r._getModuleName(e),!0,t)}if(0<C.length){var a=C.slice();!function e(t){v.push(t),y[t]=i.promise,r._loadDependencies(t,n).then(function(){try{T=[],c(s,v,n)}catch(e){return r._$log.error(e.message),void i.reject(e)}0<C.length?e(C.shift()):i.resolve(a)},function(e){i.reject(e)})}(C.shift())}else{if(n&&n.name&&y[n.name])return y[n.name];i.resolve()}return i.promise},getRequires:function(e){var t=[];return b.forEach(e.requires,function(e){-1===$.indexOf(e)&&t.push(e)}),t},_invokeQueue:d,_registerInvokeList:p,_register:c,_addToLoadList:A,_unregister:function(e){b.isDefined(e)&&b.isArray(e)&&b.forEach(e,function(e){_[e]=void 0})}}}],this._init(b.element(l.document))}]);var r=b.bootstrap;b.bootstrap=function(e,t,n){return $=[\"ng\",\"oc.lazyLoad\"],_={},S=[],C=[],k=[],D=[],x=b.noop,O={},T=[],b.forEach(t.slice(),function(e){A(e,!0,!0)}),r(e,t,n)};var A=function(e,t,n){(0<D.length||t)&&b.isString(e)&&-1===C.indexOf(e)&&(C.push(e),n&&k.push(e))},E=b.module;b.module=function(e,t,n){return A(e,!1,!0),E(e,t,n)},\"undefined\"!=typeof module&&\"undefined\"!=typeof exports&&module.exports===exports&&(module.exports=\"oc.lazyLoad\")}(angular,window),function(l){\"use strict\";l.module(\"oc.lazyLoad\").directive(\"ocLazyLoad\",[\"$ocLazyLoad\",\"$compile\",\"$animate\",\"$parse\",\"$timeout\",function(o,a,s,u,e){return{restrict:\"A\",terminal:!0,priority:1e3,compile:function(e,t){var i=e[0].innerHTML;return e.html(\"\"),function(t,n,e){var r=u(e.ocLazyLoad);t.$watch(function(){return r(t)||e.ocLazyLoad},function(e){l.isDefined(e)&&o.load(e).then(function(){s.enter(i,n),a(n.contents())(t)})},!0)}}}}])}(angular),function(S){\"use strict\";S.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",\"$window\",\"$interval\",function(v,y,w,b){var $=!1,_=w.document.getElementsByTagName(\"head\")[0]||w.document.getElementsByTagName(\"body\")[0];return v.buildElement=function(e,t,n){var r,i,o=y.defer(),a=v._getFilesCache(),s=function(e){var t=(new Date).getTime();return 0<=e.indexOf(\"?\")?\"&\"===e.substring(0,e.length-1)?e+\"_dc=\"+t:e+\"&_dc=\"+t:e+\"?_dc=\"+t};switch(S.isUndefined(a.get(t))&&a.put(t,o.promise),e){case\"css\":(r=w.document.createElement(\"link\")).type=\"text/css\",r.rel=\"stylesheet\",r.href=!1===n.cache?s(t):t;break;case\"js\":(r=w.document.createElement(\"script\")).src=!1===n.cache?s(t):t;break;default:a.remove(t),o.reject(new Error('Requested type \"'+e+'\" is not known. Could not inject \"'+t+'\"'))}r.onload=r.onreadystatechange=function(e){r.readyState&&!/^c|loade/.test(r.readyState)||i||(r.onload=r.onreadystatechange=null,i=1,v._broadcast(\"ocLazyLoad.fileLoaded\",t),o.resolve(r))},r.onerror=function(){a.remove(t),o.reject(new Error(\"Unable to load \"+t))},r.async=n.serie?0:1;var u=_.lastChild;if(n.insertBefore){var l=S.element(S.isDefined(window.jQuery)?n.insertBefore:document.querySelector(n.insertBefore));l&&0<l.length&&(u=l[0])}if(u.parentNode.insertBefore(r,u),\"css\"==e){var c=w.navigator.userAgent.toLowerCase();if(-1<c.indexOf(\"phantomjs/1.9\"))$=!0;else if(/iP(hone|od|ad)/.test(w.navigator.platform)){var d=w.navigator.appVersion.match(/OS (\\d+)_(\\d+)_?(\\d+)?/),p=parseFloat([parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3]||0,10)].join(\".\"));$=p<6}else if(-1<c.indexOf(\"android\")){var h=parseFloat(c.slice(c.indexOf(\"android\")+8));$=h<4.4}else if(-1<c.indexOf(\"safari\")){var f=c.match(/version\\/([\\.\\d]+)/i);$=f&&f[1]&&parseFloat(f[1])<6}if($)var g=1e3,m=b(function(){try{r.sheet.cssRules,b.cancel(m),r.onload()}catch(e){--g<=0&&r.onerror()}},20)}return o.promise},v}])}])}(angular),function(m){\"use strict\";m.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(f,g){return f.filesLoader=function(e){var r=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],i=[],o=[],a=[],s=[],u=null,l=f._getFilesCache();f.toggleWatch(!0),m.extend(r,e);var t=function(e){var t,n=null;if(m.isObject(e)&&(n=e.type,e=e.path),u=l.get(e),m.isUndefined(u)||!1===r.cache){if(null!==(t=/^(css|less|html|htm|js)?(?=!)/.exec(e))&&(n=t[1],e=e.substr(t[1].length+1,e.length)),!n)if(null!==(t=/[.](css|less|html|htm|js)?((\\?|#).*)?$/.exec(e)))n=t[1];else{if(f.jsLoader.hasOwnProperty(\"ocLazyLoadLoader\")||!f.jsLoader.hasOwnProperty(\"requirejs\"))return void f._$log.error(\"File type could not be determined. \"+e);n=\"js\"}\"css\"!==n&&\"less\"!==n||-1!==i.indexOf(e)?\"html\"!==n&&\"htm\"!==n||-1!==o.indexOf(e)?\"js\"===n||-1===a.indexOf(e)?a.push(e):f._$log.error(\"File type is not valid. \"+e):o.push(e):i.push(e)}else u&&s.push(u)};if(r.serie?t(r.files.shift()):m.forEach(r.files,function(e){t(e)}),0<i.length){var n=g.defer();f.cssLoader(i,function(e){m.isDefined(e)&&f.cssLoader.hasOwnProperty(\"ocLazyLoadLoader\")?(f._$log.error(e),n.reject(e)):n.resolve()},r),s.push(n.promise)}if(0<o.length){var c=g.defer();f.templatesLoader(o,function(e){m.isDefined(e)&&f.templatesLoader.hasOwnProperty(\"ocLazyLoadLoader\")?(f._$log.error(e),c.reject(e)):c.resolve()},r),s.push(c.promise)}if(0<a.length){var d=g.defer();f.jsLoader(a,function(e){m.isDefined(e)&&(f.jsLoader.hasOwnProperty(\"ocLazyLoadLoader\")||f.jsLoader.hasOwnProperty(\"requirejs\"))?(f._$log.error(e),d.reject(e)):d.resolve()},r),s.push(d.promise)}if(0!==s.length)return r.serie&&0<r.files.length?g.all(s).then(function(){return f.filesLoader(e,r)}):g.all(s).finally(function(e){return f.toggleWatch(!1),e});var p=g.defer(),h=\"Error: no file to load has been found, if you're trying to load an existing module you should use the 'inject' method instead of 'load'.\";return f._$log.error(h),p.reject(h),p.promise},f.load=function(e){var t,n=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],r=this,i=null,o=[],a=g.defer(),s=m.copy(e),u=m.copy(n);if(m.isArray(s))return m.forEach(s,function(e){o.push(r.load(e,u))}),g.all(o).then(function(e){a.resolve(e)},function(e){a.reject(e)}),a.promise;if(m.isString(s)?(i=r.getModuleConfig(s))||(i={files:[s]}):m.isObject(s)&&(i=m.isDefined(s.path)&&m.isDefined(s.type)?{files:[s]}:r.setModuleConfig(s)),null===i)return t='Module \"'+(r._getModuleName(s)||\"unknown\")+'\" is not configured, cannot load.',f._$log.error(t),a.reject(new Error(t)),a.promise;m.isDefined(i.template)&&(m.isUndefined(i.files)&&(i.files=[]),m.isString(i.template)?i.files.push(i.template):m.isArray(i.template)&&i.files.concat(i.template));var l=m.extend({},u,i);return m.isUndefined(i.files)&&m.isDefined(i.name)&&f.moduleExists(i.name)?f.inject(i.name,l,!0):(f.filesLoader(i,l).then(function(){f.inject(null,l).then(function(e){a.resolve(e)},function(e){a.reject(e)})},function(e){a.reject(e)}),a.promise)},f}])}])}(angular),function(a){\"use strict\";a.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(i,o){return i.cssLoader=function(e,t,n){var r=[];a.forEach(e,function(e){r.push(i.buildElement(\"css\",e,n))}),o.all(r).then(function(){t()},function(e){t(e)})},i.cssLoader.ocLazyLoadLoader=!0,i}])}])}(angular),function(a){\"use strict\";a.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(i,o){return i.jsLoader=function(e,t,n){var r=[];a.forEach(e,function(e){r.push(i.buildElement(\"js\",e,n))}),o.all(r).then(function(){t()},function(e){t(e)})},i.jsLoader.ocLazyLoadLoader=!0,i}])}])}(angular),function(c){\"use strict\";c.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$templateCache\",\"$q\",\"$http\",function(n,s,u,l){return n.templatesLoader=function(e,t,i){var o=[],a=n._getFilesCache();return c.forEach(e,function(n){var r=u.defer();o.push(r.promise),l.get(n,i).then(function(e){var t=e.data;c.isString(t)&&0<t.length&&c.forEach(c.element(t),function(e){\"SCRIPT\"===e.nodeName&&\"text/ng-template\"===e.type&&s.put(e.id,e.innerHTML)}),c.isUndefined(a.get(n))&&a.put(n,!0),r.resolve()}).catch(function(e){r.reject(new Error('Unable to load template file \"'+n+'\": '+e.data))})}),u.all(o).then(function(){t()},function(e){t(e)})},n.templatesLoader.ocLazyLoadLoader=!0,n}])}])}(angular),Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){var n;if(null==this)throw new TypeError('\"this\" is null or not defined');var r=Object(this),i=r.length>>>0;if(0==i)return-1;var o=+t||0;if(Math.abs(o)===1/0&&(o=0),i<=o)return-1;for(n=Math.max(0<=o?o:i-Math.abs(o),0);n<i;){if(n in r&&r[n]===e)return n;n++}return-1}),function(){\"use strict\";angular.module(\"angular-loading-bar\",[\"cfp.loadingBarInterceptor\"]),angular.module(\"chieffancypants.loadingBar\",[\"cfp.loadingBarInterceptor\"]),angular.module(\"cfp.loadingBarInterceptor\",[\"cfp.loadingBar\"]).config([\"$httpProvider\",function(h){var e=[\"$q\",\"$cacheFactory\",\"$timeout\",\"$rootScope\",\"$log\",\"cfpLoadingBar\",function(t,o,n,r,i,a){function s(){n.cancel(l),a.complete(),c=d=0}function u(e){var t,n=o.get(\"$http\"),r=h.defaults;!e.cache&&!r.cache||!1===e.cache||\"GET\"!==e.method&&\"JSONP\"!==e.method||(t=angular.isObject(e.cache)?e.cache:angular.isObject(r.cache)?r.cache:n);var i=void 0!==t&&void 0!==t.get(e.url);return void 0!==e.cached&&i!==e.cached?e.cached:e.cached=i}var l,c=0,d=0,p=a.latencyThreshold;return{request:function(e){return e.ignoreLoadingBar||u(e)||(r.$broadcast(\"cfpLoadingBar:loading\",{url:e.url}),0===c&&(l=n(function(){a.start()},p)),c++,a.set(d/c)),e},response:function(e){return e&&e.config?e.config.ignoreLoadingBar||u(e.config)||(d++,r.$broadcast(\"cfpLoadingBar:loaded\",{url:e.config.url,result:e}),c<=d?s():a.set(d/c)):i.error(\"Broken interceptor detected: Config object not supplied in response:\\n https://github.com/chieffancypants/angular-loading-bar/pull/50\"),e},responseError:function(e){return e&&e.config?e.config.ignoreLoadingBar||u(e.config)||(d++,r.$broadcast(\"cfpLoadingBar:loaded\",{url:e.config.url,result:e}),c<=d?s():a.set(d/c)):i.error(\"Broken interceptor detected: Config object not supplied in rejection:\\n https://github.com/chieffancypants/angular-loading-bar/pull/50\"),t.reject(e)}}}];h.interceptors.push(e)}]),angular.module(\"cfp.loadingBar\",[]).provider(\"cfpLoadingBar\",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector=\"body\",this.spinnerTemplate='<div id=\"loading-bar-spinner\"><div class=\"spinner-icon\"></div></div>',this.loadingBarTemplate='<div id=\"loading-bar\"><div class=\"bar\"><div class=\"peg\"></div></div></div>',this.$get=[\"$injector\",\"$document\",\"$timeout\",\"$rootScope\",function(i,o,a,s){function u(e){if(m){var t=100*e+\"%\";f.css(\"width\",t),v=e,y&&(a.cancel(c),c=a(function(){n()},250))}}function n(){if(!(1<=r())){var e,t=r();e=0<=t&&t<.25?(3*Math.random()+3)/100:.25<=t&&t<.65?3*Math.random()/100:.65<=t&&t<.9?2*Math.random()/100:.9<=t&&t<.99?.005:0,u(r()+e)}}function r(){return v}function t(){v=0,m=!1}var l,c,d,p=this.parentSelector,h=angular.element(this.loadingBarTemplate),f=h.find(\"div\").eq(0),g=angular.element(this.spinnerTemplate),m=!1,v=0,y=this.autoIncrement,w=this.includeSpinner,b=this.includeBar,$=this.startSize;return{start:function(){if(l||(l=i.get(\"$animate\")),a.cancel(d),!m){var e=o[0],t=e.querySelector?e.querySelector(p):o.find(p)[0];t||(t=e.getElementsByTagName(\"body\")[0]);var n=angular.element(t),r=t.lastChild&&angular.element(t.lastChild);s.$broadcast(\"cfpLoadingBar:started\"),m=!0,b&&l.enter(h,n,r),w&&l.enter(g,n,h),u($)}},set:u,status:r,inc:n,complete:function(){l||(l=i.get(\"$animate\")),s.$broadcast(\"cfpLoadingBar:completed\"),u(1),a.cancel(d),d=a(function(){var e=l.leave(h,t);e&&e.then&&e.then(t),l.leave(g)},500)},autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(),angular.module(\"ui.bootstrap\",[\"ui.bootstrap.tpls\",\"ui.bootstrap.transition\",\"ui.bootstrap.collapse\",\"ui.bootstrap.accordion\",\"ui.bootstrap.alert\",\"ui.bootstrap.bindHtml\",\"ui.bootstrap.buttons\",\"ui.bootstrap.carousel\",\"ui.bootstrap.dateparser\",\"ui.bootstrap.position\",\"ui.bootstrap.datepicker\",\"ui.bootstrap.dropdown\",\"ui.bootstrap.modal\",\"ui.bootstrap.pagination\",\"ui.bootstrap.tooltip\",\"ui.bootstrap.popover\",\"ui.bootstrap.progressbar\",\"ui.bootstrap.rating\",\"ui.bootstrap.tabs\",\"ui.bootstrap.timepicker\",\"ui.bootstrap.typeahead\"]),angular.module(\"ui.bootstrap.tpls\",[\"template/accordion/accordion-group.html\",\"template/accordion/accordion.html\",\"template/alert/alert.html\",\"template/carousel/carousel.html\",\"template/carousel/slide.html\",\"template/datepicker/datepicker.html\",\"template/datepicker/day.html\",\"template/datepicker/month.html\",\"template/datepicker/popup.html\",\"template/datepicker/year.html\",\"template/modal/backdrop.html\",\"template/modal/window.html\",\"template/pagination/pager.html\",\"template/pagination/pagination.html\",\"template/tooltip/tooltip-html-unsafe-popup.html\",\"template/tooltip/tooltip-popup.html\",\"template/popover/popover.html\",\"template/progressbar/bar.html\",\"template/progressbar/progress.html\",\"template/progressbar/progressbar.html\",\"template/rating/rating.html\",\"template/tabs/tab.html\",\"template/tabs/tabset.html\",\"template/timepicker/timepicker.html\",\"template/typeahead/typeahead-match.html\",\"template/typeahead/typeahead-popup.html\"]),angular.module(\"ui.bootstrap.transition\",[]).factory(\"$transition\",[\"$q\",\"$timeout\",\"$rootScope\",function(a,s,u){function e(e){for(var t in e)if(void 0!==n.style[t])return e[t]}var l=function(e,t,n){n=n||{};var r=a.defer(),i=l[n.animation?\"animationEndEventName\":\"transitionEndEventName\"],o=function(){u.$apply(function(){e.unbind(i,o),r.resolve(e)})};return i&&e.bind(i,o),s(function(){angular.isString(t)?e.addClass(t):angular.isFunction(t)?t(e):angular.isObject(t)&&e.css(t),i||r.resolve(e)}),r.promise.cancel=function(){i&&e.unbind(i,o),r.reject(\"Transition cancelled\")},r.promise},n=document.createElement(\"trans\");return l.transitionEndEventName=e({WebkitTransition:\"webkitTransitionEnd\",MozTransition:\"transitionend\",OTransition:\"oTransitionEnd\",transition:\"transitionend\"}),l.animationEndEventName=e({WebkitTransition:\"webkitAnimationEnd\",MozTransition:\"animationend\",OTransition:\"oAnimationEnd\",transition:\"animationend\"}),l}]),angular.module(\"ui.bootstrap.collapse\",[\"ui.bootstrap.transition\"]).directive(\"collapse\",[\"$transition\",function(u){return{link:function(e,r,t){function n(e){function t(){a===n&&(a=void 0)}var n=u(r,e);return a&&a.cancel(),(a=n).then(t,t),n}function i(){r.removeClass(\"collapsing\"),r.addClass(\"collapse in\"),r.css({height:\"auto\"})}function o(){r.removeClass(\"collapsing\"),r.addClass(\"collapse\")}var a,s=!0;e.$watch(t.collapse,function(e){e?s?(s=!1,o(),r.css({height:0})):(r.css({height:r[0].scrollHeight+\"px\"}),r[0].offsetWidth,r.removeClass(\"collapse in\").addClass(\"collapsing\"),n({height:0}).then(o)):s?(s=!1,i()):(r.removeClass(\"collapse\").addClass(\"collapsing\"),n({height:r[0].scrollHeight+\"px\"}).then(i))})}}}]),angular.module(\"ui.bootstrap.accordion\",[\"ui.bootstrap.collapse\"]).constant(\"accordionConfig\",{closeOthers:!0}).controller(\"AccordionController\",[\"$scope\",\"$attrs\",\"accordionConfig\",function(e,n,r){this.groups=[],this.closeOthers=function(t){(angular.isDefined(n.closeOthers)?e.$eval(n.closeOthers):r.closeOthers)&&angular.forEach(this.groups,function(e){e!==t&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on(\"$destroy\",function(){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);-1!==t&&this.groups.splice(t,1)}}]).directive(\"accordion\",function(){return{restrict:\"EA\",controller:\"AccordionController\",transclude:!0,replace:!1,templateUrl:\"template/accordion/accordion.html\"}}).directive(\"accordionGroup\",function(){return{require:\"^accordion\",restrict:\"EA\",transclude:!0,replace:!0,templateUrl:\"template/accordion/accordion-group.html\",scope:{heading:\"@\",isOpen:\"=?\",isDisabled:\"=?\"},controller:function(){this.setHeading=function(e){this.heading=e}},link:function(t,e,n,r){r.addGroup(t),t.$watch(\"isOpen\",function(e){e&&r.closeOthers(t)}),t.toggleOpen=function(){t.isDisabled||(t.isOpen=!t.isOpen)}}}}).directive(\"accordionHeading\",function(){return{restrict:\"EA\",transclude:!0,template:\"\",replace:!0,require:\"^accordionGroup\",link:function(e,t,n,r,i){r.setHeading(i(e,function(){}))}}}).directive(\"accordionTransclude\",function(){return{require:\"^accordionGroup\",link:function(e,t,n,r){e.$watch(function(){return r[n.accordionTransclude]},function(e){e&&(t.html(\"\"),t.append(e))})}}}),angular.module(\"ui.bootstrap.alert\",[]).controller(\"AlertController\",[\"$scope\",\"$attrs\",function(e,t){e.closeable=\"close\"in t,this.close=e.close}]).directive(\"alert\",function(){return{restrict:\"EA\",controller:\"AlertController\",templateUrl:\"template/alert/alert.html\",transclude:!0,replace:!0,scope:{type:\"@\",close:\"&\"}}}).directive(\"dismissOnTimeout\",[\"$timeout\",function(i){return{require:\"alert\",link:function(e,t,n,r){i(function(){r.close()},parseInt(n.dismissOnTimeout,10))}}}]),angular.module(\"ui.bootstrap.bindHtml\",[]).directive(\"bindHtmlUnsafe\",function(){return function(e,t,n){t.addClass(\"ng-binding\").data(\"$binding\",n.bindHtmlUnsafe),e.$watch(n.bindHtmlUnsafe,function(e){t.html(e||\"\")})}}),angular.module(\"ui.bootstrap.buttons\",[]).constant(\"buttonConfig\",{activeClass:\"active\",toggleEvent:\"click\"}).controller(\"ButtonsController\",[\"buttonConfig\",function(e){this.activeClass=e.activeClass||\"active\",this.toggleEvent=e.toggleEvent||\"click\"}]).directive(\"btnRadio\",function(){return{require:[\"btnRadio\",\"ngModel\"],controller:\"ButtonsController\",link:function(t,n,r,e){var i=e[0],o=e[1];o.$render=function(){n.toggleClass(i.activeClass,angular.equals(o.$modelValue,t.$eval(r.btnRadio)))},n.bind(i.toggleEvent,function(){var e=n.hasClass(i.activeClass);(!e||angular.isDefined(r.uncheckable))&&t.$apply(function(){o.$setViewValue(e?null:t.$eval(r.btnRadio)),o.$render()})})}}}).directive(\"btnCheckbox\",function(){return{require:[\"btnCheckbox\",\"ngModel\"],controller:\"ButtonsController\",link:function(r,e,t,n){function i(){return o(t.btnCheckboxTrue,!0)}function o(e,t){var n=r.$eval(e);return angular.isDefined(n)?n:t}var a=n[0],s=n[1];s.$render=function(){e.toggleClass(a.activeClass,angular.equals(s.$modelValue,i()))},e.bind(a.toggleEvent,function(){r.$apply(function(){s.$setViewValue(e.hasClass(a.activeClass)?o(t.btnCheckboxFalse,!1):i()),s.$render()})})}}}),angular.module(\"ui.bootstrap.carousel\",[\"ui.bootstrap.transition\"]).controller(\"CarouselController\",[\"$scope\",\"$timeout\",\"$interval\",\"$transition\",function(a,t,n,s){function u(){r();var e=+a.interval;!isNaN(e)&&0<e&&(o=n(i,e))}function r(){o&&(n.cancel(o),o=null)}function i(){var e=+a.interval;l&&!isNaN(e)&&0<e?a.next():a.pause()}var o,l,c=this,d=c.slides=a.slides=[],p=-1;c.currentSlide=null;var h=!1;c.select=a.select=function(n,r){function e(){var e,t;h||(c.currentSlide&&angular.isString(r)&&!a.noTransition&&n.$element?(n.$element.addClass(r),n.$element[0].offsetWidth,angular.forEach(d,function(e){angular.extend(e,{direction:\"\",entering:!1,leaving:!1,active:!1})}),angular.extend(n,{direction:r,active:!0,entering:!0}),angular.extend(c.currentSlide||{},{direction:r,leaving:!0}),a.$currentTransition=s(n.$element,{}),e=n,t=c.currentSlide,a.$currentTransition.then(function(){i(e,t)},function(){i(e,t)})):i(n,c.currentSlide),c.currentSlide=n,p=o,u())}function i(e,t){angular.extend(e,{direction:\"\",active:!0,leaving:!1,entering:!1}),angular.extend(t||{},{direction:\"\",active:!1,leaving:!1,entering:!1}),a.$currentTransition=null}var o=d.indexOf(n);void 0===r&&(r=p<o?\"next\":\"prev\"),n&&n!==c.currentSlide&&(a.$currentTransition?(a.$currentTransition.cancel(),t(e)):e())},a.$on(\"$destroy\",function(){h=!0}),c.indexOfSlide=function(e){return d.indexOf(e)},a.next=function(){var e=(p+1)%d.length;return a.$currentTransition?void 0:c.select(d[e],\"next\")},a.prev=function(){var e=p-1<0?d.length-1:p-1;return a.$currentTransition?void 0:c.select(d[e],\"prev\")},a.isActive=function(e){return c.currentSlide===e},a.$watch(\"interval\",u),a.$on(\"$destroy\",r),a.play=function(){l||(l=!0,u())},a.pause=function(){a.noPause||(l=!1,r())},c.addSlide=function(e,t){e.$element=t,d.push(e),1===d.length||e.active?(c.select(d[d.length-1]),1==d.length&&a.play()):e.active=!1},c.removeSlide=function(e){var t=d.indexOf(e);d.splice(t,1),0<d.length&&e.active?c.select(t>=d.length?d[t-1]:d[t]):t<p&&p--}}]).directive(\"carousel\",[function(){return{restrict:\"EA\",transclude:!0,replace:!0,controller:\"CarouselController\",require:\"carousel\",templateUrl:\"template/carousel/carousel.html\",scope:{interval:\"=\",noTransition:\"=\",noPause:\"=\"}}}]).directive(\"slide\",function(){return{require:\"^carousel\",restrict:\"EA\",transclude:!0,replace:!0,templateUrl:\"template/carousel/slide.html\",scope:{active:\"=?\"},link:function(t,e,n,r){r.addSlide(t,e),t.$on(\"$destroy\",function(){r.removeSlide(t)}),t.$watch(\"active\",function(e){e&&r.select(t)})}}}),angular.module(\"ui.bootstrap.dateparser\",[]).service(\"dateParser\",[\"$locale\",\"orderByFilter\",function(d,p){this.parsers={};var h={yyyy:{regex:\"\\\\d{4}\",apply:function(e){this.year=+e}},yy:{regex:\"\\\\d{2}\",apply:function(e){this.year=+e+2e3}},y:{regex:\"\\\\d{1,4}\",apply:function(e){this.year=+e}},MMMM:{regex:d.DATETIME_FORMATS.MONTH.join(\"|\"),apply:function(e){this.month=d.DATETIME_FORMATS.MONTH.indexOf(e)}},MMM:{regex:d.DATETIME_FORMATS.SHORTMONTH.join(\"|\"),apply:function(e){this.month=d.DATETIME_FORMATS.SHORTMONTH.indexOf(e)}},MM:{regex:\"0[1-9]|1[0-2]\",apply:function(e){this.month=e-1}},M:{regex:\"[1-9]|1[0-2]\",apply:function(e){this.month=e-1}},dd:{regex:\"[0-2][0-9]{1}|3[0-1]{1}\",apply:function(e){this.date=+e}},d:{regex:\"[1-2]?[0-9]{1}|3[0-1]{1}\",apply:function(e){this.date=+e}},EEEE:{regex:d.DATETIME_FORMATS.DAY.join(\"|\")},EEE:{regex:d.DATETIME_FORMATS.SHORTDAY.join(\"|\")}};this.parse=function(e,t){if(!angular.isString(e)||!t)return e;t=d.DATETIME_FORMATS[t]||t,this.parsers[t]||(this.parsers[t]=function(o){var a=[],s=o.split(\"\");return angular.forEach(h,function(e,t){var n=o.indexOf(t);if(-1<n){o=o.split(\"\"),s[n]=\"(\"+e.regex+\")\",o[n]=\"$\";for(var r=n+1,i=n+t.length;r<i;r++)s[r]=\"\",o[r]=\"$\";o=o.join(\"\"),a.push({index:n,apply:e.apply})}}),{regex:new RegExp(\"^\"+s.join(\"\")+\"$\"),map:p(a,\"index\")}}(t));var n=this.parsers[t],r=n.regex,i=n.map,o=e.match(r);if(o&&o.length){for(var a,s={year:1900,month:0,date:1,hours:0},u=1,l=o.length;u<l;u++){var c=i[u-1];c.apply&&c.apply.call(s,o[u])}return function(e,t,n){return 1===t&&28<n?29===n&&(e%4==0&&e%100!=0||e%400==0):3!==t&&5!==t&&8!==t&&10!==t||n<31}(s.year,s.month,s.date)&&(a=new Date(s.year,s.month,s.date,s.hours)),a}}}]),angular.module(\"ui.bootstrap.position\",[]).factory(\"$position\",[\"$document\",\"$window\",function(o,n){function a(e){return\"static\"===(function(e,t){return e.currentStyle?e.currentStyle[t]:n.getComputedStyle?n.getComputedStyle(e)[t]:e.style[t]}(e,\"position\")||\"static\")}return{position:function(e){var t=this.offset(e),n={top:0,left:0},r=function(e){for(var t=o[0],n=e.offsetParent||t;n&&n!==t&&a(n);)n=n.offsetParent;return n||t}(e[0]);r!=o[0]&&((n=this.offset(angular.element(r))).top+=r.clientTop-r.scrollTop,n.left+=r.clientLeft-r.scrollLeft);var i=e[0].getBoundingClientRect();return{width:i.width||e.prop(\"offsetWidth\"),height:i.height||e.prop(\"offsetHeight\"),top:t.top-n.top,left:t.left-n.left}},offset:function(e){var t=e[0].getBoundingClientRect();return{width:t.width||e.prop(\"offsetWidth\"),height:t.height||e.prop(\"offsetHeight\"),top:t.top+(n.pageYOffset||o[0].documentElement.scrollTop),left:t.left+(n.pageXOffset||o[0].documentElement.scrollLeft)}},positionElements:function(e,t,n,r){var i,o,a,s,u=n.split(\"-\"),l=u[0],c=u[1]||\"center\";i=r?this.offset(e):this.position(e),o=t.prop(\"offsetWidth\"),a=t.prop(\"offsetHeight\");var d={center:function(){return i.left+i.width/2-o/2},left:function(){return i.left},right:function(){return i.left+i.width}},p={center:function(){return i.top+i.height/2-a/2},top:function(){return i.top},bottom:function(){return i.top+i.height}};switch(l){case\"right\":s={top:p[c](),left:d[l]()};break;case\"left\":s={top:p[c](),left:i.left-o};break;case\"bottom\":s={top:p[l](),left:d[c]()};break;default:s={top:i.top-a,left:d[c]()}}return s}}}]),angular.module(\"ui.bootstrap.datepicker\",[\"ui.bootstrap.dateparser\",\"ui.bootstrap.position\"]).constant(\"datepickerConfig\",{formatDay:\"dd\",formatMonth:\"MMMM\",formatYear:\"yyyy\",formatDayHeader:\"EEE\",formatDayTitle:\"MMMM yyyy\",formatMonthTitle:\"yyyy\",datepickerMode:\"day\",minMode:\"day\",maxMode:\"year\",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null}).controller(\"DatepickerController\",[\"$scope\",\"$attrs\",\"$parse\",\"$interpolate\",\"$timeout\",\"$log\",\"dateFilter\",\"datepickerConfig\",function(n,r,e,i,t,o,a,s){var u=this,l={$setViewValue:angular.noop};this.modes=[\"day\",\"month\",\"year\"],angular.forEach([\"formatDay\",\"formatMonth\",\"formatYear\",\"formatDayHeader\",\"formatDayTitle\",\"formatMonthTitle\",\"minMode\",\"maxMode\",\"showWeeks\",\"startingDay\",\"yearRange\"],function(e,t){u[e]=angular.isDefined(r[e])?t<8?i(r[e])(n.$parent):n.$parent.$eval(r[e]):s[e]}),angular.forEach([\"minDate\",\"maxDate\"],function(t){r[t]?n.$parent.$watch(e(r[t]),function(e){u[t]=e?new Date(e):null,u.refreshView()}):u[t]=s[t]?new Date(s[t]):null}),n.datepickerMode=n.datepickerMode||s.datepickerMode,n.uniqueId=\"datepicker-\"+n.$id+\"-\"+Math.floor(1e4*Math.random()),this.activeDate=angular.isDefined(r.initDate)?n.$parent.$eval(r.initDate):new Date,n.isActive=function(e){return 0===u.compare(e.date,u.activeDate)&&(n.activeDateId=e.uid,!0)},this.init=function(e){(l=e).$render=function(){u.render()}},this.render=function(){if(l.$modelValue){var e=new Date(l.$modelValue),t=!isNaN(e);t?this.activeDate=e:o.error('Datepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'),l.$setValidity(\"date\",t)}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var e=l.$modelValue?new Date(l.$modelValue):null;l.$setValidity(\"date-disabled\",!e||this.element&&!this.isDisabled(e))}},this.createDateObject=function(e,t){var n=l.$modelValue?new Date(l.$modelValue):null;return{date:e,label:a(e,t),selected:n&&0===this.compare(e,n),disabled:this.isDisabled(e),current:0===this.compare(e,new Date)}},this.isDisabled=function(e){return this.minDate&&this.compare(e,this.minDate)<0||this.maxDate&&0<this.compare(e,this.maxDate)||r.dateDisabled&&n.dateDisabled({date:e,mode:n.datepickerMode})},this.split=function(e,t){for(var n=[];0<e.length;)n.push(e.splice(0,t));return n},n.select=function(e){if(n.datepickerMode===u.minMode){var t=l.$modelValue?new Date(l.$modelValue):new Date(0,0,0,0,0,0,0);t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),l.$setViewValue(t),l.$render()}else u.activeDate=e,n.datepickerMode=u.modes[u.modes.indexOf(n.datepickerMode)-1]},n.move=function(e){var t=u.activeDate.getFullYear()+e*(u.step.years||0),n=u.activeDate.getMonth()+e*(u.step.months||0);u.activeDate.setFullYear(t,n,1),u.refreshView()},n.toggleMode=function(e){e=e||1,n.datepickerMode===u.maxMode&&1===e||n.datepickerMode===u.minMode&&-1===e||(n.datepickerMode=u.modes[u.modes.indexOf(n.datepickerMode)+e])},n.keys={13:\"enter\",32:\"space\",33:\"pageup\",34:\"pagedown\",35:\"end\",36:\"home\",37:\"left\",38:\"up\",39:\"right\",40:\"down\"};var c=function(){t(function(){u.element[0].focus()},0,!1)};n.$on(\"datepicker.focus\",c),n.keydown=function(e){var t=n.keys[e.which];if(t&&!e.shiftKey&&!e.altKey)if(e.preventDefault(),e.stopPropagation(),\"enter\"===t||\"space\"===t){if(u.isDisabled(u.activeDate))return;n.select(u.activeDate),c()}else!e.ctrlKey||\"up\"!==t&&\"down\"!==t?(u.handleKeyDown(t,e),u.refreshView()):(n.toggleMode(\"up\"===t?1:-1),c())}}]).directive(\"datepicker\",function(){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/datepicker.html\",scope:{datepickerMode:\"=?\",dateDisabled:\"&\"},require:[\"datepicker\",\"?^ngModel\"],controller:\"DatepickerController\",link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o)}}}).directive(\"daypicker\",[\"dateFilter\",function(h){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/day.html\",require:\"^datepicker\",link:function(d,e,t,p){function r(e,t){return 1!==t||e%4!=0||e%100==0&&e%400!=0?n[t]:29}d.showWeeks=p.showWeeks,p.step={months:1},p.element=e;var n=[31,28,31,30,31,30,31,31,30,31,30,31];p._refreshView=function(){var e=p.activeDate.getFullYear(),t=p.activeDate.getMonth(),n=new Date(e,t,1),r=p.startingDay-n.getDay(),i=0<r?7-r:-r,o=new Date(n);0<i&&o.setDate(1-i);for(var a=function(e,t){var n=new Array(t),r=new Date(e),i=0;for(r.setHours(12);i<t;)n[i++]=new Date(r),r.setDate(r.getDate()+1);return n}(o,42),s=0;s<42;s++)a[s]=angular.extend(p.createDateObject(a[s],p.formatDay),{secondary:a[s].getMonth()!==t,uid:d.uniqueId+\"-\"+s});d.labels=new Array(7);for(var u=0;u<7;u++)d.labels[u]={abbr:h(a[u].date,p.formatDayHeader),full:h(a[u].date,\"EEEE\")};if(d.title=h(p.activeDate,p.formatDayTitle),d.rows=p.split(a,7),d.showWeeks){d.weekNumbers=[];for(var l=function(e){var t=new Date(e);t.setDate(t.getDate()+4-(t.getDay()||7));var n=t.getTime();return t.setMonth(0),t.setDate(1),Math.floor(Math.round((n-t)/864e5)/7)+1}(d.rows[0][0].date),c=d.rows.length;d.weekNumbers.push(l++)<c;);}},p.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth(),e.getDate())-new Date(t.getFullYear(),t.getMonth(),t.getDate())},p.handleKeyDown=function(e){var t=p.activeDate.getDate();if(\"left\"===e)t-=1;else if(\"up\"===e)t-=7;else if(\"right\"===e)t+=1;else if(\"down\"===e)t+=7;else if(\"pageup\"===e||\"pagedown\"===e){var n=p.activeDate.getMonth()+(\"pageup\"===e?-1:1);p.activeDate.setMonth(n,1),t=Math.min(r(p.activeDate.getFullYear(),p.activeDate.getMonth()),t)}else\"home\"===e?t=1:\"end\"===e&&(t=r(p.activeDate.getFullYear(),p.activeDate.getMonth()));p.activeDate.setDate(t)},p.refreshView()}}}]).directive(\"monthpicker\",[\"dateFilter\",function(o){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/month.html\",require:\"^datepicker\",link:function(r,e,t,i){i.step={years:1},i.element=e,i._refreshView=function(){for(var e=new Array(12),t=i.activeDate.getFullYear(),n=0;n<12;n++)e[n]=angular.extend(i.createDateObject(new Date(t,n,1),i.formatMonth),{uid:r.uniqueId+\"-\"+n});r.title=o(i.activeDate,i.formatMonthTitle),r.rows=i.split(e,3)},i.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth())-new Date(t.getFullYear(),t.getMonth())},i.handleKeyDown=function(e){var t=i.activeDate.getMonth();if(\"left\"===e)t-=1;else if(\"up\"===e)t-=3;else if(\"right\"===e)t+=1;else if(\"down\"===e)t+=3;else if(\"pageup\"===e||\"pagedown\"===e){var n=i.activeDate.getFullYear()+(\"pageup\"===e?-1:1);i.activeDate.setFullYear(n)}else\"home\"===e?t=0:\"end\"===e&&(t=11);i.activeDate.setMonth(t)},i.refreshView()}}}]).directive(\"yearpicker\",[\"dateFilter\",function(){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/year.html\",require:\"^datepicker\",link:function(r,e,t,i){function o(e){return parseInt((e-1)/a,10)*a+1}var a=i.yearRange;i.step={years:a},i.element=e,i._refreshView=function(){for(var e=new Array(a),t=0,n=o(i.activeDate.getFullYear());t<a;t++)e[t]=angular.extend(i.createDateObject(new Date(n+t,0,1),i.formatYear),{uid:r.uniqueId+\"-\"+t});r.title=[e[0].label,e[a-1].label].join(\" - \"),r.rows=i.split(e,5)},i.compare=function(e,t){return e.getFullYear()-t.getFullYear()},i.handleKeyDown=function(e){var t=i.activeDate.getFullYear();\"left\"===e?t-=1:\"up\"===e?t-=5:\"right\"===e?t+=1:\"down\"===e?t+=5:\"pageup\"===e||\"pagedown\"===e?t+=(\"pageup\"===e?-1:1)*i.step.years:\"home\"===e?t=o(i.activeDate.getFullYear()):\"end\"===e&&(t=o(i.activeDate.getFullYear())+a-1),i.activeDate.setFullYear(t)},i.refreshView()}}}]).constant(\"datepickerPopupConfig\",{datepickerPopup:\"yyyy-MM-dd\",currentText:\"Today\",clearText:\"Clear\",closeText:\"Done\",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0}).directive(\"datepickerPopup\",[\"$compile\",\"$parse\",\"$document\",\"$position\",\"dateFilter\",\"dateParser\",\"datepickerPopupConfig\",function(f,g,m,v,y,w,b){return{restrict:\"EA\",require:\"ngModel\",scope:{isOpen:\"=?\",currentText:\"@\",clearText:\"@\",closeText:\"@\",dateDisabled:\"&\"},link:function(r,t,i,n){function o(e){return e.replace(/([A-Z])/g,function(e){return\"-\"+e.toLowerCase()})}function a(e){if(!e)return n.$setValidity(\"date\",!0),null;if(angular.isDate(e)&&!isNaN(e))return n.$setValidity(\"date\",!0),e;if(angular.isString(e)){var t=w.parse(e,s)||new Date(e);return isNaN(t)?void n.$setValidity(\"date\",!1):(n.$setValidity(\"date\",!0),t)}n.$setValidity(\"date\",!1)}var s,u=angular.isDefined(i.closeOnDateSelection)?r.$parent.$eval(i.closeOnDateSelection):b.closeOnDateSelection,l=angular.isDefined(i.datepickerAppendToBody)?r.$parent.$eval(i.datepickerAppendToBody):b.appendToBody;r.showButtonBar=angular.isDefined(i.showButtonBar)?r.$parent.$eval(i.showButtonBar):b.showButtonBar,r.getText=function(e){return r[e+\"Text\"]||b[e+\"Text\"]},i.$observe(\"datepickerPopup\",function(e){s=e||b.datepickerPopup,n.$render()});var e=angular.element(\"<div datepicker-popup-wrap><div datepicker></div></div>\");e.attr({\"ng-model\":\"date\",\"ng-change\":\"dateSelection()\"});var c=angular.element(e.children()[0]);i.datepickerOptions&&angular.forEach(r.$parent.$eval(i.datepickerOptions),function(e,t){c.attr(o(t),e)}),r.watchData={},angular.forEach([\"minDate\",\"maxDate\",\"datepickerMode\"],function(t){if(i[t]){var e=g(i[t]);if(r.$parent.$watch(e,function(e){r.watchData[t]=e}),c.attr(o(t),\"watchData.\"+t),\"datepickerMode\"===t){var n=e.assign;r.$watch(\"watchData.\"+t,function(e,t){e!==t&&n(r.$parent,e)})}}}),i.dateDisabled&&c.attr(\"date-disabled\",\"dateDisabled({ date: date, mode: mode })\"),n.$parsers.unshift(a),r.dateSelection=function(e){angular.isDefined(e)&&(r.date=e),n.$setViewValue(r.date),n.$render(),u&&(r.isOpen=!1,t[0].focus())},t.bind(\"input change keyup\",function(){r.$apply(function(){r.date=n.$modelValue})}),n.$render=function(){var e=n.$viewValue?y(n.$viewValue,s):\"\";t.val(e),r.date=a(n.$modelValue)};var d=function(e){r.isOpen&&e.target!==t[0]&&r.$apply(function(){r.isOpen=!1})},p=function(e){r.keydown(e)};t.bind(\"keydown\",p),r.keydown=function(e){27===e.which?(e.preventDefault(),e.stopPropagation(),r.close()):40!==e.which||r.isOpen||(r.isOpen=!0)},r.$watch(\"isOpen\",function(e){e?(r.$broadcast(\"datepicker.focus\"),r.position=l?v.offset(t):v.position(t),r.position.top=r.position.top+t.prop(\"offsetHeight\"),m.bind(\"click\",d)):m.unbind(\"click\",d)}),r.select=function(e){if(\"today\"===e){var t=new Date;angular.isDate(n.$modelValue)?(e=new Date(n.$modelValue)).setFullYear(t.getFullYear(),t.getMonth(),t.getDate()):e=new Date(t.setHours(0,0,0,0))}r.dateSelection(e)},r.close=function(){r.isOpen=!1,t[0].focus()};var h=f(e)(r);e.remove(),l?m.find(\"body\").append(h):t.after(h),r.$on(\"$destroy\",function(){h.remove(),t.unbind(\"keydown\",p),m.unbind(\"click\",d)})}}}]).directive(\"datepickerPopupWrap\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,templateUrl:\"template/datepicker/popup.html\",link:function(e,t){t.bind(\"click\",function(e){e.preventDefault(),e.stopPropagation()})}}}),angular.module(\"ui.bootstrap.dropdown\",[]).constant(\"dropdownConfig\",{openClass:\"open\"}).service(\"dropdownService\",[\"$document\",function(t){var n=null;this.open=function(e){n||(t.bind(\"click\",r),t.bind(\"keydown\",i)),n&&n!==e&&(n.isOpen=!1),n=e},this.close=function(e){n===e&&(n=null,t.unbind(\"click\",r),t.unbind(\"keydown\",i))};var r=function(e){if(n){var t=n.getToggleElement();e&&t&&t[0].contains(e.target)||n.$apply(function(){n.isOpen=!1})}},i=function(e){27===e.which&&(n.focusToggleElement(),r())}}]).controller(\"DropdownController\",[\"$scope\",\"$attrs\",\"$parse\",\"dropdownConfig\",\"dropdownService\",\"$animate\",function(n,t,r,e,i,o){var a,s=this,u=n.$new(),l=e.openClass,c=angular.noop,d=t.onToggle?r(t.onToggle):angular.noop;this.init=function(e){s.$element=e,t.isOpen&&(a=r(t.isOpen),c=a.assign,n.$watch(a,function(e){u.isOpen=!!e}))},this.toggle=function(e){return u.isOpen=arguments.length?!!e:!u.isOpen},this.isOpen=function(){return u.isOpen},u.getToggleElement=function(){return s.toggleElement},u.focusToggleElement=function(){s.toggleElement&&s.toggleElement[0].focus()},u.$watch(\"isOpen\",function(e,t){o[e?\"addClass\":\"removeClass\"](s.$element,l),e?(u.focusToggleElement(),i.open(u)):i.close(u),c(n,e),angular.isDefined(e)&&e!==t&&d(n,{open:!!e})}),n.$on(\"$locationChangeSuccess\",function(){u.isOpen=!1}),n.$on(\"$destroy\",function(){u.$destroy()})}]).directive(\"dropdown\",function(){return{controller:\"DropdownController\",link:function(e,t,n,r){r.init(t)}}}).directive(\"dropdownToggle\",function(){return{require:\"?^dropdown\",link:function(t,n,r,i){if(i){i.toggleElement=n;var e=function(e){e.preventDefault(),n.hasClass(\"disabled\")||r.disabled||t.$apply(function(){i.toggle()})};n.bind(\"click\",e),n.attr({\"aria-haspopup\":!0,\"aria-expanded\":!1}),t.$watch(i.isOpen,function(e){n.attr(\"aria-expanded\",!!e)}),t.$on(\"$destroy\",function(){n.unbind(\"click\",e)})}}}}),angular.module(\"ui.bootstrap.modal\",[\"ui.bootstrap.transition\"]).factory(\"$$stackedMap\",function(){return{createNew:function(){var r=[];return{add:function(e,t){r.push({key:e,value:t})},get:function(e){for(var t=0;t<r.length;t++)if(e==r[t].key)return r[t]},keys:function(){for(var e=[],t=0;t<r.length;t++)e.push(r[t].key);return e},top:function(){return r[r.length-1]},remove:function(e){for(var t=-1,n=0;n<r.length;n++)if(e==r[n].key){t=n;break}return r.splice(t,1)[0]},removeTop:function(){return r.splice(r.length-1,1)[0]},length:function(){return r.length}}}}}).directive(\"modalBackdrop\",[\"$timeout\",function(r){return{restrict:\"EA\",replace:!0,templateUrl:\"template/modal/backdrop.html\",link:function(e,t,n){e.backdropClass=n.backdropClass||\"\",e.animate=!1,r(function(){e.animate=!0})}}}]).directive(\"modalWindow\",[\"$modalStack\",\"$timeout\",function(r,i){return{restrict:\"EA\",scope:{index:\"@\",animate:\"=\"},replace:!0,transclude:!0,templateUrl:function(e,t){return t.templateUrl||\"template/modal/window.html\"},link:function(e,t,n){t.addClass(n.windowClass||\"\"),e.size=n.size,i(function(){e.animate=!0,t[0].querySelectorAll(\"[autofocus]\").length||t[0].focus()}),e.close=function(e){var t=r.getTop();t&&t.value.backdrop&&\"static\"!=t.value.backdrop&&e.target===e.currentTarget&&(e.preventDefault(),e.stopPropagation(),r.dismiss(t.key,\"backdrop click\"))}}}}]).directive(\"modalTransclude\",function(){return{link:function(e,t,n,r,i){i(e.$parent,function(e){t.empty(),t.append(e)})}}}).factory(\"$modalStack\",[\"$transition\",\"$timeout\",\"$document\",\"$compile\",\"$rootScope\",\"$$stackedMap\",function(s,u,l,c,d,e){function p(){for(var e=-1,t=m.keys(),n=0;n<t.length;n++)m.get(t[n]).value.backdrop&&(e=n);return e}function r(e){var t=l.find(\"body\").eq(0),n=m.get(e).value;m.remove(e),i(n.modalDomEl,n.modalScope,300,function(){n.modalScope.$destroy(),t.toggleClass(g,0<m.length()),function(){if(h&&-1==p()){var e=f;i(h,f,150,function(){e.$destroy(),e=null}),f=h=void 0}}()})}function i(e,t,n,r){function i(){i.done||(i.done=!0,e.remove(),r&&r())}t.animate=!1;var o=s.transitionEndEventName;if(o){var a=u(i,n);e.bind(o,function(){u.cancel(a),i(),t.$apply()})}else u(i)}var h,f,g=\"modal-open\",m=e.createNew(),n={};return d.$watch(p,function(e){f&&(f.index=e)}),l.bind(\"keydown\",function(e){var t;27===e.which&&((t=m.top())&&t.value.keyboard&&(e.preventDefault(),d.$apply(function(){n.dismiss(t.key,\"escape key press\")})))}),n.open=function(e,t){m.add(e,{deferred:t.deferred,modalScope:t.scope,backdrop:t.backdrop,keyboard:t.keyboard});var n=l.find(\"body\").eq(0),r=p();if(0<=r&&!h){(f=d.$new(!0)).index=r;var i=angular.element(\"<div modal-backdrop></div>\");i.attr(\"backdrop-class\",t.backdropClass),h=c(i)(f),n.append(h)}var o=angular.element(\"<div modal-window></div>\");o.attr({\"template-url\":t.windowTemplateUrl,\"window-class\":t.windowClass,size:t.size,index:m.length()-1,animate:\"animate\"}).html(t.content);var a=c(o)(t.scope);m.top().value.modalDomEl=a,n.append(a),n.addClass(g)},n.close=function(e,t){var n=m.get(e);n&&(n.value.deferred.resolve(t),r(e))},n.dismiss=function(e,t){var n=m.get(e);n&&(n.value.deferred.reject(t),r(e))},n.dismissAll=function(e){for(var t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},n.getTop=function(){return m.top()},n}]).provider(\"$modal\",function(){var p={options:{backdrop:!0,keyboard:!0},$get:[\"$injector\",\"$rootScope\",\"$q\",\"$http\",\"$templateCache\",\"$controller\",\"$modalStack\",function(n,u,r,i,l,c,d){var e={};return e.open=function(o){var a=r.defer(),e=r.defer(),s={result:a.promise,opened:e.promise,close:function(e){d.close(s,e)},dismiss:function(e){d.dismiss(s,e)}};if((o=angular.extend({},p.options,o)).resolve=o.resolve||{},!o.template&&!o.templateUrl)throw new Error(\"One of template or templateUrl options is required.\");var t=r.all([function(e){return e.template?r.when(e.template):i.get(angular.isFunction(e.templateUrl)?e.templateUrl():e.templateUrl,{cache:l}).then(function(e){return e.data})}(o)].concat(function(e){var t=[];return angular.forEach(e,function(e){(angular.isFunction(e)||angular.isArray(e))&&t.push(r.when(n.invoke(e)))}),t}(o.resolve)));return t.then(function(n){var e=(o.scope||u).$new();e.$close=s.close,e.$dismiss=s.dismiss;var t,r={},i=1;o.controller&&(r.$scope=e,r.$modalInstance=s,angular.forEach(o.resolve,function(e,t){r[t]=n[i++]}),t=c(o.controller,r),o.controllerAs&&(e[o.controllerAs]=t)),d.open(s,{scope:e,deferred:a,content:n[0],backdrop:o.backdrop,keyboard:o.keyboard,backdropClass:o.backdropClass,windowClass:o.windowClass,windowTemplateUrl:o.windowTemplateUrl,size:o.size})},function(e){a.reject(e)}),t.then(function(){e.resolve(!0)},function(){e.reject(!1)}),s},e}]};return p}),angular.module(\"ui.bootstrap.pagination\",[]).controller(\"PaginationController\",[\"$scope\",\"$attrs\",\"$parse\",function(n,r,i){var o=this,a={$setViewValue:angular.noop},t=r.numPages?i(r.numPages).assign:angular.noop;this.init=function(e,t){a=e,this.config=t,a.$render=function(){o.render()},r.itemsPerPage?n.$parent.$watch(i(r.itemsPerPage),function(e){o.itemsPerPage=parseInt(e,10),n.totalPages=o.calculateTotalPages()}):this.itemsPerPage=t.itemsPerPage},this.calculateTotalPages=function(){var e=this.itemsPerPage<1?1:Math.ceil(n.totalItems/this.itemsPerPage);return Math.max(e||0,1)},this.render=function(){n.page=parseInt(a.$viewValue,10)||1},n.selectPage=function(e){n.page!==e&&0<e&&e<=n.totalPages&&(a.$setViewValue(e),a.$render())},n.getText=function(e){return n[e+\"Text\"]||o.config[e+\"Text\"]},n.noPrevious=function(){return 1===n.page},n.noNext=function(){return n.page===n.totalPages},n.$watch(\"totalItems\",function(){n.totalPages=o.calculateTotalPages()}),n.$watch(\"totalPages\",function(e){t(n.$parent,e),n.page>e?n.selectPage(e):a.$render()})}]).constant(\"paginationConfig\",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:\"First\",previousText:\"Previous\",nextText:\"Next\",lastText:\"Last\",rotate:!0}).directive(\"pagination\",[\"$parse\",\"paginationConfig\",function(s,u){return{restrict:\"EA\",scope:{totalItems:\"=\",firstText:\"@\",previousText:\"@\",nextText:\"@\",lastText:\"@\"},require:[\"pagination\",\"?ngModel\"],controller:\"PaginationController\",templateUrl:\"template/pagination/pagination.html\",replace:!0,link:function(e,t,n,r){function c(e,t,n){return{number:e,text:t,active:n}}var i=r[0],o=r[1];if(o){var d=angular.isDefined(n.maxSize)?e.$parent.$eval(n.maxSize):u.maxSize,p=angular.isDefined(n.rotate)?e.$parent.$eval(n.rotate):u.rotate;e.boundaryLinks=angular.isDefined(n.boundaryLinks)?e.$parent.$eval(n.boundaryLinks):u.boundaryLinks,e.directionLinks=angular.isDefined(n.directionLinks)?e.$parent.$eval(n.directionLinks):u.directionLinks,i.init(o,u),n.maxSize&&e.$parent.$watch(s(n.maxSize),function(e){d=parseInt(e,10),i.render()});var a=i.render;i.render=function(){a(),0<e.page&&e.page<=e.totalPages&&(e.pages=function(e,t){var n=[],r=1,i=t,o=angular.isDefined(d)&&d<t;o&&(p?t<(i=(r=Math.max(e-Math.floor(d/2),1))+d-1)&&(r=(i=t)-d+1):(r=(Math.ceil(e/d)-1)*d+1,i=Math.min(r+d-1,t)));for(var a=r;a<=i;a++){var s=c(a,a,a===e);n.push(s)}if(o&&!p){if(1<r){var u=c(r-1,\"...\",!1);n.unshift(u)}if(i<t){var l=c(i+1,\"...\",!1);n.push(l)}}return n}(e.page,e.totalPages))}}}}}]).constant(\"pagerConfig\",{itemsPerPage:10,previousText:\"« Previous\",nextText:\"Next »\",align:!0}).directive(\"pager\",[\"pagerConfig\",function(a){return{restrict:\"EA\",scope:{totalItems:\"=\",previousText:\"@\",nextText:\"@\"},require:[\"pager\",\"?ngModel\"],controller:\"PaginationController\",templateUrl:\"template/pagination/pager.html\",replace:!0,link:function(e,t,n,r){var i=r[0],o=r[1];o&&(e.align=angular.isDefined(n.align)?e.$parent.$eval(n.align):a.align,i.init(o,a))}}}]),angular.module(\"ui.bootstrap.tooltip\",[\"ui.bootstrap.position\",\"ui.bootstrap.bindHtml\"]).provider(\"$tooltip\",function(){var s={placement:\"top\",animation:!0,popupDelay:0},u={mouseenter:\"mouseleave\",click:\"click\",focus:\"blur\"},l={};this.options=function(e){angular.extend(l,e)},this.setTriggers=function(e){angular.extend(u,e)},this.$get=[\"$window\",\"$compile\",\"$timeout\",\"$document\",\"$position\",\"$interpolate\",function(e,o,x,O,T,a){return function(S,C,n){function k(e){var t=e||D.trigger||n;return{show:t,hide:u[t]||t}}var D=angular.extend({},s,l),e=function(e){return e.replace(/[A-Z]/g,function(e,t){return(t?\"-\":\"\")+e.toLowerCase()})}(S),t=a.startSymbol(),r=a.endSymbol(),i=\"<div \"+e+'-popup title=\"'+t+\"title\"+r+'\" content=\"'+t+\"content\"+r+'\" placement=\"'+t+\"placement\"+r+'\" animation=\"animation\" is-open=\"isOpen\"></div>';return{restrict:\"EA\",compile:function(){var _=o(i);return function(e,t,n){function r(){m.isOpen?o():i()}function i(){(!g||e.$eval(n[C+\"Enable\"]))&&(function(){var e=n[C+\"Placement\"];m.placement=angular.isDefined(e)?e:D.placement}(),function(){var e=n[C+\"PopupDelay\"],t=parseInt(e,10);m.popupDelay=isNaN(t)?D.popupDelay:t}(),m.popupDelay?p||(p=x(a,m.popupDelay,!1)).then(function(e){e()}):a()())}function o(){e.$apply(function(){s()})}function a(){return p=null,d&&(x.cancel(d),d=null),m.content?(l&&u(),c=m.$new(),(l=_(c,function(e){h?O.find(\"body\").append(e):t.after(e)})).css({top:0,left:0,display:\"block\"}),m.$digest(),v(),m.isOpen=!0,m.$digest(),v):angular.noop}function s(){m.isOpen=!1,x.cancel(p),p=null,m.animation?d||(d=x(u,500)):u()}function u(){d=null,l&&(l.remove(),l=null),c&&(c.$destroy(),c=null)}var l,c,d,p,h=!!angular.isDefined(D.appendToBody)&&D.appendToBody,f=k(void 0),g=angular.isDefined(n[C+\"Enable\"]),m=e.$new(!0),v=function(){var e=T.positionElements(t,l,m.placement,h);e.top+=\"px\",e.left+=\"px\",l.css(e)};m.isOpen=!1,n.$observe(S,function(e){!(m.content=e)&&m.isOpen&&s()}),n.$observe(C+\"Title\",function(e){m.title=e});var y,w=function(){t.unbind(f.show,i),t.unbind(f.hide,o)};y=n[C+\"Trigger\"],w(),(f=k(y)).show===f.hide?t.bind(f.show,r):(t.bind(f.show,i),t.bind(f.hide,o));var b=e.$eval(n[C+\"Animation\"]);m.animation=angular.isDefined(b)?!!b:D.animation;var $=e.$eval(n[C+\"AppendToBody\"]);(h=angular.isDefined($)?$:h)&&e.$on(\"$locationChangeSuccess\",function(){m.isOpen&&s()}),e.$on(\"$destroy\",function(){x.cancel(d),x.cancel(p),w(),u(),m=null})}}}}}]}).directive(\"tooltipPopup\",function(){return{restrict:\"EA\",replace:!0,scope:{content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/tooltip/tooltip-popup.html\"}}).directive(\"tooltip\",[\"$tooltip\",function(e){return e(\"tooltip\",\"tooltip\",\"mouseenter\")}]).directive(\"tooltipHtmlUnsafePopup\",function(){return{restrict:\"EA\",replace:!0,scope:{content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/tooltip/tooltip-html-unsafe-popup.html\"}}).directive(\"tooltipHtmlUnsafe\",[\"$tooltip\",function(e){return e(\"tooltipHtmlUnsafe\",\"tooltip\",\"mouseenter\")}]),angular.module(\"ui.bootstrap.popover\",[\"ui.bootstrap.tooltip\"]).directive(\"popoverPopup\",function(){return{restrict:\"EA\",replace:!0,scope:{title:\"@\",content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/popover/popover.html\"}}).directive(\"popover\",[\"$tooltip\",function(e){return e(\"popover\",\"popover\",\"click\")}]),angular.module(\"ui.bootstrap.progressbar\",[]).constant(\"progressConfig\",{animate:!0,max:100}).controller(\"ProgressController\",[\"$scope\",\"$attrs\",\"progressConfig\",function(n,e,t){var r=this,i=angular.isDefined(e.animate)?n.$parent.$eval(e.animate):t.animate;this.bars=[],n.max=angular.isDefined(e.max)?n.$parent.$eval(e.max):t.max,this.addBar=function(t,e){i||e.css({transition:\"none\"}),this.bars.push(t),t.$watch(\"value\",function(e){t.percent=+(100*e/n.max).toFixed(2)}),t.$on(\"$destroy\",function(){e=null,r.removeBar(t)})},this.removeBar=function(e){this.bars.splice(this.bars.indexOf(e),1)}}]).directive(\"progress\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,controller:\"ProgressController\",require:\"progress\",scope:{},templateUrl:\"template/progressbar/progress.html\"}}).directive(\"bar\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,require:\"^progress\",scope:{value:\"=\",type:\"@\"},templateUrl:\"template/progressbar/bar.html\",link:function(e,t,n,r){r.addBar(e,t)}}}).directive(\"progressbar\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,controller:\"ProgressController\",scope:{value:\"=\",type:\"@\"},templateUrl:\"template/progressbar/progressbar.html\",link:function(e,t,n,r){r.addBar(e,angular.element(t.children()[0]))}}}),angular.module(\"ui.bootstrap.rating\",[]).constant(\"ratingConfig\",{max:5,stateOn:null,stateOff:null}).controller(\"RatingController\",[\"$scope\",\"$attrs\",\"ratingConfig\",function(n,r,i){var o={$setViewValue:angular.noop};this.init=function(e){(o=e).$render=this.render,this.stateOn=angular.isDefined(r.stateOn)?n.$parent.$eval(r.stateOn):i.stateOn,this.stateOff=angular.isDefined(r.stateOff)?n.$parent.$eval(r.stateOff):i.stateOff;var t=angular.isDefined(r.ratingStates)?n.$parent.$eval(r.ratingStates):new Array(angular.isDefined(r.max)?n.$parent.$eval(r.max):i.max);n.range=this.buildTemplateObjects(t)},this.buildTemplateObjects=function(e){for(var t=0,n=e.length;t<n;t++)e[t]=angular.extend({index:t},{stateOn:this.stateOn,stateOff:this.stateOff},e[t]);return e},n.rate=function(e){!n.readonly&&0<=e&&e<=n.range.length&&(o.$setViewValue(e),o.$render())},n.enter=function(e){n.readonly||(n.value=e),n.onHover({value:e})},n.reset=function(){n.value=o.$viewValue,n.onLeave()},n.onKeydown=function(e){/(37|38|39|40)/.test(e.which)&&(e.preventDefault(),e.stopPropagation(),n.rate(n.value+(38===e.which||39===e.which?1:-1)))},this.render=function(){n.value=o.$viewValue}}]).directive(\"rating\",function(){return{restrict:\"EA\",require:[\"rating\",\"ngModel\"],scope:{readonly:\"=?\",onHover:\"&\",onLeave:\"&\"},controller:\"RatingController\",templateUrl:\"template/rating/rating.html\",replace:!0,link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o)}}}),angular.module(\"ui.bootstrap.tabs\",[]).controller(\"TabsetController\",[\"$scope\",function(e){var r,i=this,o=i.tabs=e.tabs=[];i.select=function(t){angular.forEach(o,function(e){e.active&&e!==t&&(e.active=!1,e.onDeselect())}),t.active=!0,t.onSelect()},i.addTab=function(e){o.push(e),1===o.length?e.active=!0:e.active&&i.select(e)},i.removeTab=function(e){var t=o.indexOf(e);if(e.active&&1<o.length&&!r){var n=t==o.length-1?t-1:t+1;i.select(o[n])}o.splice(t,1)},e.$on(\"$destroy\",function(){r=!0})}]).directive(\"tabset\",function(){return{restrict:\"EA\",transclude:!0,replace:!0,scope:{type:\"@\"},controller:\"TabsetController\",templateUrl:\"template/tabs/tabset.html\",link:function(e,t,n){e.vertical=!!angular.isDefined(n.vertical)&&e.$parent.$eval(n.vertical),e.justified=!!angular.isDefined(n.justified)&&e.$parent.$eval(n.justified)}}}).directive(\"tab\",[\"$parse\",function(o){return{require:\"^tabset\",restrict:\"EA\",replace:!0,templateUrl:\"template/tabs/tab.html\",transclude:!0,scope:{active:\"=?\",heading:\"@\",onSelect:\"&select\",onDeselect:\"&deselect\"},controller:function(){},compile:function(e,t,i){return function(t,e,n,r){t.$watch(\"active\",function(e){e&&r.select(t)}),t.disabled=!1,n.disabled&&t.$parent.$watch(o(n.disabled),function(e){t.disabled=!!e}),t.select=function(){t.disabled||(t.active=!0)},r.addTab(t),t.$on(\"$destroy\",function(){r.removeTab(t)}),t.$transcludeFn=i}}}}]).directive(\"tabHeadingTransclude\",[function(){return{restrict:\"A\",require:\"^tab\",link:function(e,t){e.$watch(\"headingElement\",function(e){e&&(t.html(\"\"),t.append(e))})}}}]).directive(\"tabContentTransclude\",function(){return{restrict:\"A\",require:\"^tabset\",link:function(e,t,n){var r=e.$eval(n.tabContentTransclude);r.$transcludeFn(r.$parent,function(e){angular.forEach(e,function(e){!function(e){return e.tagName&&(e.hasAttribute(\"tab-heading\")||e.hasAttribute(\"data-tab-heading\")||\"tab-heading\"===e.tagName.toLowerCase()||\"data-tab-heading\"===e.tagName.toLowerCase())}(e)?t.append(e):r.headingElement=e})})}}}),angular.module(\"ui.bootstrap.timepicker\",[]).constant(\"timepickerConfig\",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0}).controller(\"TimepickerController\",[\"$scope\",\"$attrs\",\"$parse\",\"$log\",\"$locale\",\"timepickerConfig\",function(i,o,e,t,n,a){function r(){var e=parseInt(i.hours,10);return(i.showMeridian?0<e&&e<13:0<=e&&e<24)?(i.showMeridian&&(12===e&&(e=0),i.meridian===g[1]&&(e+=12)),e):void 0}function s(){var e=parseInt(i.minutes,10);return 0<=e&&e<60?e:void 0}function u(e){return angular.isDefined(e)&&e.toString().length<2?\"0\"+e:e}function l(e){c(),f.$setViewValue(new Date(h)),d(e)}function c(){f.$setValidity(\"time\",!0),i.invalidHours=!1,i.invalidMinutes=!1}function d(e){var t=h.getHours(),n=h.getMinutes();i.showMeridian&&(t=0===t||12===t?12:t%12),i.hours=\"h\"===e?t:u(t),i.minutes=\"m\"===e?n:u(n),i.meridian=h.getHours()<12?g[0]:g[1]}function p(e){var t=new Date(h.getTime()+6e4*e);h.setHours(t.getHours(),t.getMinutes()),l()}var h=new Date,f={$setViewValue:angular.noop},g=angular.isDefined(o.meridians)?i.$parent.$eval(o.meridians):a.meridians||n.DATETIME_FORMATS.AMPMS;this.init=function(e,t){(f=e).$render=this.render;var n=t.eq(0),r=t.eq(1);(angular.isDefined(o.mousewheel)?i.$parent.$eval(o.mousewheel):a.mousewheel)&&this.setupMousewheelEvents(n,r),i.readonlyInput=angular.isDefined(o.readonlyInput)?i.$parent.$eval(o.readonlyInput):a.readonlyInput,this.setupInputEvents(n,r)};var m=a.hourStep;o.hourStep&&i.$parent.$watch(e(o.hourStep),function(e){m=parseInt(e,10)});var v=a.minuteStep;o.minuteStep&&i.$parent.$watch(e(o.minuteStep),function(e){v=parseInt(e,10)}),i.showMeridian=a.showMeridian,o.showMeridian&&i.$parent.$watch(e(o.showMeridian),function(e){if(i.showMeridian=!!e,f.$error.time){var t=r(),n=s();angular.isDefined(t)&&angular.isDefined(n)&&(h.setHours(t),l())}else d()}),this.setupMousewheelEvents=function(e,t){var n=function(e){e.originalEvent&&(e=e.originalEvent);var t=e.wheelDelta?e.wheelDelta:-e.deltaY;return e.detail||0<t};e.bind(\"mousewheel wheel\",function(e){i.$apply(n(e)?i.incrementHours():i.decrementHours()),e.preventDefault()}),t.bind(\"mousewheel wheel\",function(e){i.$apply(n(e)?i.incrementMinutes():i.decrementMinutes()),e.preventDefault()})},this.setupInputEvents=function(e,t){if(i.readonlyInput)return i.updateHours=angular.noop,void(i.updateMinutes=angular.noop);var n=function(e,t){f.$setViewValue(null),f.$setValidity(\"time\",!1),angular.isDefined(e)&&(i.invalidHours=e),angular.isDefined(t)&&(i.invalidMinutes=t)};i.updateHours=function(){var e=r();angular.isDefined(e)?(h.setHours(e),l(\"h\")):n(!0)},e.bind(\"blur\",function(){!i.invalidHours&&i.hours<10&&i.$apply(function(){i.hours=u(i.hours)})}),i.updateMinutes=function(){var e=s();angular.isDefined(e)?(h.setMinutes(e),l(\"m\")):n(void 0,!0)},t.bind(\"blur\",function(){!i.invalidMinutes&&i.minutes<10&&i.$apply(function(){i.minutes=u(i.minutes)})})},this.render=function(){var e=f.$modelValue?new Date(f.$modelValue):null;isNaN(e)?(f.$setValidity(\"time\",!1),t.error('Timepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(e&&(h=e),c(),d())},i.incrementHours=function(){p(60*m)},i.decrementHours=function(){p(60*-m)},i.incrementMinutes=function(){p(v)},i.decrementMinutes=function(){p(-v)},i.toggleMeridian=function(){p(720*(h.getHours()<12?1:-1))}}]).directive(\"timepicker\",function(){return{restrict:\"EA\",require:[\"timepicker\",\"?^ngModel\"],controller:\"TimepickerController\",replace:!0,scope:{},templateUrl:\"template/timepicker/timepicker.html\",link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o,t.find(\"input\"))}}}),angular.module(\"ui.bootstrap.typeahead\",[\"ui.bootstrap.position\",\"ui.bootstrap.bindHtml\"]).factory(\"typeaheadParser\",[\"$parse\",function(n){var r=/^\\s*([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?\\s+for\\s+(?:([\\$\\w][\\$\\w\\d]*))\\s+in\\s+([\\s\\S]+?)$/;return{parse:function(e){var t=e.match(r);if(!t)throw new Error('Expected typeahead specification in form of \"_modelValue_ (as _label_)? for _item_ in _collection_\" but got \"'+e+'\".');return{itemName:t[3],source:n(t[4]),viewMapper:n(t[2]||t[1]),modelMapper:n(t[1])}}}}]).directive(\"typeahead\",[\"$compile\",\"$parse\",\"$q\",\"$timeout\",\"$document\",\"$position\",\"typeaheadParser\",function(k,D,x,O,T,A,E){var P=[9,13,27,38,40];return{require:\"ngModel\",link:function(o,a,e,s){var u,t=o.$eval(e.typeaheadMinLength)||1,n=o.$eval(e.typeaheadWaitMs)||0,r=!1!==o.$eval(e.typeaheadEditable),l=D(e.typeaheadLoading).assign||angular.noop,i=D(e.typeaheadOnSelect),c=e.typeaheadInputFormatter?D(e.typeaheadInputFormatter):void 0,d=!!e.typeaheadAppendToBody&&o.$eval(e.typeaheadAppendToBody),p=!1!==o.$eval(e.typeaheadFocusFirst),h=D(e.ngModel).assign,f=E.parse(e.typeahead),g=o.$new();o.$on(\"$destroy\",function(){g.$destroy()});var m=\"typeahead-\"+g.$id+\"-\"+Math.floor(1e4*Math.random());a.attr({\"aria-autocomplete\":\"list\",\"aria-expanded\":!1,\"aria-owns\":m});var v=angular.element(\"<div typeahead-popup></div>\");v.attr({id:m,matches:\"matches\",active:\"activeIdx\",select:\"select(activeIdx)\",query:\"query\",position:\"position\"}),angular.isDefined(e.typeaheadTemplateUrl)&&v.attr(\"template-url\",e.typeaheadTemplateUrl);var y=function(){g.matches=[],g.activeIdx=-1,a.attr(\"aria-expanded\",!1)},w=function(e){return m+\"-option-\"+e};g.$watch(\"activeIdx\",function(e){e<0?a.removeAttr(\"aria-activedescendant\"):a.attr(\"aria-activedescendant\",w(e))});var b=function(r){var i={$viewValue:r};l(o,!0),x.when(f.source(o,i)).then(function(e){var t=r===s.$viewValue;if(t&&u)if(0<e.length){g.activeIdx=p?0:-1;for(var n=g.matches.length=0;n<e.length;n++)i[f.itemName]=e[n],g.matches.push({id:w(n),label:f.viewMapper(g,i),model:e[n]});g.query=r,g.position=d?A.offset(a):A.position(a),g.position.top=g.position.top+a.prop(\"offsetHeight\"),a.attr(\"aria-expanded\",!0)}else y();t&&l(o,!1)},function(){y(),l(o,!1)})};y(),g.query=void 0;var $,_=function(){$&&O.cancel($)};s.$parsers.unshift(function(e){return u=!0,e&&e.length>=t?0<n?(_(),function(e){$=O(function(){b(e)},n)}(e)):b(e):(l(o,!1),_(),y()),r?e:e?void s.$setValidity(\"editable\",!1):(s.$setValidity(\"editable\",!0),e)}),s.$formatters.push(function(e){var t,n={};return c?(n.$model=e,c(o,n)):(n[f.itemName]=e,t=f.viewMapper(o,n),n[f.itemName]=void 0,t!==f.viewMapper(o,n)?t:e)}),g.select=function(e){var t,n,r={};r[f.itemName]=n=g.matches[e].model,t=f.modelMapper(o,r),h(o,t),s.$setValidity(\"editable\",!0),i(o,{$item:n,$model:t,$label:f.viewMapper(o,r)}),y(),O(function(){a[0].focus()},0,!1)},a.bind(\"keydown\",function(e){0!==g.matches.length&&-1!==P.indexOf(e.which)&&(-1!=g.activeIdx||13!==e.which&&9!==e.which)&&(e.preventDefault(),40===e.which?(g.activeIdx=(g.activeIdx+1)%g.matches.length,g.$digest()):38===e.which?(g.activeIdx=(0<g.activeIdx?g.activeIdx:g.matches.length)-1,g.$digest()):13===e.which||9===e.which?g.$apply(function(){g.select(g.activeIdx)}):27===e.which&&(e.stopPropagation(),y(),g.$digest()))}),a.bind(\"blur\",function(){u=!1});var S=function(e){a[0]!==e.target&&(y(),g.$digest())};T.bind(\"click\",S),o.$on(\"$destroy\",function(){T.unbind(\"click\",S),d&&C.remove()});var C=k(v)(g);d?T.find(\"body\").append(C):a.after(C)}}}]).directive(\"typeaheadPopup\",function(){return{restrict:\"EA\",scope:{matches:\"=\",query:\"=\",active:\"=\",position:\"=\",select:\"&\"},replace:!0,templateUrl:\"template/typeahead/typeahead-popup.html\",link:function(t,e,n){t.templateUrl=n.templateUrl,t.isOpen=function(){return 0<t.matches.length},t.isActive=function(e){return t.active==e},t.selectActive=function(e){t.active=e},t.selectMatch=function(e){t.select({activeIdx:e})}}}}).directive(\"typeaheadMatch\",[\"$http\",\"$templateCache\",\"$compile\",\"$parse\",function(i,o,a,s){return{restrict:\"EA\",scope:{index:\"=\",match:\"=\",query:\"=\"},link:function(t,n,e){var r=s(e.templateUrl)(t.$parent)||\"template/typeahead/typeahead-match.html\";i.get(r,{cache:o}).success(function(e){n.replaceWith(a(e.trim())(t))})}}}]).filter(\"typeaheadHighlight\",function(){return function(e,t){return t?(\"\"+e).replace(new RegExp(function(e){return e.replace(/([.?*+^$[\\]\\\\(){}|-])/g,\"\\\\$1\")}(t),\"gi\"),\"<strong>$&</strong>\"):e}}),angular.module(\"template/accordion/accordion-group.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/accordion/accordion-group.html\",'<div class=\"panel panel-default\">\\n  <div class=\"panel-heading\">\\n    <h4 class=\"panel-title\">\\n      <a href class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{\\'text-muted\\': isDisabled}\">{{heading}}</span></a>\\n    </h4>\\n  </div>\\n  <div class=\"panel-collapse\" collapse=\"!isOpen\">\\n\\t  <div class=\"panel-body\" ng-transclude></div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/accordion/accordion.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/accordion/accordion.html\",'<div class=\"panel-group\" ng-transclude></div>')}]),angular.module(\"template/alert/alert.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/alert/alert.html\",'<div class=\"alert\" ng-class=\"[\\'alert-\\' + (type || \\'warning\\'), closeable ? \\'alert-dismissable\\' : null]\" role=\"alert\">\\n    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\\n        <span aria-hidden=\"true\">&times;</span>\\n        <span class=\"sr-only\">Close</span>\\n    </button>\\n    <div ng-transclude></div>\\n</div>\\n')}]),angular.module(\"template/carousel/carousel.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/carousel/carousel.html\",'<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\\n    <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\\n        <li ng-repeat=\"slide in slides track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\\n    </ol>\\n    <div class=\"carousel-inner\" ng-transclude></div>\\n    <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\\n    <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-right\"></span></a>\\n</div>\\n')}]),angular.module(\"template/carousel/slide.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/carousel/slide.html\",\"<div ng-class=\\\"{\\n    'active': leaving || (active && !entering),\\n    'prev': (next || active) && direction=='prev',\\n    'next': (next || active) && direction=='next',\\n    'right': direction=='prev',\\n    'left': direction=='next'\\n  }\\\" class=\\\"item text-center\\\" ng-transclude></div>\\n\")}]),angular.module(\"template/datepicker/datepicker.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/datepicker.html\",'<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\\n  <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\\n  <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\\n  <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\\n</div>')}]),angular.module(\"template/datepicker/day.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/day.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n    <tr>\\n      <th ng-show=\"showWeeks\" class=\"text-center\"></th>\\n      <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-muted\\': dt.secondary, \\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/datepicker/month.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/month.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/datepicker/popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/popup.html\",'<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && \\'block\\') || \\'none\\', top: position.top+\\'px\\', left: position.left+\\'px\\'}\" ng-keydown=\"keydown($event)\">\\n\\t<li ng-transclude></li>\\n\\t<li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\\n\\t\\t<span class=\"btn-group pull-left\">\\n\\t\\t\\t<button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select(\\'today\\')\">{{ getText(\\'current\\') }}</button>\\n\\t\\t\\t<button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText(\\'clear\\') }}</button>\\n\\t\\t</span>\\n\\t\\t<button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText(\\'close\\') }}</button>\\n\\t</li>\\n</ul>\\n')}]),angular.module(\"template/datepicker/year.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/year.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/modal/backdrop.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/modal/backdrop.html\",'<div class=\"modal-backdrop fade {{ backdropClass }}\"\\n     ng-class=\"{in: animate}\"\\n     ng-style=\"{\\'z-index\\': 1040 + (index && 1 || 0) + index*10}\"\\n></div>\\n')}]),angular.module(\"template/modal/window.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/modal/window.html\",'<div tabindex=\"-1\" role=\"dialog\" class=\"modal fade\" ng-class=\"{in: animate}\" ng-style=\"{\\'z-index\\': 1050 + index*10, display: \\'block\\'}\" ng-click=\"close($event)\">\\n    <div class=\"modal-dialog\" ng-class=\"{\\'modal-sm\\': size == \\'sm\\', \\'modal-lg\\': size == \\'lg\\'}\"><div class=\"modal-content\" modal-transclude></div></div>\\n</div>')}]),angular.module(\"template/pagination/pager.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/pagination/pager.html\",'<ul class=\"pager\">\\n  <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1)\">{{getText(\\'previous\\')}}</a></li>\\n  <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1)\">{{getText(\\'next\\')}}</a></li>\\n</ul>')}]),angular.module(\"template/pagination/pagination.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/pagination/pagination.html\",'<ul class=\"pagination\">\\n  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1)\">{{getText(\\'first\\')}}</a></li>\\n  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1)\">{{getText(\\'previous\\')}}</a></li>\\n  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\\n  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1)\">{{getText(\\'next\\')}}</a></li>\\n  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages)\">{{getText(\\'last\\')}}</a></li>\\n</ul>')}]),angular.module(\"template/tooltip/tooltip-html-unsafe-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tooltip/tooltip-html-unsafe-popup.html\",'<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"tooltip-arrow\"></div>\\n  <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\\n</div>\\n')}]),angular.module(\"template/tooltip/tooltip-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tooltip/tooltip-popup.html\",'<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"tooltip-arrow\"></div>\\n  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\\n</div>\\n')}]),angular.module(\"template/popover/popover.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/popover/popover.html\",'<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"arrow\"></div>\\n\\n  <div class=\"popover-inner\">\\n      <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\\n      <div class=\"popover-content\" ng-bind=\"content\"></div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/progressbar/bar.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/bar.html\",'<div class=\"progress-bar\" ng-class=\"type && \\'progress-bar-\\' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + \\'%\\'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>')}]),angular.module(\"template/progressbar/progress.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/progress.html\",'<div class=\"progress\" ng-transclude></div>')}]),angular.module(\"template/progressbar/progressbar.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/progressbar.html\",'<div class=\"progress\">\\n  <div class=\"progress-bar\" ng-class=\"type && \\'progress-bar-\\' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + \\'%\\'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\\n</div>')}]),angular.module(\"template/rating/rating.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/rating/rating.html\",'<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\\n    <i ng-repeat=\"r in range track by $index\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || \\'glyphicon-star\\') || (r.stateOff || \\'glyphicon-star-empty\\')\">\\n        <span class=\"sr-only\">({{ $index < value ? \\'*\\' : \\' \\' }})</span>\\n    </i>\\n</span>')}]),angular.module(\"template/tabs/tab.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tabs/tab.html\",'<li ng-class=\"{active: active, disabled: disabled}\">\\n  <a href ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\\n</li>\\n')}]),angular.module(\"template/tabs/tabset.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tabs/tabset.html\",'<div>\\n  <ul class=\"nav nav-{{type || \\'tabs\\'}}\" ng-class=\"{\\'nav-stacked\\': vertical, \\'nav-justified\\': justified}\" ng-transclude></ul>\\n  <div class=\"tab-content\">\\n    <div class=\"tab-pane\" \\n         ng-repeat=\"tab in tabs\" \\n         ng-class=\"{active: tab.active}\"\\n         tab-content-transclude=\"tab\">\\n    </div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/timepicker/timepicker.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/timepicker/timepicker.html\",'<table>\\n\\t<tbody>\\n\\t\\t<tr class=\"text-center\">\\n\\t\\t\\t<td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\\n\\t\\t\\t<td>&nbsp;</td>\\n\\t\\t\\t<td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"></td>\\n\\t\\t</tr>\\n\\t\\t<tr>\\n\\t\\t\\t<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{\\'has-error\\': invalidHours}\">\\n\\t\\t\\t\\t<input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\\n\\t\\t\\t</td>\\n\\t\\t\\t<td>:</td>\\n\\t\\t\\t<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{\\'has-error\\': invalidMinutes}\">\\n\\t\\t\\t\\t<input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\\n\\t\\t\\t</td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\\n\\t\\t</tr>\\n\\t\\t<tr class=\"text-center\">\\n\\t\\t\\t<td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\\n\\t\\t\\t<td>&nbsp;</td>\\n\\t\\t\\t<td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"></td>\\n\\t\\t</tr>\\n\\t</tbody>\\n</table>\\n')}]),angular.module(\"template/typeahead/typeahead-match.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/typeahead/typeahead-match.html\",'<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>')}]),angular.module(\"template/typeahead/typeahead-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/typeahead/typeahead-popup.html\",'<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+\\'px\\', left: position.left+\\'px\\'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\\n    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\\n        <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\\n    </li>\\n</ul>\\n')}]),function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){\"use strict\";var e,i;function p(){return e.apply(null,arguments)}function a(e){return e instanceof Array||\"[object Array]\"===Object.prototype.toString.call(e)}function s(e){return null!=e&&\"[object Object]\"===Object.prototype.toString.call(e)}function o(e){return void 0===e}function u(e){return\"number\"==typeof e||\"[object Number]\"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||\"[object Date]\"===Object.prototype.toString.call(e)}function r(e,t){var n,r=[];for(n=0;n<e.length;++n)r.push(t(e[n],n));return r}function h(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function c(e,t){for(var n in t)h(t,n)&&(e[n]=t[n]);return h(t,\"toString\")&&(e.toString=t.toString),h(t,\"valueOf\")&&(e.valueOf=t.valueOf),e}function d(e,t,n,r){return xt(e,t,n,r,!0).utc()}function f(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function g(e){if(null==e._isValid){var t=f(e),n=i.call(t.parsedDateParts,function(e){return null!=e}),r=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(r=r&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return r;e._isValid=r}return e._isValid}function m(e){var t=d(NaN);return null!=e?c(f(t),e):f(t).userInvalidated=!0,t}i=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,r=0;r<n;r++)if(r in t&&e.call(this,t[r],r,t))return!0;return!1};var v=p.momentProperties=[];function y(e,t){var n,r,i;if(o(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),o(t._i)||(e._i=t._i),o(t._f)||(e._f=t._f),o(t._l)||(e._l=t._l),o(t._strict)||(e._strict=t._strict),o(t._tzm)||(e._tzm=t._tzm),o(t._isUTC)||(e._isUTC=t._isUTC),o(t._offset)||(e._offset=t._offset),o(t._pf)||(e._pf=f(t)),o(t._locale)||(e._locale=t._locale),0<v.length)for(n=0;n<v.length;n++)o(i=t[r=v[n]])||(e[r]=i);return e}var t=!1;function w(e){y(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===t&&(t=!0,p.updateOffset(this),t=!1)}function b(e){return e instanceof w||null!=e&&null!=e._isAMomentObject}function $(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function _(e){var t=+e,n=0;return 0!=t&&isFinite(t)&&(n=$(t)),n}function S(e,t,n){var r,i=Math.min(e.length,t.length),o=Math.abs(e.length-t.length),a=0;for(r=0;r<i;r++)(n&&e[r]!==t[r]||!n&&_(e[r])!==_(t[r]))&&a++;return a+o}function C(e){!1===p.suppressDeprecationWarnings&&\"undefined\"!=typeof console&&console.warn&&console.warn(\"Deprecation warning: \"+e)}function n(i,o){var a=!0;return c(function(){if(null!=p.deprecationHandler&&p.deprecationHandler(null,i),a){for(var e,t=[],n=0;n<arguments.length;n++){if(e=\"\",\"object\"==typeof arguments[n]){for(var r in e+=\"\\n[\"+n+\"] \",arguments[0])e+=r+\": \"+arguments[0][r]+\", \";e=e.slice(0,-2)}else e=arguments[n];t.push(e)}C(i+\"\\nArguments: \"+Array.prototype.slice.call(t).join(\"\")+\"\\n\"+(new Error).stack),a=!1}return o.apply(this,arguments)},o)}var k,D={};function x(e,t){null!=p.deprecationHandler&&p.deprecationHandler(e,t),D[e]||(C(t),D[e]=!0)}function O(e){return e instanceof Function||\"[object Function]\"===Object.prototype.toString.call(e)}function T(e,t){var n,r=c({},e);for(n in t)h(t,n)&&(s(e[n])&&s(t[n])?(r[n]={},c(r[n],e[n]),c(r[n],t[n])):null!=t[n]?r[n]=t[n]:delete r[n]);for(n in e)h(e,n)&&!h(t,n)&&s(e[n])&&(r[n]=c({},r[n]));return r}function A(e){null!=e&&this.set(e)}p.suppressDeprecationWarnings=!1,p.deprecationHandler=null,k=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)h(e,t)&&n.push(t);return n};var E={};function P(e,t){var n=e.toLowerCase();E[n]=E[n+\"s\"]=E[t]=e}function M(e){return\"string\"==typeof e?E[e]||E[e.toLowerCase()]:void 0}function R(e){var t,n,r={};for(n in e)h(e,n)&&(t=M(n))&&(r[t]=e[n]);return r}var I={};function V(e,t){I[e]=t}function L(e,t,n){var r=\"\"+Math.abs(e),i=t-r.length;return(0<=e?n?\"+\":\"\":\"-\")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var F=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,j=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,H={},N={};function Y(e,t,n,r){var i=r;\"string\"==typeof r&&(i=function(){return this[r]()}),e&&(N[e]=i),t&&(N[t[0]]=function(){return L(i.apply(this,arguments),t[1],t[2])}),n&&(N[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function U(e,t){return e.isValid()?(t=q(t,e.localeData()),H[t]=H[t]||function(r){var e,i,t,o=r.match(F);for(e=0,i=o.length;e<i;e++)N[o[e]]?o[e]=N[o[e]]:o[e]=(t=o[e]).match(/\\[[\\s\\S]/)?t.replace(/^\\[|\\]$/g,\"\"):t.replace(/\\\\/g,\"\");return function(e){var t,n=\"\";for(t=0;t<i;t++)n+=O(o[t])?o[t].call(e,r):o[t];return n}}(t),H[t](e)):e.localeData().invalidDate()}function q(e,t){var n=5;function r(e){return t.longDateFormat(e)||e}for(j.lastIndex=0;0<=n&&j.test(e);)e=e.replace(j,r),j.lastIndex=0,n-=1;return e}var z=/\\d/,B=/\\d\\d/,W=/\\d{3}/,G=/\\d{4}/,K=/[+-]?\\d{6}/,Q=/\\d\\d?/,Z=/\\d\\d\\d\\d?/,X=/\\d\\d\\d\\d\\d\\d?/,J=/\\d{1,3}/,ee=/\\d{1,4}/,te=/[+-]?\\d{1,6}/,ne=/\\d+/,re=/[+-]?\\d+/,ie=/Z|[+-]\\d\\d:?\\d\\d/gi,oe=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,ae=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i,se={};function ue(e,n,r){se[e]=O(n)?n:function(e,t){return e&&r?r:n}}function le(e,t){return h(se,e)?se[e](t._strict,t._locale):new RegExp(function(e){return ce(e.replace(\"\\\\\",\"\").replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,function(e,t,n,r,i){return t||n||r||i}))}(e))}function ce(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\")}var de={};function pe(e,n){var t,r=n;for(\"string\"==typeof e&&(e=[e]),u(n)&&(r=function(e,t){t[n]=_(e)}),t=0;t<e.length;t++)de[e[t]]=r}function he(e,i){pe(e,function(e,t,n,r){n._w=n._w||{},i(e,n._w,n,r)})}var fe=0,ge=1,me=2,ve=3,ye=4,we=5,be=6,$e=7,_e=8;function Se(e){return Ce(e)?366:365}function Ce(e){return e%4==0&&e%100!=0||e%400==0}Y(\"Y\",0,0,function(){var e=this.year();return e<=9999?\"\"+e:\"+\"+e}),Y(0,[\"YY\",2],0,function(){return this.year()%100}),Y(0,[\"YYYY\",4],0,\"year\"),Y(0,[\"YYYYY\",5],0,\"year\"),Y(0,[\"YYYYYY\",6,!0],0,\"year\"),P(\"year\",\"y\"),V(\"year\",1),ue(\"Y\",re),ue(\"YY\",Q,B),ue(\"YYYY\",ee,G),ue(\"YYYYY\",te,K),ue(\"YYYYYY\",te,K),pe([\"YYYYY\",\"YYYYYY\"],fe),pe(\"YYYY\",function(e,t){t[fe]=2===e.length?p.parseTwoDigitYear(e):_(e)}),pe(\"YY\",function(e,t){t[fe]=p.parseTwoDigitYear(e)}),pe(\"Y\",function(e,t){t[fe]=parseInt(e,10)}),p.parseTwoDigitYear=function(e){return _(e)+(68<_(e)?1900:2e3)};var ke,De=xe(\"FullYear\",!0);function xe(t,n){return function(e){return null!=e?(Te(this,t,e),p.updateOffset(this,n),this):Oe(this,t)}}function Oe(e,t){return e.isValid()?e._d[\"get\"+(e._isUTC?\"UTC\":\"\")+t]():NaN}function Te(e,t,n){e.isValid()&&!isNaN(n)&&(\"FullYear\"===t&&Ce(e.year())&&1===e.month()&&29===e.date()?e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+t](n,e.month(),Ae(n,e.month())):e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+t](n))}function Ae(e,t){if(isNaN(e)||isNaN(t))return NaN;var n=function(e,t){return(e%t+t)%t}(t,12);return e+=(t-n)/12,1===n?Ce(e)?29:28:31-n%7%2}ke=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},Y(\"M\",[\"MM\",2],\"Mo\",function(){return this.month()+1}),Y(\"MMM\",0,0,function(e){return this.localeData().monthsShort(this,e)}),Y(\"MMMM\",0,0,function(e){return this.localeData().months(this,e)}),P(\"month\",\"M\"),V(\"month\",8),ue(\"M\",Q),ue(\"MM\",Q,B),ue(\"MMM\",function(e,t){return t.monthsShortRegex(e)}),ue(\"MMMM\",function(e,t){return t.monthsRegex(e)}),pe([\"M\",\"MM\"],function(e,t){t[ge]=_(e)-1}),pe([\"MMM\",\"MMMM\"],function(e,t,n,r){var i=n._locale.monthsParse(e,r,n._strict);null!=i?t[ge]=i:f(n).invalidMonth=e});var Ee=/D[oD]?(\\[[^\\[\\]]*\\]|\\s)+MMMM?/,Pe=\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\");var Me=\"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec\".split(\"_\");function Re(e,t){var n;if(!e.isValid())return e;if(\"string\"==typeof t)if(/^\\d+$/.test(t))t=_(t);else if(!u(t=e.localeData().monthsParse(t)))return e;return n=Math.min(e.date(),Ae(e.year(),t)),e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+\"Month\"](t,n),e}function Ie(e){return null!=e?(Re(this,e),p.updateOffset(this,!0),this):Oe(this,\"Month\")}var Ve=ae;var Le=ae;function Fe(){function e(e,t){return t.length-e.length}var t,n,r=[],i=[],o=[];for(t=0;t<12;t++)n=d([2e3,t]),r.push(this.monthsShort(n,\"\")),i.push(this.months(n,\"\")),o.push(this.months(n,\"\")),o.push(this.monthsShort(n,\"\"));for(r.sort(e),i.sort(e),o.sort(e),t=0;t<12;t++)r[t]=ce(r[t]),i[t]=ce(i[t]);for(t=0;t<24;t++)o[t]=ce(o[t]);this._monthsRegex=new RegExp(\"^(\"+o.join(\"|\")+\")\",\"i\"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp(\"^(\"+i.join(\"|\")+\")\",\"i\"),this._monthsShortStrictRegex=new RegExp(\"^(\"+r.join(\"|\")+\")\",\"i\")}function je(e){var t;if(e<100&&0<=e){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function He(e,t,n){var r=7+t-n;return r-(7+je(e,0,r).getUTCDay()-t)%7-1}function Ne(e,t,n,r,i){var o,a,s=1+7*(t-1)+(7+n-r)%7+He(e,r,i);return a=s<=0?Se(o=e-1)+s:s>Se(e)?(o=e+1,s-Se(e)):(o=e,s),{year:o,dayOfYear:a}}function Ye(e,t,n){var r,i,o=He(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=He(e,t,n),i=He(e+1,t,n);return(Se(e)-r+i)/7}Y(\"w\",[\"ww\",2],\"wo\",\"week\"),Y(\"W\",[\"WW\",2],\"Wo\",\"isoWeek\"),P(\"week\",\"w\"),P(\"isoWeek\",\"W\"),V(\"week\",5),V(\"isoWeek\",5),ue(\"w\",Q),ue(\"ww\",Q,B),ue(\"W\",Q),ue(\"WW\",Q,B),he([\"w\",\"ww\",\"W\",\"WW\"],function(e,t,n,r){t[r.substr(0,1)]=_(e)});function qe(e,t){return e.slice(t,7).concat(e.slice(0,t))}Y(\"d\",0,\"do\",\"day\"),Y(\"dd\",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),Y(\"ddd\",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),Y(\"dddd\",0,0,function(e){return this.localeData().weekdays(this,e)}),Y(\"e\",0,0,\"weekday\"),Y(\"E\",0,0,\"isoWeekday\"),P(\"day\",\"d\"),P(\"weekday\",\"e\"),P(\"isoWeekday\",\"E\"),V(\"day\",11),V(\"weekday\",11),V(\"isoWeekday\",11),ue(\"d\",Q),ue(\"e\",Q),ue(\"E\",Q),ue(\"dd\",function(e,t){return t.weekdaysMinRegex(e)}),ue(\"ddd\",function(e,t){return t.weekdaysShortRegex(e)}),ue(\"dddd\",function(e,t){return t.weekdaysRegex(e)}),he([\"dd\",\"ddd\",\"dddd\"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e}),he([\"d\",\"e\",\"E\"],function(e,t,n,r){t[r]=_(e)});var ze=\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\");var Be=\"Sun_Mon_Tue_Wed_Thu_Fri_Sat\".split(\"_\");var We=\"Su_Mo_Tu_We_Th_Fr_Sa\".split(\"_\");var Ge=ae;var Ke=ae;var Qe=ae;function Ze(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],s=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),r=this.weekdaysMin(n,\"\"),i=this.weekdaysShort(n,\"\"),o=this.weekdays(n,\"\"),a.push(r),s.push(i),u.push(o),l.push(r),l.push(i),l.push(o);for(a.sort(e),s.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)s[t]=ce(s[t]),u[t]=ce(u[t]),l[t]=ce(l[t]);this._weekdaysRegex=new RegExp(\"^(\"+l.join(\"|\")+\")\",\"i\"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp(\"^(\"+u.join(\"|\")+\")\",\"i\"),this._weekdaysShortStrictRegex=new RegExp(\"^(\"+s.join(\"|\")+\")\",\"i\"),this._weekdaysMinStrictRegex=new RegExp(\"^(\"+a.join(\"|\")+\")\",\"i\")}function Xe(){return this.hours()%12||12}function Je(e,t){Y(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}Y(\"H\",[\"HH\",2],0,\"hour\"),Y(\"h\",[\"hh\",2],0,Xe),Y(\"k\",[\"kk\",2],0,function(){return this.hours()||24}),Y(\"hmm\",0,0,function(){return\"\"+Xe.apply(this)+L(this.minutes(),2)}),Y(\"hmmss\",0,0,function(){return\"\"+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),Y(\"Hmm\",0,0,function(){return\"\"+this.hours()+L(this.minutes(),2)}),Y(\"Hmmss\",0,0,function(){return\"\"+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Je(\"a\",!0),Je(\"A\",!1),P(\"hour\",\"h\"),V(\"hour\",13),ue(\"a\",et),ue(\"A\",et),ue(\"H\",Q),ue(\"h\",Q),ue(\"k\",Q),ue(\"HH\",Q,B),ue(\"hh\",Q,B),ue(\"kk\",Q,B),ue(\"hmm\",Z),ue(\"hmmss\",X),ue(\"Hmm\",Z),ue(\"Hmmss\",X),pe([\"H\",\"HH\"],ve),pe([\"k\",\"kk\"],function(e,t,n){var r=_(e);t[ve]=24===r?0:r}),pe([\"a\",\"A\"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe([\"h\",\"hh\"],function(e,t,n){t[ve]=_(e),f(n).bigHour=!0}),pe(\"hmm\",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r)),f(n).bigHour=!0}),pe(\"hmmss\",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i)),f(n).bigHour=!0}),pe(\"Hmm\",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r))}),pe(\"Hmmss\",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i))});var tt,nt=xe(\"Hours\",!0),rt={calendar:{sameDay:\"[Today at] LT\",nextDay:\"[Tomorrow at] LT\",nextWeek:\"dddd [at] LT\",lastDay:\"[Yesterday at] LT\",lastWeek:\"[Last] dddd [at] LT\",sameElse:\"L\"},longDateFormat:{LTS:\"h:mm:ss A\",LT:\"h:mm A\",L:\"MM/DD/YYYY\",LL:\"MMMM D, YYYY\",LLL:\"MMMM D, YYYY h:mm A\",LLLL:\"dddd, MMMM D, YYYY h:mm A\"},invalidDate:\"Invalid date\",ordinal:\"%d\",dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:\"in %s\",past:\"%s ago\",s:\"a few seconds\",ss:\"%d seconds\",m:\"a minute\",mm:\"%d minutes\",h:\"an hour\",hh:\"%d hours\",d:\"a day\",dd:\"%d days\",M:\"a month\",MM:\"%d months\",y:\"a year\",yy:\"%d years\"},months:Pe,monthsShort:Me,week:{dow:0,doy:6},weekdays:ze,weekdaysMin:We,weekdaysShort:Be,meridiemParse:/[ap]\\.?m?\\.?/i},it={},ot={};function at(e){return e?e.toLowerCase().replace(\"_\",\"-\"):e}function st(e){var t=null;if(!it[e]&&\"undefined\"!=typeof module&&module&&module.exports)try{t=tt._abbr,require(\"./locale/\"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=o(t)?ct(e):lt(e,t))?tt=n:\"undefined\"!=typeof console&&console.warn&&console.warn(\"Locale \"+e+\" not found. Did you forget to load it?\")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,r=rt;if(t.abbr=e,null!=it[e])x(\"defineLocaleOverride\",\"use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.\"),r=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])r=it[t.parentLocale]._config;else{if(null==(n=st(t.parentLocale)))return ot[t.parentLocale]||(ot[t.parentLocale]=[]),ot[t.parentLocale].push({name:e,config:t}),null;r=n._config}return it[e]=new A(T(r,t)),ot[e]&&ot[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ct(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!a(e)){if(t=st(e))return t;e=[e]}return function(e){for(var t,n,r,i,o=0;o<e.length;){for(t=(i=at(e[o]).split(\"-\")).length,n=(n=at(e[o+1]))?n.split(\"-\"):null;0<t;){if(r=st(i.slice(0,t).join(\"-\")))return r;if(n&&n.length>=t&&S(i,n,!0)>=t-1)break;t--}o++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[ge]<0||11<n[ge]?ge:n[me]<1||n[me]>Ae(n[fe],n[ge])?me:n[ve]<0||24<n[ve]||24===n[ve]&&(0!==n[ye]||0!==n[we]||0!==n[be])?ve:n[ye]<0||59<n[ye]?ye:n[we]<0||59<n[we]?we:n[be]<0||999<n[be]?be:-1,f(e)._overflowDayOfYear&&(t<fe||me<t)&&(t=me),f(e)._overflowWeeks&&-1===t&&(t=$e),f(e)._overflowWeekday&&-1===t&&(t=_e),f(e).overflow=t),e}function pt(e,t,n){return null!=e?e:null!=t?t:n}function ht(e){var t,n,r,i,o,a=[];if(!e._d){for(r=function(e){var t=new Date(p.now());return e._useUTC?[t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()]:[t.getFullYear(),t.getMonth(),t.getDate()]}(e),e._w&&null==e._a[me]&&null==e._a[ge]&&function(e){var t,n,r,i,o,a,s,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)o=1,a=4,n=pt(t.GG,e._a[fe],Ye(Ot(),1,4).year),r=pt(t.W,1),((i=pt(t.E,1))<1||7<i)&&(u=!0);else{o=e._locale._week.dow,a=e._locale._week.doy;var l=Ye(Ot(),o,a);n=pt(t.gg,e._a[fe],l.year),r=pt(t.w,l.week),null!=t.d?((i=t.d)<0||6<i)&&(u=!0):null!=t.e?(i=t.e+o,(t.e<0||6<t.e)&&(u=!0)):i=o}r<1||r>Ue(n,o,a)?f(e)._overflowWeeks=!0:null!=u?f(e)._overflowWeekday=!0:(s=Ne(n,r,i,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=pt(e._a[fe],r[fe]),(e._dayOfYear>Se(o)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=je(o,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ve]&&0===e._a[ye]&&0===e._a[we]&&0===e._a[be]&&(e._nextDay=!0,e._a[ve]=0),e._d=(e._useUTC?je:function(e,t,n,r,i,o,a){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,r,i,o,a),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,o,a),s}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ve]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(f(e).weekdayMismatch=!0)}}var ft=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,gt=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,mt=/Z|[+-]\\d\\d(?::?\\d\\d)?/,vt=[[\"YYYYYY-MM-DD\",/[+-]\\d{6}-\\d\\d-\\d\\d/],[\"YYYY-MM-DD\",/\\d{4}-\\d\\d-\\d\\d/],[\"GGGG-[W]WW-E\",/\\d{4}-W\\d\\d-\\d/],[\"GGGG-[W]WW\",/\\d{4}-W\\d\\d/,!1],[\"YYYY-DDD\",/\\d{4}-\\d{3}/],[\"YYYY-MM\",/\\d{4}-\\d\\d/,!1],[\"YYYYYYMMDD\",/[+-]\\d{10}/],[\"YYYYMMDD\",/\\d{8}/],[\"GGGG[W]WWE\",/\\d{4}W\\d{3}/],[\"GGGG[W]WW\",/\\d{4}W\\d{2}/,!1],[\"YYYYDDD\",/\\d{7}/]],yt=[[\"HH:mm:ss.SSSS\",/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],[\"HH:mm:ss,SSSS\",/\\d\\d:\\d\\d:\\d\\d,\\d+/],[\"HH:mm:ss\",/\\d\\d:\\d\\d:\\d\\d/],[\"HH:mm\",/\\d\\d:\\d\\d/],[\"HHmmss.SSSS\",/\\d\\d\\d\\d\\d\\d\\.\\d+/],[\"HHmmss,SSSS\",/\\d\\d\\d\\d\\d\\d,\\d+/],[\"HHmmss\",/\\d\\d\\d\\d\\d\\d/],[\"HHmm\",/\\d\\d\\d\\d/],[\"HH\",/\\d\\d/]],wt=/^\\/?Date\\((\\-?\\d+)/i;function bt(e){var t,n,r,i,o,a,s=e._i,u=ft.exec(s)||gt.exec(s);if(u){for(f(e).iso=!0,t=0,n=vt.length;t<n;t++)if(vt[t][1].exec(u[1])){i=vt[t][0],r=!1!==vt[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=yt.length;t<n;t++)if(yt[t][1].exec(u[3])){o=(u[2]||\" \")+yt[t][0];break}if(null==o)return void(e._isValid=!1)}if(!r&&null!=o)return void(e._isValid=!1);if(u[4]){if(!mt.exec(u[4]))return void(e._isValid=!1);a=\"Z\"}e._f=i+(o||\"\")+(a||\"\"),kt(e)}else e._isValid=!1}var $t=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/;function _t(e,t,n,r,i,o){var a=[function(e){var t=parseInt(e,10);{if(t<=49)return 2e3+t;if(t<=999)return 1900+t}return t}(e),Me.indexOf(t),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return o&&a.push(parseInt(o,10)),a}var St={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Ct(e){var t=$t.exec(function(e){return e.replace(/\\([^)]*\\)|[\\n\\t]/g,\" \").replace(/(\\s\\s+)/g,\" \").replace(/^\\s\\s*/,\"\").replace(/\\s\\s*$/,\"\")}(e._i));if(t){var n=_t(t[4],t[3],t[2],t[5],t[6],t[7]);if(!function(e,t,n){return!e||Be.indexOf(e)===new Date(t[0],t[1],t[2]).getDay()||(f(n).weekdayMismatch=!0,n._isValid=!1)}(t[1],n,e))return;e._a=n,e._tzm=function(e,t,n){if(e)return St[e];if(t)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(t[8],t[9],t[10]),e._d=je.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),f(e).rfc2822=!0}else e._isValid=!1}function kt(e){if(e._f!==p.ISO_8601)if(e._f!==p.RFC_2822){e._a=[],f(e).empty=!0;var t,n,r,i,o,a,s,u,l=\"\"+e._i,c=l.length,d=0;for(r=q(e._f,e._locale).match(F)||[],t=0;t<r.length;t++)i=r[t],(n=(l.match(le(i,e))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&f(e).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),N[i]?(n?f(e).empty=!1:f(e).unusedTokens.push(i),a=i,u=e,null!=(s=n)&&h(de,a)&&de[a](s,u._a,u,a)):e._strict&&!n&&f(e).unusedTokens.push(i);f(e).charsLeftOver=c-d,0<l.length&&f(e).unusedInput.push(l),e._a[ve]<=12&&!0===f(e).bigHour&&0<e._a[ve]&&(f(e).bigHour=void 0),f(e).parsedDateParts=e._a.slice(0),f(e).meridiem=e._meridiem,e._a[ve]=function(e,t,n){var r;if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0)),t)}(e._locale,e._a[ve],e._meridiem),ht(e),dt(e)}else Ct(e);else bt(e)}function Dt(e){var t=e._i,n=e._f;return e._locale=e._locale||ct(e._l),null===t||void 0===n&&\"\"===t?m({nullInput:!0}):(\"string\"==typeof t&&(e._i=t=e._locale.preparse(t)),b(t)?new w(dt(t)):(l(t)?e._d=t:a(n)?function(e){var t,n,r,i,o;if(0===e._f.length)return f(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<e._f.length;i++)o=0,t=y({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],kt(t),g(t)&&(o+=f(t).charsLeftOver,o+=10*f(t).unusedTokens.length,f(t).score=o,(null==r||o<r)&&(r=o,n=t));c(e,n||t)}(e):n?kt(e):function(e){var t=e._i;o(t)?e._d=new Date(p.now()):l(t)?e._d=new Date(t.valueOf()):\"string\"==typeof t?function(e){var t=wt.exec(e._i);null===t?(bt(e),!1===e._isValid&&(delete e._isValid,Ct(e),!1===e._isValid&&(delete e._isValid,p.createFromInputFallback(e)))):e._d=new Date(+t[1])}(e):a(t)?(e._a=r(t.slice(0),function(e){return parseInt(e,10)}),ht(e)):s(t)?function(e){if(!e._d){var t=R(e._i);e._a=r([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),ht(e)}}(e):u(t)?e._d=new Date(t):p.createFromInputFallback(e)}(e),g(e)||(e._d=null),e))}function xt(e,t,n,r,i){var o={};return!0!==n&&!1!==n||(r=n,n=void 0),(s(e)&&function(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}(e)||a(e)&&0===e.length)&&(e=void 0),o._isAMomentObject=!0,o._useUTC=o._isUTC=i,o._l=n,o._i=e,o._f=t,o._strict=r,function(e){var t=new w(dt(Dt(e)));return t._nextDay&&(t.add(1,\"d\"),t._nextDay=void 0),t}(o)}function Ot(e,t,n,r){return xt(e,t,n,r,!1)}p.createFromInputFallback=n(\"value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.\",function(e){e._d=new Date(e._i+(e._useUTC?\" UTC\":\"\"))}),p.ISO_8601=function(){},p.RFC_2822=function(){};var Tt=n(\"moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/\",function(){var e=Ot.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:m()}),At=n(\"moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/\",function(){var e=Ot.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:m()});function Et(e,t){var n,r;if(1===t.length&&a(t[0])&&(t=t[0]),!t.length)return Ot();for(n=t[0],r=1;r<t.length;++r)t[r].isValid()&&!t[r][e](n)||(n=t[r]);return n}var Pt=[\"year\",\"quarter\",\"month\",\"week\",\"day\",\"hour\",\"minute\",\"second\",\"millisecond\"];function Mt(e){var t=R(e),n=t.year||0,r=t.quarter||0,i=t.month||0,o=t.week||t.isoWeek||0,a=t.day||0,s=t.hour||0,u=t.minute||0,l=t.second||0,c=t.millisecond||0;this._isValid=function(e){for(var t in e)if(-1===ke.call(Pt,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,r=0;r<Pt.length;++r)if(e[Pt[r]]){if(n)return!1;parseFloat(e[Pt[r]])!==_(e[Pt[r]])&&(n=!0)}return!0}(t),this._milliseconds=+c+1e3*l+6e4*u+1e3*s*60*60,this._days=+a+7*o,this._months=+i+3*r+12*n,this._data={},this._locale=ct(),this._bubble()}function Rt(e){return e instanceof Mt}function It(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Vt(e,n){Y(e,0,0,function(){var e=this.utcOffset(),t=\"+\";return e<0&&(e=-e,t=\"-\"),t+L(~~(e/60),2)+n+L(~~e%60,2)})}Vt(\"Z\",\":\"),Vt(\"ZZ\",\"\"),ue(\"Z\",oe),ue(\"ZZ\",oe),pe([\"Z\",\"ZZ\"],function(e,t,n){n._useUTC=!0,n._tzm=Ft(oe,e)});var Lt=/([\\+\\-]|\\d\\d)/gi;function Ft(e,t){var n=(t||\"\").match(e);if(null===n)return null;var r=((n[n.length-1]||[])+\"\").match(Lt)||[\"-\",0,0],i=60*r[1]+_(r[2]);return 0===i?0:\"+\"===r[0]?i:-i}function jt(e,t){var n,r;return t._isUTC?(n=t.clone(),r=(b(e)||l(e)?e.valueOf():Ot(e).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),p.updateOffset(n,!1),n):Ot(e).local()}function Ht(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Nt(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}p.updateOffset=function(){};var Yt=/^(\\-|\\+)?(?:(\\d*)[. ])?(\\d+)\\:(\\d+)(?:\\:(\\d+)(\\.\\d*)?)?$/,Ut=/^(-|\\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function qt(e,t){var n,r,i,o=e,a=null;return Rt(e)?o={ms:e._milliseconds,d:e._days,M:e._months}:u(e)?(o={},t?o[t]=e:o.milliseconds=e):(a=Yt.exec(e))?(n=\"-\"===a[1]?-1:1,o={y:0,d:_(a[me])*n,h:_(a[ve])*n,m:_(a[ye])*n,s:_(a[we])*n,ms:_(It(1e3*a[be]))*n}):(a=Ut.exec(e))?(n=\"-\"===a[1]?-1:1,o={y:zt(a[2],n),M:zt(a[3],n),w:zt(a[4],n),d:zt(a[5],n),h:zt(a[6],n),m:zt(a[7],n),s:zt(a[8],n)}):null==o?o={}:\"object\"==typeof o&&(\"from\"in o||\"to\"in o)&&(i=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=jt(t,e),e.isBefore(t)?n=Bt(e,t):((n=Bt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(Ot(o.from),Ot(o.to)),(o={}).ms=i.milliseconds,o.M=i.months),r=new Mt(o),Rt(e)&&h(e,\"_locale\")&&(r._locale=e._locale),r}function zt(e,t){var n=e&&parseFloat(e.replace(\",\",\".\"));return(isNaN(n)?0:n)*t}function Bt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,\"M\").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,\"M\"),n}function Wt(r,i){return function(e,t){var n;return null===t||isNaN(+t)||(x(i,\"moment().\"+i+\"(period, number) is deprecated. Please use moment().\"+i+\"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.\"),n=e,e=t,t=n),Gt(this,qt(e=\"string\"==typeof e?+e:e,t),r),this}}function Gt(e,t,n,r){var i=t._milliseconds,o=It(t._days),a=It(t._months);e.isValid()&&(r=null==r||r,a&&Re(e,Oe(e,\"Month\")+a*n),o&&Te(e,\"Date\",Oe(e,\"Date\")+o*n),i&&e._d.setTime(e._d.valueOf()+i*n),r&&p.updateOffset(e,o||a))}qt.fn=Mt.prototype,qt.invalid=function(){return qt(NaN)};var Kt=Wt(1,\"add\"),Qt=Wt(-1,\"subtract\");function Zt(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),r=e.clone().add(n,\"months\");return-(n+(t-r<0?(t-r)/(r-e.clone().add(n-1,\"months\")):(t-r)/(e.clone().add(1+n,\"months\")-r)))||0}function Xt(e){var t;return void 0===e?this._locale._abbr:(null!=(t=ct(e))&&(this._locale=t),this)}p.defaultFormat=\"YYYY-MM-DDTHH:mm:ssZ\",p.defaultFormatUtc=\"YYYY-MM-DDTHH:mm:ss[Z]\";var Jt=n(\"moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.\",function(e){return void 0===e?this.localeData():this.locale(e)});function en(){return this._locale}var tn=126227808e5;function nn(e,t){return(e%t+t)%t}function rn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-tn:new Date(e,t,n).valueOf()}function on(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-tn:Date.UTC(e,t,n)}function an(e,t){Y(0,[e,e.length],0,t)}function sn(e,t,n,r,i){var o;return null==e?Ye(this,r,i).year:((o=Ue(e,r,i))<t&&(t=o),function(e,t,n,r,i){var o=Ne(e,t,n,r,i),a=je(o.year,0,o.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}.call(this,e,t,n,r,i))}Y(0,[\"gg\",2],0,function(){return this.weekYear()%100}),Y(0,[\"GG\",2],0,function(){return this.isoWeekYear()%100}),an(\"gggg\",\"weekYear\"),an(\"ggggg\",\"weekYear\"),an(\"GGGG\",\"isoWeekYear\"),an(\"GGGGG\",\"isoWeekYear\"),P(\"weekYear\",\"gg\"),P(\"isoWeekYear\",\"GG\"),V(\"weekYear\",1),V(\"isoWeekYear\",1),ue(\"G\",re),ue(\"g\",re),ue(\"GG\",Q,B),ue(\"gg\",Q,B),ue(\"GGGG\",ee,G),ue(\"gggg\",ee,G),ue(\"GGGGG\",te,K),ue(\"ggggg\",te,K),he([\"gggg\",\"ggggg\",\"GGGG\",\"GGGGG\"],function(e,t,n,r){t[r.substr(0,2)]=_(e)}),he([\"gg\",\"GG\"],function(e,t,n,r){t[r]=p.parseTwoDigitYear(e)}),Y(\"Q\",0,\"Qo\",\"quarter\"),P(\"quarter\",\"Q\"),V(\"quarter\",7),ue(\"Q\",z),pe(\"Q\",function(e,t){t[ge]=3*(_(e)-1)}),Y(\"D\",[\"DD\",2],\"Do\",\"date\"),P(\"date\",\"D\"),V(\"date\",9),ue(\"D\",Q),ue(\"DD\",Q,B),ue(\"Do\",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),pe([\"D\",\"DD\"],me),pe(\"Do\",function(e,t){t[me]=_(e.match(Q)[0])});var un=xe(\"Date\",!0);Y(\"DDD\",[\"DDDD\",3],\"DDDo\",\"dayOfYear\"),P(\"dayOfYear\",\"DDD\"),V(\"dayOfYear\",4),ue(\"DDD\",J),ue(\"DDDD\",W),pe([\"DDD\",\"DDDD\"],function(e,t,n){n._dayOfYear=_(e)}),Y(\"m\",[\"mm\",2],0,\"minute\"),P(\"minute\",\"m\"),V(\"minute\",14),ue(\"m\",Q),ue(\"mm\",Q,B),pe([\"m\",\"mm\"],ye);var ln=xe(\"Minutes\",!1);Y(\"s\",[\"ss\",2],0,\"second\"),P(\"second\",\"s\"),V(\"second\",15),ue(\"s\",Q),ue(\"ss\",Q,B),pe([\"s\",\"ss\"],we);var cn,dn=xe(\"Seconds\",!1);for(Y(\"S\",0,0,function(){return~~(this.millisecond()/100)}),Y(0,[\"SS\",2],0,function(){return~~(this.millisecond()/10)}),Y(0,[\"SSS\",3],0,\"millisecond\"),Y(0,[\"SSSS\",4],0,function(){return 10*this.millisecond()}),Y(0,[\"SSSSS\",5],0,function(){return 100*this.millisecond()}),Y(0,[\"SSSSSS\",6],0,function(){return 1e3*this.millisecond()}),Y(0,[\"SSSSSSS\",7],0,function(){return 1e4*this.millisecond()}),Y(0,[\"SSSSSSSS\",8],0,function(){return 1e5*this.millisecond()}),Y(0,[\"SSSSSSSSS\",9],0,function(){return 1e6*this.millisecond()}),P(\"millisecond\",\"ms\"),V(\"millisecond\",16),ue(\"S\",J,z),ue(\"SS\",J,B),ue(\"SSS\",J,W),cn=\"SSSS\";cn.length<=9;cn+=\"S\")ue(cn,ne);function pn(e,t){t[be]=_(1e3*(\"0.\"+e))}for(cn=\"S\";cn.length<=9;cn+=\"S\")pe(cn,pn);var hn=xe(\"Milliseconds\",!1);Y(\"z\",0,0,\"zoneAbbr\"),Y(\"zz\",0,0,\"zoneName\");var fn=w.prototype;function gn(e){return e}fn.add=Kt,fn.calendar=function(e,t){var n=e||Ot(),r=jt(n,this).startOf(\"day\"),i=p.calendarFormat(this,r)||\"sameElse\",o=t&&(O(t[i])?t[i].call(this,n):t[i]);return this.format(o||this.localeData().calendar(i,this,Ot(n)))},fn.clone=function(){return new w(this)},fn.diff=function(e,t,n){var r,i,o;if(!this.isValid())return NaN;if(!(r=jt(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=M(t)){case\"year\":o=Zt(this,r)/12;break;case\"month\":o=Zt(this,r);break;case\"quarter\":o=Zt(this,r)/3;break;case\"second\":o=(this-r)/1e3;break;case\"minute\":o=(this-r)/6e4;break;case\"hour\":o=(this-r)/36e5;break;case\"day\":o=(this-r-i)/864e5;break;case\"week\":o=(this-r-i)/6048e5;break;default:o=this-r}return n?o:$(o)},fn.endOf=function(e){var t;if(void 0===(e=M(e))||\"millisecond\"===e||!this.isValid())return this;var n=this._isUTC?on:rn;switch(e){case\"year\":t=n(this.year()+1,0,1)-1;break;case\"quarter\":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case\"month\":t=n(this.year(),this.month()+1,1)-1;break;case\"week\":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case\"isoWeek\":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case\"day\":case\"date\":t=n(this.year(),this.month(),this.date()+1)-1;break;case\"hour\":t=this._d.valueOf(),t+=36e5-nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case\"minute\":t=this._d.valueOf(),t+=6e4-nn(t,6e4)-1;break;case\"second\":t=this._d.valueOf(),t+=1e3-nn(t,1e3)-1}return this._d.setTime(t),p.updateOffset(this,!0),this},fn.format=function(e){e||(e=this.isUtc()?p.defaultFormatUtc:p.defaultFormat);var t=U(this,e);return this.localeData().postformat(t)},fn.from=function(e,t){return this.isValid()&&(b(e)&&e.isValid()||Ot(e).isValid())?qt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},fn.fromNow=function(e){return this.from(Ot(),e)},fn.to=function(e,t){return this.isValid()&&(b(e)&&e.isValid()||Ot(e).isValid())?qt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},fn.toNow=function(e){return this.to(Ot(),e)},fn.get=function(e){return O(this[e=M(e)])?this[e]():this},fn.invalidAt=function(){return f(this).overflow},fn.isAfter=function(e,t){var n=b(e)?e:Ot(e);return!(!this.isValid()||!n.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},fn.isBefore=function(e,t){var n=b(e)?e:Ot(e);return!(!this.isValid()||!n.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},fn.isBetween=function(e,t,n,r){var i=b(e)?e:Ot(e),o=b(t)?t:Ot(t);return!!(this.isValid()&&i.isValid()&&o.isValid())&&(\"(\"===(r=r||\"()\")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(\")\"===r[1]?this.isBefore(o,n):!this.isAfter(o,n))},fn.isSame=function(e,t){var n,r=b(e)?e:Ot(e);return!(!this.isValid()||!r.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},fn.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},fn.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},fn.isValid=function(){return g(this)},fn.lang=Jt,fn.locale=Xt,fn.localeData=en,fn.max=At,fn.min=Tt,fn.parsingFlags=function(){return c({},f(this))},fn.set=function(e,t){if(\"object\"==typeof e)for(var n=function(e){var t=[];for(var n in e)t.push({unit:n,priority:I[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}(e=R(e)),r=0;r<n.length;r++)this[n[r].unit](e[n[r].unit]);else if(O(this[e=M(e)]))return this[e](t);return this},fn.startOf=function(e){var t;if(void 0===(e=M(e))||\"millisecond\"===e||!this.isValid())return this;var n=this._isUTC?on:rn;switch(e){case\"year\":t=n(this.year(),0,1);break;case\"quarter\":t=n(this.year(),this.month()-this.month()%3,1);break;case\"month\":t=n(this.year(),this.month(),1);break;case\"week\":t=n(this.year(),this.month(),this.date()-this.weekday());break;case\"isoWeek\":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case\"day\":case\"date\":t=n(this.year(),this.month(),this.date());break;case\"hour\":t=this._d.valueOf(),t-=nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case\"minute\":t=this._d.valueOf(),t-=nn(t,6e4);break;case\"second\":t=this._d.valueOf(),t-=nn(t,1e3)}return this._d.setTime(t),p.updateOffset(this,!0),this},fn.subtract=Qt,fn.toArray=function(){return[this.year(),this.month(),this.date(),this.hour(),this.minute(),this.second(),this.millisecond()]},fn.toObject=function(){return{years:this.year(),months:this.month(),date:this.date(),hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()}},fn.toDate=function(){return new Date(this.valueOf())},fn.toISOString=function(e){if(!this.isValid())return null;var t=!0!==e,n=t?this.clone().utc():this;return n.year()<0||9999<n.year()?U(n,t?\"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ\"):O(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace(\"Z\",U(n,\"Z\")):U(n,t?\"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYY-MM-DD[T]HH:mm:ss.SSSZ\")},fn.inspect=function(){if(!this.isValid())return\"moment.invalid(/* \"+this._i+\" */)\";var e=\"moment\",t=\"\";this.isLocal()||(e=0===this.utcOffset()?\"moment.utc\":\"moment.parseZone\",t=\"Z\");var n=\"[\"+e+'(\"]',r=0<=this.year()&&this.year()<=9999?\"YYYY\":\"YYYYYY\",i=t+'[\")]';return this.format(n+r+\"-MM-DD[T]HH:mm:ss.SSS\"+i)},fn.toJSON=function(){return this.isValid()?this.toISOString():null},fn.toString=function(){return this.clone().locale(\"en\").format(\"ddd MMM DD YYYY HH:mm:ss [GMT]ZZ\")},fn.unix=function(){return Math.floor(this.valueOf()/1e3)},fn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},fn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},fn.year=De,fn.isLeapYear=function(){return Ce(this.year())},fn.weekYear=function(e){return sn.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},fn.isoWeekYear=function(e){return sn.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},fn.quarter=fn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},fn.month=Ie,fn.daysInMonth=function(){return Ae(this.year(),this.month())},fn.week=fn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),\"d\")},fn.isoWeek=fn.isoWeeks=function(e){var t=Ye(this,1,4).week;return null==e?t:this.add(7*(e-t),\"d\")},fn.weeksInYear=function(){var e=this.localeData()._week;return Ue(this.year(),e.dow,e.doy)},fn.isoWeeksInYear=function(){return Ue(this.year(),1,4)},fn.date=un,fn.day=fn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=function(e,t){return\"string\"!=typeof e?e:isNaN(e)?\"number\"==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}(e,this.localeData()),this.add(e-t,\"d\")):t},fn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,\"d\")},fn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null==e)return this.day()||7;var t=function(e,t){return\"string\"==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}(e,this.localeData());return this.day(this.day()%7?t:t-7)},fn.dayOfYear=function(e){var t=Math.round((this.clone().startOf(\"day\")-this.clone().startOf(\"year\"))/864e5)+1;return null==e?t:this.add(e-t,\"d\")},fn.hour=fn.hours=nt,fn.minute=fn.minutes=ln,fn.second=fn.seconds=dn,fn.millisecond=fn.milliseconds=hn,fn.utcOffset=function(e,t,n){var r,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Ht(this);if(\"string\"==typeof e){if(null===(e=Ft(oe,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(r=Ht(this)),this._offset=e,this._isUTC=!0,null!=r&&this.add(r,\"m\"),i!==e&&(!t||this._changeInProgress?Gt(this,qt(e-i,\"m\"),1,!1):this._changeInProgress||(this._changeInProgress=!0,p.updateOffset(this,!0),this._changeInProgress=null)),this},fn.utc=function(e){return this.utcOffset(0,e)},fn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Ht(this),\"m\")),this},fn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if(\"string\"==typeof this._i){var e=Ft(ie,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},fn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?Ot(e).utcOffset():0,(this.utcOffset()-e)%60==0)},fn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},fn.isLocal=function(){return!!this.isValid()&&!this._isUTC},fn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},fn.isUtc=Nt,fn.isUTC=Nt,fn.zoneAbbr=function(){return this._isUTC?\"UTC\":\"\"},fn.zoneName=function(){return this._isUTC?\"Coordinated Universal Time\":\"\"},fn.dates=n(\"dates accessor is deprecated. Use date instead.\",un),fn.months=n(\"months accessor is deprecated. Use month instead\",Ie),fn.years=n(\"years accessor is deprecated. Use year instead\",De),fn.zone=n(\"moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/\",function(e,t){return null!=e?(\"string\"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),fn.isDSTShifted=n(\"isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information\",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?d(e._a):Ot(e._a);this._isDSTShifted=this.isValid()&&0<S(e._a,t.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted});var mn=A.prototype;function vn(e,t,n,r){var i=ct(),o=d().set(r,t);return i[n](o,e)}function yn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||\"\",null!=t)return vn(e,t,n,\"month\");var r,i=[];for(r=0;r<12;r++)i[r]=vn(e,r,n,\"month\");return i}function wn(e,t,n,r){t=(\"boolean\"==typeof e?u(t)&&(n=t,t=void 0):(t=e,e=!1,u(n=t)&&(n=t,t=void 0)),t||\"\");var i,o=ct(),a=e?o._week.dow:0;if(null!=n)return vn(t,(n+a)%7,r,\"day\");var s=[];for(i=0;i<7;i++)s[i]=vn(t,(i+a)%7,r,\"day\");return s}mn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return O(r)?r.call(t,n):r},mn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},mn.invalidDate=function(){return this._invalidDate},mn.ordinal=function(e){return this._ordinal.replace(\"%d\",e)},mn.preparse=gn,mn.postformat=gn,mn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return O(i)?i(e,t,n,r):i.replace(/%d/i,e)},mn.pastFuture=function(e,t){var n=this._relativeTime[0<e?\"future\":\"past\"];return O(n)?n(t):n.replace(/%s/i,t)},mn.set=function(e){var t,n;for(n in e)O(t=e[n])?this[n]=t:this[\"_\"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+\"|\"+/\\d{1,2}/.source)},mn.months=function(e,t){return e?a(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Ee).test(t)?\"format\":\"standalone\"][e.month()]:a(this._months)?this._months:this._months.standalone},mn.monthsShort=function(e,t){return e?a(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Ee.test(t)?\"format\":\"standalone\"][e.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},mn.monthsParse=function(e,t,n){var r,i,o;if(this._monthsParseExact)return function(e,t,n){var r,i,o,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)o=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(o,\"\").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(o,\"\").toLocaleLowerCase();return n?\"MMM\"===t?-1!==(i=ke.call(this._shortMonthsParse,a))?i:null:-1!==(i=ke.call(this._longMonthsParse,a))?i:null:\"MMM\"===t?-1!==(i=ke.call(this._shortMonthsParse,a))?i:-1!==(i=ke.call(this._longMonthsParse,a))?i:null:-1!==(i=ke.call(this._longMonthsParse,a))?i:-1!==(i=ke.call(this._shortMonthsParse,a))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp(\"^\"+this.months(i,\"\").replace(\".\",\"\")+\"$\",\"i\"),this._shortMonthsParse[r]=new RegExp(\"^\"+this.monthsShort(i,\"\").replace(\".\",\"\")+\"$\",\"i\")),n||this._monthsParse[r]||(o=\"^\"+this.months(i,\"\")+\"|^\"+this.monthsShort(i,\"\"),this._monthsParse[r]=new RegExp(o.replace(\".\",\"\"),\"i\")),n&&\"MMMM\"===t&&this._longMonthsParse[r].test(e))return r;if(n&&\"MMM\"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},mn.monthsRegex=function(e){return this._monthsParseExact?(h(this,\"_monthsRegex\")||Fe.call(this),e?this._monthsStrictRegex:this._monthsRegex):(h(this,\"_monthsRegex\")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},mn.monthsShortRegex=function(e){return this._monthsParseExact?(h(this,\"_monthsRegex\")||Fe.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,\"_monthsShortRegex\")||(this._monthsShortRegex=Ve),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},mn.week=function(e){return Ye(e,this._week.dow,this._week.doy).week},mn.firstDayOfYear=function(){return this._week.doy},mn.firstDayOfWeek=function(){return this._week.dow},mn.weekdays=function(e,t){var n=a(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?\"format\":\"standalone\"];return!0===e?qe(n,this._week.dow):e?n[e.day()]:n},mn.weekdaysMin=function(e){return!0===e?qe(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},mn.weekdaysShort=function(e){return!0===e?qe(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},mn.weekdaysParse=function(e,t,n){var r,i,o;if(this._weekdaysParseExact)return function(e,t,n){var r,i,o,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)o=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(o,\"\").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(o,\"\").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(o,\"\").toLocaleLowerCase();return n?\"dddd\"===t?-1!==(i=ke.call(this._weekdaysParse,a))?i:null:\"ddd\"===t?-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:\"dddd\"===t?-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:\"ddd\"===t?-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp(\"^\"+this.weekdays(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._shortWeekdaysParse[r]=new RegExp(\"^\"+this.weekdaysShort(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._minWeekdaysParse[r]=new RegExp(\"^\"+this.weekdaysMin(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\")),this._weekdaysParse[r]||(o=\"^\"+this.weekdays(i,\"\")+\"|^\"+this.weekdaysShort(i,\"\")+\"|^\"+this.weekdaysMin(i,\"\"),this._weekdaysParse[r]=new RegExp(o.replace(\".\",\"\"),\"i\")),n&&\"dddd\"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&\"ddd\"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&\"dd\"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},mn.weekdaysRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,\"_weekdaysRegex\")||(this._weekdaysRegex=Ge),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},mn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,\"_weekdaysShortRegex\")||(this._weekdaysShortRegex=Ke),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},mn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,\"_weekdaysMinRegex\")||(this._weekdaysMinRegex=Qe),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},mn.isPM=function(e){return\"p\"===(e+\"\").toLowerCase().charAt(0)},mn.meridiem=function(e,t,n){return 11<e?n?\"pm\":\"PM\":n?\"am\":\"AM\"},ut(\"en\",{dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===_(e%100/10)?\"th\":1==t?\"st\":2==t?\"nd\":3==t?\"rd\":\"th\")}}),p.lang=n(\"moment.lang is deprecated. Use moment.locale instead.\",ut),p.langData=n(\"moment.langData is deprecated. Use moment.localeData instead.\",ct);var bn=Math.abs;function $n(e,t,n,r){var i=qt(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function _n(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Cn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}var Dn=kn(\"ms\"),xn=kn(\"s\"),On=kn(\"m\"),Tn=kn(\"h\"),An=kn(\"d\"),En=kn(\"w\"),Pn=kn(\"M\"),Mn=kn(\"Q\"),Rn=kn(\"y\");function In(e){return function(){return this.isValid()?this._data[e]:NaN}}var Vn=In(\"milliseconds\"),Ln=In(\"seconds\"),Fn=In(\"minutes\"),jn=In(\"hours\"),Hn=In(\"days\"),Nn=In(\"months\"),Yn=In(\"years\");var Un=Math.round,qn={ss:44,s:45,m:45,h:22,d:26,M:11};var zn=Math.abs;function Bn(e){return(0<e)-(e<0)||+e}function Wn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=zn(this._milliseconds)/1e3,r=zn(this._days),i=zn(this._months);t=$((e=$(n/60))/60),n%=60,e%=60;var o=$(i/12),a=i%=12,s=r,u=t,l=e,c=n?n.toFixed(3).replace(/\\.?0+$/,\"\"):\"\",d=this.asSeconds();if(!d)return\"P0D\";var p=d<0?\"-\":\"\",h=Bn(this._months)!==Bn(d)?\"-\":\"\",f=Bn(this._days)!==Bn(d)?\"-\":\"\",g=Bn(this._milliseconds)!==Bn(d)?\"-\":\"\";return p+\"P\"+(o?h+o+\"Y\":\"\")+(a?h+a+\"M\":\"\")+(s?f+s+\"D\":\"\")+(u||l||c?\"T\":\"\")+(u?g+u+\"H\":\"\")+(l?g+l+\"M\":\"\")+(c?g+c+\"S\":\"\")}var Gn=Mt.prototype;return Gn.isValid=function(){return this._isValid},Gn.abs=function(){var e=this._data;return this._milliseconds=bn(this._milliseconds),this._days=bn(this._days),this._months=bn(this._months),e.milliseconds=bn(e.milliseconds),e.seconds=bn(e.seconds),e.minutes=bn(e.minutes),e.hours=bn(e.hours),e.months=bn(e.months),e.years=bn(e.years),this},Gn.add=function(e,t){return $n(this,e,t,1)},Gn.subtract=function(e,t){return $n(this,e,t,-1)},Gn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if(\"month\"===(e=M(e))||\"quarter\"===e||\"year\"===e)switch(t=this._days+r/864e5,n=this._months+Sn(t),e){case\"month\":return n;case\"quarter\":return n/3;case\"year\":return n/12}else switch(t=this._days+Math.round(Cn(this._months)),e){case\"week\":return t/7+r/6048e5;case\"day\":return t+r/864e5;case\"hour\":return 24*t+r/36e5;case\"minute\":return 1440*t+r/6e4;case\"second\":return 86400*t+r/1e3;case\"millisecond\":return Math.floor(864e5*t)+r;default:throw new Error(\"Unknown unit \"+e)}},Gn.asMilliseconds=Dn,Gn.asSeconds=xn,Gn.asMinutes=On,Gn.asHours=Tn,Gn.asDays=An,Gn.asWeeks=En,Gn.asMonths=Pn,Gn.asQuarters=Mn,Gn.asYears=Rn,Gn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*_(this._months/12):NaN},Gn._bubble=function(){var e,t,n,r,i,o=this._milliseconds,a=this._days,s=this._months,u=this._data;return 0<=o&&0<=a&&0<=s||o<=0&&a<=0&&s<=0||(o+=864e5*_n(Cn(s)+a),s=a=0),u.milliseconds=o%1e3,e=$(o/1e3),u.seconds=e%60,t=$(e/60),u.minutes=t%60,n=$(t/60),u.hours=n%24,s+=i=$(Sn(a+=$(n/24))),a-=_n(Cn(i)),r=$(s/12),s%=12,u.days=a,u.months=s,u.years=r,this},Gn.clone=function(){return qt(this)},Gn.get=function(e){return e=M(e),this.isValid()?this[e+\"s\"]():NaN},Gn.milliseconds=Vn,Gn.seconds=Ln,Gn.minutes=Fn,Gn.hours=jn,Gn.days=Hn,Gn.weeks=function(){return $(this.days()/7)},Gn.months=Nn,Gn.years=Yn,Gn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t=this.localeData(),n=function(e,t,n){var r=qt(e).abs(),i=Un(r.as(\"s\")),o=Un(r.as(\"m\")),a=Un(r.as(\"h\")),s=Un(r.as(\"d\")),u=Un(r.as(\"M\")),l=Un(r.as(\"y\")),c=i<=qn.ss&&[\"s\",i]||i<qn.s&&[\"ss\",i]||o<=1&&[\"m\"]||o<qn.m&&[\"mm\",o]||a<=1&&[\"h\"]||a<qn.h&&[\"hh\",a]||s<=1&&[\"d\"]||s<qn.d&&[\"dd\",s]||u<=1&&[\"M\"]||u<qn.M&&[\"MM\",u]||l<=1&&[\"y\"]||[\"yy\",l];return c[2]=t,c[3]=0<+e,c[4]=n,function(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}.apply(null,c)}(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)},Gn.toISOString=Wn,Gn.toString=Wn,Gn.toJSON=Wn,Gn.locale=Xt,Gn.localeData=en,Gn.toIsoString=n(\"toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)\",Wn),Gn.lang=Jt,Y(\"X\",0,0,\"unix\"),Y(\"x\",0,0,\"valueOf\"),ue(\"x\",re),ue(\"X\",/[+-]?\\d+(\\.\\d{1,3})?/),pe(\"X\",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),pe(\"x\",function(e,t,n){n._d=new Date(_(e))}),p.version=\"2.24.0\",e=Ot,p.fn=fn,p.min=function(){return Et(\"isBefore\",[].slice.call(arguments,0))},p.max=function(){return Et(\"isAfter\",[].slice.call(arguments,0))},p.now=function(){return Date.now?Date.now():+new Date},p.utc=d,p.unix=function(e){return Ot(1e3*e)},p.months=function(e,t){return yn(e,t,\"months\")},p.isDate=l,p.locale=ut,p.invalid=m,p.duration=qt,p.isMoment=b,p.weekdays=function(e,t,n){return wn(e,t,n,\"weekdays\")},p.parseZone=function(){return Ot.apply(null,arguments).parseZone()},p.localeData=ct,p.isDuration=Rt,p.monthsShort=function(e,t){return yn(e,t,\"monthsShort\")},p.weekdaysMin=function(e,t,n){return wn(e,t,n,\"weekdaysMin\")},p.defineLocale=lt,p.updateLocale=function(e,t){if(null!=t){var n,r,i=rt;null!=(r=st(e))&&(i=r._config),(n=new A(t=T(i,t))).parentLocale=it[e],it[e]=n,ut(e)}else null!=it[e]&&(null!=it[e].parentLocale?it[e]=it[e].parentLocale:null!=it[e]&&delete it[e]);return it[e]},p.locales=function(){return k(it)},p.weekdaysShort=function(e,t,n){return wn(e,t,n,\"weekdaysShort\")},p.normalizeUnits=M,p.relativeTimeRounding=function(e){return void 0===e?Un:\"function\"==typeof e&&(Un=e,!0)},p.relativeTimeThreshold=function(e,t){return void 0!==qn[e]&&(void 0===t?qn[e]:(qn[e]=t,\"s\"===e&&(qn.ss=t-1),!0))},p.calendarFormat=function(e,t){var n=e.diff(t,\"days\",!0);return n<-6?\"sameElse\":n<-1?\"lastWeek\":n<0?\"lastDay\":n<1?\"sameDay\":n<2?\"nextDay\":n<7?\"nextWeek\":\"sameElse\"},p.prototype=fn,p.HTML5_FMT={DATETIME_LOCAL:\"YYYY-MM-DDTHH:mm\",DATETIME_LOCAL_SECONDS:\"YYYY-MM-DDTHH:mm:ss\",DATETIME_LOCAL_MS:\"YYYY-MM-DDTHH:mm:ss.SSS\",DATE:\"YYYY-MM-DD\",TIME:\"HH:mm\",TIME_SECONDS:\"HH:mm:ss\",TIME_MS:\"HH:mm:ss.SSS\",WEEK:\"GGGG-[W]WW\",MONTH:\"YYYY-MM\"},p}),function(e,t){\"use strict\";\"undefined\"!=typeof module&&module.exports?(t(\"undefined\"==typeof angular?require(\"angular\"):angular,\"undefined\"==typeof moment?require(\"moment\"):moment),module.exports=\"ui.bootstrap.datetimepicker\"):\"function\"==typeof define&&define.amd?define([\"angular\",\"moment\"],t):t(e.angular,e.moment)}(this,function(d,p){\"use strict\";function e(){return function(e,t,n){var r,i;switch(e){case\"Date\":r=o(function(e){return a(e).toDate()});break;case\"moment\":r=o(a);break;case\"milliseconds\":r=o(function(e){return p.utc(e,t,p.locale(),n).valueOf()});break;default:r=o((i=e,function(e){return a(e).format(i)}))}return r;function o(t){return function(e){return d.isUndefined(e)||\"\"===e||null===e?null:t(e)}}function a(e){return p(e,t,p.locale(),n)}}}function t(c){return{require:\"ngModel\",restrict:\"A\",scope:{dateFormats:\"=\"},link:function(e,t,n,r){if(d.isDefined(e.dateFormats)&&!d.isString(e.dateFormats)&&!d.isArray(e.dateFormats))throw new Error(\"date-formats must be a single string or an array of strings i.e. date-formats=\\\"['YYYY-MM-DD']\\\" \");if(d.isDefined(n.modelType)&&(!d.isString(n.modelType)||0===n.modelType.length))throw new Error('model-type must be \"Date\", \"moment\", \"milliseconds\", or a moment format string');var i=n.dateTimeInput||p.defaultFormat,o=void 0===n.dateParseStrict||\"true\"===n.dateParseStrict,a=n.modelType||\"Date\",s=[n.dateTimeInput,a].concat(e.dateFormats).concat([p.ISO_8601]).filter(l),u=[a].concat(s).filter(l);function l(e,t,n){return-1===[\"Date\",\"moment\",\"milliseconds\",void 0].indexOf(e)&&n.indexOf(e)===t}r.$parsers.unshift(c(a,s,o)),r.$formatters.push(function(e){if(d.isUndefined(e)||\"\"===e||null===e)return null;{if(d.isDate(e))return p(e).format(i);if(d.isNumber(e))return p.utc(e).format(i)}return p(e,u,p.locale(),o).format(i)}),r.$validators.dateTimeInput=function(e,t){if(d.isUndefined(t)||\"\"===t||null===t)return!0;return p(t,s,p.locale(),o).isValid()},t.bind(\"blur\",function(){function e(){return!0}function t(e,t){return t(e)}r.$viewValue=r.$formatters.filter(e).reverse().reduce(t,r.$modelValue),r.$render()})}}}d.module(\"ui.dateTimeInput\",[]).service(\"dateTimeParserFactory\",e).directive(\"dateTimeInput\",t),e.$inject=[],t.$inject=[\"dateTimeParserFactory\"]}),function(e,t){\"use strict\";\"undefined\"!=typeof module&&module.exports?(t(\"undefined\"==typeof angular?require(\"angular\"):angular,\"undefined\"==typeof moment?require(\"moment\"):moment),module.exports=\"ui.bootstrap.datetimepicker\"):\"function\"==typeof define&&define.amd?define([\"angular\",\"moment\"],t):t(e.angular,e.moment)}(this,function(a,b){\"use strict\";function e(i,o){var e={bindToController:!1,controller:t,controllerAs:\"dateTimePickerController\",replace:!0,require:\"ngModel\",restrict:\"E\",scope:{beforeRender:\"&\",onSetTime:\"&\"},templateUrl:\"templates/datetimepicker.html\"};function t(l,e,n){var m=e.controller(\"ngModel\"),v=t();l.screenReader=v.screenReader,l.changeView=function(e,t,n){n&&(n.stopPropagation(),n.preventDefault());if(e&&t.utcDateValue>-1/0&&t.selectable&&c[e]){var r=c[e](t.utcDateValue),i=[];if(r.weeks)for(var o=0;o<r.weeks.length;o+=1)for(var a=r.weeks[o],s=0;s<a.dates.length;s+=1){var u=a.dates[s];i.push(u)}l.beforeRender({$view:r.currentView,$dates:r.dates||i,$leftDate:r.leftDate,$upDate:r.previousViewDate,$rightDate:r.rightDate}),l.data=r}},m.$render=function(){l.changeView(v.startView,new w({utcDateValue:function(e){var t=new Date;if(e){var n=r(e);if(!n.isValid())throw new Error(\"Invalid date: \"+e);t=n.toDate()}return t.getTime()-6e4*t.getTimezoneOffset()}(m.$viewValue)}))},v.configureOn&&l.$on(v.configureOn,function(){v=t(),l.screenReader=v.screenReader,m.$render()}),v.renderOn&&l.$on(v.renderOn,m.$render);var c={year:function(e){for(var t=b.utc(e).startOf(\"year\"),n=10*parseInt(t.year()/10,10),r=b.utc(d(e)).subtract(1,\"year\").startOf(\"year\"),i=\"YYYY\",o=y(m.$modelValue,i),a=b().format(i),s={currentView:\"year\",nextView:\"year\"===v.minView?\"setTime\":\"month\",previousViewDate:new w({utcDateValue:null,display:n+\"-\"+(9+n)}),leftDate:new w({utcDateValue:b.utc(r).subtract(9,\"year\").valueOf()}),rightDate:new w({utcDateValue:b.utc(r).add(11,\"year\").valueOf()}),dates:[]},u=0;u<12;u+=1){var l=b.utc(r).add(u,\"years\"),c={active:l.format(i)===o,current:l.format(i)===a,display:l.format(i),future:l.year()>9+n,past:l.year()<n,utcDateValue:l.valueOf()};s.dates.push(new w(c))}return s},month:function(e){for(var t=b.utc(e).startOf(\"year\"),n=d(e),r=\"YYYY-MMM\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"year\",currentView:\"month\",nextView:\"month\"===v.minView?\"setTime\":\"day\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"YYYY\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"year\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"year\").valueOf()}),dates:[]},s=0;s<12;s+=1){var u=b.utc(t).add(s,\"months\"),l={active:u.format(r)===i,current:u.format(r)===o,display:u.format(\"MMM\"),utcDateValue:u.valueOf()};a.dates.push(new w(l))}return a},day:function(e){for(var t=b.utc(e),n=b.utc(t).startOf(\"month\"),r=b.utc(t).startOf(\"year\"),i=b.utc(t).endOf(\"month\"),o=b.utc(n).subtract(Math.abs(n.weekday()),\"days\"),a=\"YYYY-MMM-DD\",s=y(m.$modelValue,a),u=b().format(a),l={previousView:\"month\",currentView:\"day\",nextView:\"day\"===v.minView?\"setTime\":\"hour\",previousViewDate:new w({utcDateValue:r.valueOf(),display:n.format(\"YYYY-MMM\")}),leftDate:new w({utcDateValue:b.utc(n).subtract(1,\"months\").valueOf()}),rightDate:new w({utcDateValue:b.utc(n).add(1,\"months\").valueOf()}),dayNames:[],weeks:[]},c=0;c<7;c+=1)l.dayNames.push(b.utc().weekday(c).format(\"dd\"));for(var d=0;d<6;d+=1){for(var p={dates:[]},h=0;h<7;h+=1){var f=b.utc(o).add(7*d+h,\"days\"),g={active:f.format(a)===s,current:f.format(a)===u,display:f.format(\"D\"),future:f.isAfter(i),past:f.isBefore(n),utcDateValue:f.valueOf()};p.dates.push(new w(g))}l.weeks.push(p)}return l},hour:function(e){for(var t=b.utc(e).startOf(\"day\"),n=b.utc(t).startOf(\"month\"),r=\"YYYY-MM-DD H\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"day\",currentView:\"hour\",nextView:\"hour\"===v.minView?\"setTime\":\"minute\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"ll\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"days\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"days\").valueOf()}),dates:[]},s=0;s<24;s+=1){var u=b.utc(t).add(s,\"hours\"),l={active:u.format(r)===i,current:u.format(r)===o,display:u.format(\"LT\"),utcDateValue:u.valueOf()};a.dates.push(new w(l))}return a},minute:function(e){for(var t=b.utc(e).startOf(\"hour\"),n=b.utc(t).startOf(\"day\"),r=\"YYYY-MM-DD H:mm\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"hour\",currentView:\"minute\",nextView:\"setTime\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"lll\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"hours\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"hours\").valueOf()}),dates:[]},s=60/v.minuteStep,u=0;u<s;u+=1){var l=b.utc(t).add(u*v.minuteStep,\"minute\"),c={active:l.format(r)===i,current:l.format(r)===o,display:l.format(\"LT\"),utcDateValue:l.valueOf()};a.dates.push(new w(c))}return a},setTime:function(e){var t=new Date(e),n=new Date(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds());switch(v.modelType){case\"Date\":break;case\"moment\":n=b([t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds()]);break;case\"milliseconds\":n=e;break;default:n=b([t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds()]).format(v.modelType)}var r=m.$modelValue;m.$setViewValue(n),v.dropdownSelector&&jQuery(v.dropdownSelector).dropdown(\"toggle\");return l.onSetTime({newDate:n,oldDate:r}),c[v.startView](e)}};function d(e){var t=10*parseInt(b.utc(e).year()/10,10);return b.utc(e).year(t).startOf(\"year\")}function y(e,t){return e?r(e).format(t):\"\"}function r(e){return b(e,a.isString(e)?v.parseFormat:void 0)}function t(){var e={};n.datetimepickerConfig&&(e=l.$parent.$eval(n.datetimepickerConfig));var t=a.extend({},i,e);return o.validate(t),t}}function w(){var e=new Date(arguments[0].utcDateValue),t=6e4*e.getTimezoneOffset();this.utcDateValue=e.getTime(),this.selectable=!0,this.localDateValue=function(){return this.utcDateValue+t};var n=[\"active\",\"current\",\"display\",\"future\",\"past\",\"selectable\",\"utcDateValue\"],r=arguments[0];Object.keys(r).filter(function(e){return 0<=n.indexOf(e)}).forEach(function(e){this[e]=r[e]},this)}return t.$inject=[\"$scope\",\"$element\",\"$attrs\"],e}function t(i){return{validate:function(e){var t=[\"configureOn\",\"dropdownSelector\",\"minuteStep\",\"minView\",\"modelType\",\"parseFormat\",\"renderOn\",\"startView\",\"screenReader\"],n=Object.keys(e).filter(function(e){return t.indexOf(e)<0});if(n.length)throw new Error(\"Invalid options: \"+n.join(\", \"));var r=[\"minute\",\"hour\",\"day\",\"month\",\"year\"];if(r.indexOf(e.startView)<0)throw new Error(\"invalid startView value: \"+e.startView);if(r.indexOf(e.minView)<0)throw new Error(\"invalid minView value: \"+e.minView);if(r.indexOf(e.minView)>r.indexOf(e.startView))throw new Error(\"startView must be greater than minView\");if(!a.isNumber(e.minuteStep))throw new Error(\"minuteStep must be numeric\");if(e.minuteStep<=0||60<=e.minuteStep)throw new Error(\"minuteStep must be greater than zero and less than 60\");if(null!==e.configureOn&&!a.isString(e.configureOn))throw new Error(\"configureOn must be a string\");if(null!==e.configureOn&&e.configureOn.length<1)throw new Error(\"configureOn must not be an empty string\");if(null!==e.renderOn&&!a.isString(e.renderOn))throw new Error(\"renderOn must be a string\");if(null!==e.renderOn&&e.renderOn.length<1)throw new Error(\"renderOn must not be an empty string\");if(null!==e.modelType&&!a.isString(e.modelType))throw new Error(\"modelType must be a string\");if(null!==e.modelType&&e.modelType.length<1)throw new Error(\"modelType must not be an empty string\");\"Date\"!==e.modelType&&\"moment\"!==e.modelType&&\"milliseconds\"!==e.modelType&&(e.parseFormat=e.modelType);if(null!==e.dropdownSelector&&!a.isString(e.dropdownSelector))throw new Error(\"dropdownSelector must be a string\");null===e.dropdownSelector||\"undefined\"!=typeof jQuery&&\"function\"==typeof jQuery().dropdown||(i.error(\"Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \\n\\nThe dropdownSelector configuration option is being removed because it will not function properly.\"),delete e.dropdownSelector)}}}a.module(\"ui.bootstrap.datetimepicker\",[]).service(\"dateTimePickerConfig\",function(){var e={bg:{previous:\"предишна\",next:\"следваща\"},ca:{previous:\"anterior\",next:\"següent\"},da:{previous:\"forrige\",next:\"næste\"},de:{previous:\"vorige\",next:\"weiter\"},\"en-au\":{previous:\"previous\",next:\"next\"},\"en-gb\":{previous:\"previous\",next:\"next\"},en:{previous:\"previous\",next:\"next\"},\"es-us\":{previous:\"atrás\",next:\"siguiente\"},es:{previous:\"atrás\",next:\"siguiente\"},fi:{previous:\"edellinen\",next:\"seuraava\"},fr:{previous:\"précédent\",next:\"suivant\"},hu:{previous:\"előző\",next:\"következő\"},it:{previous:\"precedente\",next:\"successivo\"},ja:{previous:\"前へ\",next:\"次へ\"},ml:{previous:\"മുൻപുള്ളത്\",next:\"അടുത്തത്\"},nl:{previous:\"vorige\",next:\"volgende\"},pl:{previous:\"poprzednia\",next:\"następna\"},\"pt-br\":{previous:\"anteriores\",next:\"próximos\"},pt:{previous:\"anterior\",next:\"próximo\"},ro:{previous:\"anterior\",next:\"următor\"},ru:{previous:\"предыдущая\",next:\"следующая\"},sk:{previous:\"predošlá\",next:\"ďalšia\"},sv:{previous:\"föregående\",next:\"nästa\"},tr:{previous:\"önceki\",next:\"sonraki\"},uk:{previous:\"назад\",next:\"далі\"},\"zh-cn\":{previous:\"上一页\",next:\"下一页\"},\"zh-tw\":{previous:\"上一頁\",next:\"下一頁\"}}[b.locale().toLowerCase()];return a.extend({},{configureOn:null,dropdownSelector:null,minuteStep:5,minView:\"minute\",modelType:\"Date\",parseFormat:\"YYYY-MM-DDTHH:mm:ss.SSSZZ\",renderOn:null,startView:\"day\"},{screenReader:e})}).service(\"dateTimePickerValidator\",t).directive(\"datetimepicker\",e),e.$inject=[\"dateTimePickerConfig\",\"dateTimePickerValidator\"],t.$inject=[\"$log\"]}),angular.module(\"rzTable\",[]),angular.module(\"rzTable\").directive(\"rzTable\",[\"resizeStorage\",\"$injector\",\"$parse\",function(s,i,o){function e(e){}function r(n,r,i){return function(e,t){!0!==i.busy&&void 0!==t&&t!==e&&(a(n),u(n,r,i))}}function a(e){_=!0,v.map(function(e){e.remove()}),v=[]}function u(e,t,n){if(!n.busy){h=$(e).find(\"th\"),l=n.mode,d=!angular.isDefined(n.saveTableSizes)||n.saveTableSizes,p=n.profile;var r=function(t,e){try{var n=e.rzMode?t.mode:\"BasicResizer\",r=i.get(n);return r}catch(e){return console.error(\"The resizer \"+t.mode+\" was not found\"),null}}(n,t);r&&(b=new r(e,h,w),d&&(S=s.loadTableSizes(e,n.mode,n.profile)),g=b.handles(h),f=b.ctrlColumns,b.setup(),function(o){o&&($(y).width(\"auto\"),f.each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr(\"id\"),i=o[r];$(t).css({width:i})}),b.onTableReady())}(S),g.each(function(e,t){!function(e,t,n){var r=$(\"<div>\",{class:e.options.handleClass||\"rz-handle\"});$(n).prepend(r),v.push(r);var i=b.handleMiddleware(r,n);!function(i,e,o,a){$(o).mousedown(function(e){_&&(b.onFirstDrag(a,o),b.onTableReady(),_=!1),i.options.onResizeStarted&&i.options.onResizeStarted(a);var t={};b.intervene&&(((t=b.intervene.selector(a)).column=t).orgWidth=$(t).width()),e.preventDefault(),$(o).addClass(i.options.handleClassActive||\"rz-handle-active\");var n=e.clientX,r=$(a).width();m=function(o,a,s,u,l){return function(e){var t=e.clientX,n=t-s,r=b.calculate(u,n);if(!(r<c(a)||b.restrict(r,n))){if(b.intervene){var i=b.intervene.calculator(l.orgWidth,n);if(i<c(l.column))return;if(b.intervene.restrict(i,n))return;$(l.column).width(i)}o.options.onResizeInProgress&&o.options.onResizeInProgress(a,r,n),$(a).width(r)}}}(i,a,n,r,t),$(window).mousemove(m),$(window).one(\"mouseup\",function(e,t,n){return function(){$(n).removeClass(e.options.handleClassActive||\"rz-handle-active\"),m&&$(window).unbind(\"mousemove\",m),e.options.onResizeEnded&&e.options.onResizeEnded(t),b.onEndDrag(),d&&(S||(S={}),$(h).each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr(\"id\");r&&(S[r]=b.saveAttr(t))}),s.saveTableSizes(y,l,p,S))}}(i,a,o))})}(e,0,r,i)}(n,0,t)}))}}function c(e){return parseInt($(e).css(\"min-width\"))||0}var l,d,p,h=null,f=null,g=null,m=null,v=[],y=null,w=null,b=null,_=!0,S=null;return e.$inject=[\"$scope\",\"$attrs\",\"$element\"],{restrict:\"A\",link:function(e,t,n){y=t,w=e.container?$(e.container):$(y).parent(),e.options=n.rzOptions&&e.options||{},$(y).addClass(e.options.tableClass||\"rz-table\"),u(y,n,e),function(e,t,n){if(t.rzModel){var r=o(t.rzModel);r.assign(n.$parent,{update:function(){a(),u(e,t,n)},reset:function(){(function(e){$(e).outerWidth(\"100%\"),$(e).find(\"th\").width(\"auto\")})(e),this.clearStorageActive(),this.update()},clearStorage:function(){s.clearAll()},clearStorageActive:function(){s.clearCurrent(e,l,p)}})}}(y,n,e),function(e,t,n){n.$watch(function(){return $(e).find(\"th\").length},r(e,t,n))}(y,n,e),function(e,t,n){n.$watch(\"profile\",r(e,t,n)),n.$watch(\"mode\",r(e,t,n)),n.$watch(\"busy\",r(e,t,n))}(y,n,e)},controller:e,scope:{mode:\"=rzMode\",profile:\"=?rzProfile\",busy:\"=?rzBusy\",saveTableSizes:\"=?rzSave\",options:\"=?rzOptions\",model:\"=rzModel\",container:\"@rzContainer\"}}}]),angular.module(\"rzTable\").directive(\"rzCol\",[function(){return{restrict:\"A\",priority:650,link:function(e,t,n){e.rzCol=e.$eval(n.rzCol)},require:\"^^rzTable\",scope:!0}}]),angular.module(\"rzTable\").service(\"resizeStorage\",[\"$window\",function(a){function s(e,t,n){return e.attr(\"id\")?r+\".\"+e.attr(\"id\")+\".\"+t+(n?\".\"+n:\"\"):void console.error(\"Table has no id\",e)}var r=\"ngColumnResize\";this.loadTableSizes=function(e,t,n){var r=s(e,t,n),i=a.localStorage.getItem(r);return JSON.parse(i)},this.saveTableSizes=function(e,t,n,r){var i=s(e,t,n);if(i){var o=JSON.stringify(r);a.localStorage.setItem(i,o)}},this.clearAll=function(){for(var e=[],t=0;t<a.localStorage.length;++t){var n=localStorage.key(t);n&&n.startsWith(r)&&e.push(n)}e.map(function(e){a.localStorage.removeItem(e)})},this.clearCurrent=function(e,t,n){var r=s(e,t,n);r&&a.localStorage.removeItem(r)}}]),angular.module(\"rzTable\").factory(\"ResizerModel\",[function(){function e(e,t,n){this.table=e,this.columns=t,this.container=n,this.handleColumns=this.handles(),this.ctrlColumns=this.ctrlColumns()}return e.prototype.setup=function(){$(this.container).css({overflowX:\"hidden\"})},e.prototype.onTableReady=function(){$(this.table).outerWidth(\"100%\")},e.prototype.getMinWidth=function(e){return parseInt($(e).css(\"min-width\"))||0},e.prototype.handles=function(){return this.columns},e.prototype.ctrlColumns=function(){return this.handleColumns},e.prototype.onFirstDrag=function(){$(this.ctrlColumns).each(function(e,t){$(t).width($(t).width())})},e.prototype.handleMiddleware=function(e,t){return t},e.prototype.restrict=function(e){return!1},e.prototype.calculate=function(e,t){return e+t},e.prototype.onEndDrag=function(){},e.prototype.saveAttr=function(e){return $(e).outerWidth()},e}]),angular.module(\"rzTable\").factory(\"BasicResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n),this.ctrlColumns=this.columns,this.intervene={selector:i,calculator:o,restrict:a}}function i(e){return $(e).next()}function o(e,t){return e-t}function a(e){return e<25}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflowX:\"hidden\"}),$(this.table).css({width:\"100%\"})},e.prototype.handles=function(){return $(this.columns).not(\":last\")},e.prototype.onFirstDrag=function(){this.onEndDrag()},e.prototype.onEndDrag=function(){var r=$(this.table).outerWidth(),i=[];$(this.columns).each(function(e,t){var n=$(t).outerWidth()/r*100+\"%\";i.push(function(){$(t).css({width:n})})}),i.map(function(e){e()})},e.prototype.saveAttr=function(e){return $(e)[0].style.width},e}]),angular.module(\"rzTable\").factory(\"FixedResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n),this.fixedColumn=$(e).find(\"th\").first(),this.bound=!1}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflowX:\"hidden\"}),$(this.table).css({width:\"100%\"}),$(this.columns).first().css({width:\"auto\"})},e.prototype.handles=function(){return $(this.columns).not(\":last\")},e.prototype.ctrlColumns=function(){return $(this.columns).not(\":first\")},e.prototype.onFirstDrag=function(){$(this.ctrlColumns).each(function(e,t){$(t).width($(t).width())})},e.prototype.handleMiddleware=function(e,t){return $(t).next()},e.prototype.restrict=function(e,t){return this.bound&&this.bound<t?this.bound=!1:!!(this.bound&&this.bound>t)||(this.fixedColumn.width()<=this.getMinWidth(this.fixedColumn)?(this.bound=t,$(this.fixedColumn).width(this.minWidth),!0):void 0)},e.prototype.onEndDrag=function(){this.bound=!1},e.prototype.calculate=function(e,t){return e-t},e}]),angular.module(\"rzTable\").factory(\"OverflowResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n)}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflow:\"auto\"})},e.prototype.onTableReady=function(){$(this.table).width(1)},e}]),function(e,t){\"function\"==typeof define&&define.amd?define([\"angular\"],t):\"object\"==typeof module&&module.exports?module.exports=t(require(\"angular\")):e.angularClipboard=t(e.angular)}(this,function(i){return i.module(\"angular-clipboard\",[]).factory(\"clipboard\",[\"$document\",\"$window\",function(o,a){return{copyText:function(e,t){var n=a.pageXOffset||o[0].documentElement.scrollLeft,r=a.pageYOffset||o[0].documentElement.scrollTop,i=function(e,t){var n=o[0].createElement(\"textarea\");return n.style.position=\"absolute\",n.style.fontSize=\"12pt\",n.style.border=\"0\",n.style.padding=\"0\",n.style.margin=\"0\",n.style.left=\"-10000px\",n.style.top=(a.pageYOffset||o[0].documentElement.scrollTop)+\"px\",n.textContent=e,n}(e);o[0].body.appendChild(i),function(e){try{o[0].body.style.webkitUserSelect=\"initial\";var t=o[0].getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n),e.select(),e.setSelectionRange(0,999999);try{if(!o[0].execCommand(\"copy\"))throw\"failure copy\"}finally{t.removeAllRanges()}}finally{o[0].body.style.webkitUserSelect=\"\"}}(i),a.scrollTo(n,r),o[0].body.removeChild(i)},supported:\"queryCommandSupported\"in o[0]&&o[0].queryCommandSupported(\"copy\")}}]).directive(\"clipboard\",[\"clipboard\",function(r){return{restrict:\"A\",scope:{onCopied:\"&\",onError:\"&\",text:\"=\",supported:\"=?\"},link:function(t,n){t.supported=r.supported,n.on(\"click\",function(e){try{r.copyText(t.text,n[0]),i.isFunction(t.onCopied)&&t.$evalAsync(t.onCopied())}catch(e){i.isFunction(t.onError)&&t.$evalAsync(t.onError({err:e}))}})}}}])}),function(e,t){\"function\"==typeof define&&define.amd?define(\"sifter\",t):\"object\"==typeof exports?module.exports=t():e.Sifter=t()}(this,function(){var e=function(e,t){this.items=e,this.settings=t||{diacritics:!0}};e.prototype.tokenize=function(e){if(!(e=s(String(e||\"\").toLowerCase()))||!e.length)return[];var t,n,r,i,o=[],a=e.split(/ +/);for(t=0,n=a.length;t<n;t++){if(r=u(a[t]),this.settings.diacritics)for(i in l)l.hasOwnProperty(i)&&(r=r.replace(new RegExp(i,\"g\"),l[i]));o.push({string:a[t],regex:new RegExp(r,\"i\")})}return o},e.prototype.iterator=function(e,t){(a(e)?Array.prototype.forEach||function(e){for(var t=0,n=this.length;t<n;t++)e(this[t],t,this)}:function(e){for(var t in this)this.hasOwnProperty(t)&&e(this[t],t,this)}).apply(e,[t])},e.prototype.getScoreFunction=function(e,t){var i,o,a,s;e=this.prepareSearch(e,t),o=e.tokens,i=e.options.fields,a=o.length,s=e.options.nesting;var u,l=function(e,t){var n,r;return e?-1===(r=(e=String(e||\"\")).search(t.regex))?0:(n=t.string.length/e.length,0===r&&(n+=.5),n):0},c=(u=i.length)?1===u?function(e,t){return l(f(t,i[0],s),e)}:function(e,t){for(var n=0,r=0;n<u;n++)r+=l(f(t,i[n],s),e);return r/u}:function(){return 0};return a?1===a?function(e){return c(o[0],e)}:\"and\"===e.options.conjunction?function(e){for(var t,n=0,r=0;n<a;n++){if((t=c(o[n],e))<=0)return 0;r+=t}return r/a}:function(e){for(var t=0,n=0;t<a;t++)n+=c(o[t],e);return n/a}:function(){return 0}},e.prototype.getSortFunction=function(e,n){var t,r,i,o,a,s,u,l,c,d,p;if(p=!(e=(i=this).prepareSearch(e,n)).query&&n.sort_empty||n.sort,c=function(e,t){return\"$score\"===e?t.score:f(i.items[t.id],e,n.nesting)},a=[],p)for(t=0,r=p.length;t<r;t++)(e.query||\"$score\"!==p[t].field)&&a.push(p[t]);if(e.query){for(d=!0,t=0,r=a.length;t<r;t++)if(\"$score\"===a[t].field){d=!1;break}d&&a.unshift({field:\"$score\",direction:\"desc\"})}else for(t=0,r=a.length;t<r;t++)if(\"$score\"===a[t].field){a.splice(t,1);break}for(l=[],t=0,r=a.length;t<r;t++)l.push(\"desc\"===a[t].direction?-1:1);return(s=a.length)?1===s?(o=a[0].field,u=l[0],function(e,t){return u*h(c(o,e),c(o,t))}):function(e,t){var n,r,i;for(n=0;n<s;n++)if(i=a[n].field,r=l[n]*h(c(i,e),c(i,t)))return r;return 0}:null},e.prototype.prepareSearch=function(e,t){if(\"object\"==typeof e)return e;var n=(t=o({},t)).fields,r=t.sort,i=t.sort_empty;return n&&!a(n)&&(t.fields=[n]),r&&!a(r)&&(t.sort=[r]),i&&!a(i)&&(t.sort_empty=[i]),{options:t,query:String(e||\"\").toLowerCase(),tokens:this.tokenize(e),total:0,items:[]}},e.prototype.search=function(e,n){var r,i,t,o;return i=this.prepareSearch(e,n),n=i.options,e=i.query,o=n.score||this.getScoreFunction(i),e.length?this.iterator(this.items,function(e,t){r=o(e),(!1===n.filter||0<r)&&i.items.push({score:r,id:t})}):this.iterator(this.items,function(e,t){i.items.push({score:1,id:t})}),(t=this.getSortFunction(i,n))&&i.items.sort(t),i.total=i.items.length,\"number\"==typeof n.limit&&(i.items=i.items.slice(0,n.limit)),i};var h=function(e,t){return\"number\"==typeof e&&\"number\"==typeof t?t<e?1:e<t?-1:0:(e=n(String(e||\"\")),(t=n(String(t||\"\")))<e?1:e<t?-1:0)},o=function(e,t){var n,r,i,o;for(n=1,r=arguments.length;n<r;n++)if(o=arguments[n])for(i in o)o.hasOwnProperty(i)&&(e[i]=o[i]);return e},f=function(e,t,n){if(e&&t){if(!n)return e[t];for(var r=t.split(\".\");r.length&&(e=e[r.shift()]););return e}},s=function(e){return(e+\"\").replace(/^\\s+|\\s+$|/g,\"\")},u=function(e){return(e+\"\").replace(/([.?*+^$[\\]\\\\(){}|-])/g,\"\\\\$1\")},a=Array.isArray||\"undefined\"!=typeof $&&$.isArray||function(e){return\"[object Array]\"===Object.prototype.toString.call(e)},l={a:\"[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]\",b:\"[b␢βΒB฿𐌁ᛒ]\",c:\"[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄＣｃ]\",d:\"[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅＤｄð]\",e:\"[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇＥｅɘǝƏƐε]\",f:\"[fƑƒḞḟ]\",g:\"[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]\",h:\"[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]\",i:\"[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪＩｉ]\",j:\"[jȷĴĵɈɉʝɟʲ]\",k:\"[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]\",l:\"[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟＬｌ]\",n:\"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴＮｎŊŋ]\",o:\"[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]\",p:\"[pṔṕṖṗⱣᵽƤƥᵱ]\",q:\"[qꝖꝗʠɊɋꝘꝙq̃]\",r:\"[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]\",s:\"[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]\",t:\"[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]\",u:\"[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]\",v:\"[vṼṽṾṿƲʋꝞꝟⱱʋ]\",w:\"[wẂẃẀẁŴŵẄẅẆẇẈẉ]\",x:\"[xẌẍẊẋχ]\",y:\"[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]\",z:\"[zŹźẐẑŽžŻżẒẓẔẕƵƶ]\"},n=function(){var e,t,n,r,i=\"\",o={};for(n in l)if(l.hasOwnProperty(n))for(i+=r=l[n].substring(2,l[n].length-1),e=0,t=r.length;e<t;e++)o[r.charAt(e)]=n;var a=new RegExp(\"[\"+i+\"]\",\"g\");return function(e){return e.replace(a,function(e){return o[e]}).toLowerCase()}}();return e}),function(e,t){\"function\"==typeof define&&define.amd?define(\"microplugin\",t):\"object\"==typeof exports?module.exports=t():e.MicroPlugin=t()}(this,function(){var e={mixin:function(r){r.plugins={},r.prototype.initializePlugins=function(e){var t,n,r,i=[];if(this.plugins={names:[],settings:{},requested:{},loaded:{}},o.isArray(e))for(t=0,n=e.length;t<n;t++)\"string\"==typeof e[t]?i.push(e[t]):(this.plugins.settings[e[t].name]=e[t].options,i.push(e[t].name));else if(e)for(r in e)e.hasOwnProperty(r)&&(this.plugins.settings[r]=e[r],i.push(r));for(;i.length;)this.require(i.shift())},r.prototype.loadPlugin=function(e){var t=this.plugins,n=r.plugins[e];if(!r.plugins.hasOwnProperty(e))throw new Error('Unable to find \"'+e+'\" plugin');t.requested[e]=!0,t.loaded[e]=n.fn.apply(this,[this.plugins.settings[e]||{}]),t.names.push(e)},r.prototype.require=function(e){var t=this.plugins;if(!this.plugins.loaded.hasOwnProperty(e)){if(t.requested[e])throw new Error('Plugin has circular dependency (\"'+e+'\")');this.loadPlugin(e)}return t.loaded[e]},r.define=function(e,t){r.plugins[e]={name:e,fn:t}}}},o={isArray:Array.isArray||function(e){return\"[object Array]\"===Object.prototype.toString.call(e)}};return e}),function(e,t){\"function\"==typeof define&&define.amd?define(\"selectize\",[\"jquery\",\"sifter\",\"microplugin\"],t):\"object\"==typeof exports?module.exports=t(require(\"jquery\"),require(\"sifter\"),require(\"microplugin\")):e.Selectize=t(e.jQuery,e.Sifter,e.MicroPlugin)}(this,function(C,u,e){\"use strict\";var k=function(e,t){if(\"string\"!=typeof t||t.length){var u=\"string\"==typeof t?new RegExp(t,\"i\"):t,l=function(e){var t=0;if(3===e.nodeType){var n=e.data.search(u);if(0<=n&&0<e.data.length){var r=e.data.match(u),i=document.createElement(\"span\");i.className=\"highlight\";var o=e.splitText(n),a=(o.splitText(r[0].length),o.cloneNode(!0));i.appendChild(a),o.parentNode.replaceChild(i,o),t=1}}else if(1===e.nodeType&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&(\"highlight\"!==e.className||\"SPAN\"!==e.tagName))for(var s=0;s<e.childNodes.length;++s)s+=l(e.childNodes[s]);return t};return e.each(function(){l(this)})}};C.fn.removeHighlight=function(){return this.find(\"span.highlight\").each(function(){this.parentNode.firstChild.nodeName;var e=this.parentNode;e.replaceChild(this.firstChild,this),e.normalize()}).end()};var r=function(){};r.prototype={on:function(e,t){this._events=this._events||{},this._events[e]=this._events[e]||[],this._events[e].push(t)},off:function(e,t){var n=arguments.length;return 0===n?delete this._events:1===n?delete this._events[e]:(this._events=this._events||{},void(e in this._events!=!1&&this._events[e].splice(this._events[e].indexOf(t),1)))},trigger:function(e){if(this._events=this._events||{},e in this._events!=!1)for(var t=0;t<this._events[e].length;t++)this._events[e][t].apply(this,Array.prototype.slice.call(arguments,1))}},r.mixin=function(e){for(var t=[\"on\",\"off\",\"trigger\"],n=0;n<t.length;n++)e.prototype[t[n]]=r.prototype[t[n]]};var v=/Mac/.test(navigator.userAgent),y=v?91:17,w=v?18:17,b=!/android/i.test(window.navigator.userAgent)&&!!document.createElement(\"input\").validity,l=function(e){return void 0!==e},D=function(e){return null==e?null:\"boolean\"==typeof e?e?\"1\":\"0\":e+\"\"},s=function(e){return(e+\"\").replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")},t={before:function(e,t,n){var r=e[t];e[t]=function(){return n.apply(e,arguments),r.apply(e,arguments)}},after:function(t,e,n){var r=t[e];t[e]=function(){var e=r.apply(t,arguments);return n.apply(t,arguments),e}}},n=function(t,n,e){var r,i=t.trigger,o={};for(r in t.trigger=function(){var e=arguments[0];if(-1===n.indexOf(e))return i.apply(t,arguments);o[e]=arguments},e.apply(t,[]),t.trigger=i,o)o.hasOwnProperty(r)&&i.apply(t,o[r])},p=function(e){var t={};if(\"selectionStart\"in e)t.start=e.selectionStart,t.length=e.selectionEnd-t.start;else if(document.selection){e.focus();var n=document.selection.createRange(),r=document.selection.createRange().text.length;n.moveStart(\"character\",-e.value.length),t.start=n.text.length-r,t.length=r}return t},$=function(c){var d=null,e=function(e,t){var n,r,i,o,a,s,u,l;t=t||{},(e=e||window.event||{}).metaKey||e.altKey||(t.force||!1!==c.data(\"grow\"))&&(n=c.val(),e.type&&\"keydown\"===e.type.toLowerCase()&&(i=48<=(r=e.keyCode)&&r<=57||65<=r&&r<=90||96<=r&&r<=111||186<=r&&r<=222||32===r,46===r||8===r?(l=p(c[0])).length?n=n.substring(0,l.start)+n.substring(l.start+l.length):8===r&&l.start?n=n.substring(0,l.start-1)+n.substring(l.start+1):46===r&&void 0!==l.start&&(n=n.substring(0,l.start)+n.substring(l.start+1)):i&&(s=e.shiftKey,u=String.fromCharCode(e.keyCode),n+=u=s?u.toUpperCase():u.toLowerCase())),o=c.attr(\"placeholder\"),!n&&o&&(n=o),(a=function(e,t){return e?(_.$testInput||(_.$testInput=C(\"<span />\").css({position:\"absolute\",top:-99999,left:-99999,width:\"auto\",padding:0,whiteSpace:\"pre\"}).appendTo(\"body\")),_.$testInput.text(e),function(e,t,n){var r,i,o={};if(n)for(r=0,i=n.length;r<i;r++)o[n[r]]=e.css(n[r]);else o=e.css();t.css(o)}(t,_.$testInput,[\"letterSpacing\",\"fontSize\",\"fontFamily\",\"fontWeight\",\"textTransform\"]),_.$testInput.width()):0}(n,c)+4)!==d&&(d=a,c.width(a),c.triggerHandler(\"resize\")))};c.on(\"keydown keyup update blur\",e),e()},_=function(e,t){var n,r,i,o,a=this;(o=e[0]).selectize=a;var s=window.getComputedStyle&&window.getComputedStyle(o,null);if(i=(i=s?s.getPropertyValue(\"direction\"):o.currentStyle&&o.currentStyle.direction)||e.parents(\"[dir]:first\").attr(\"dir\")||\"\",C.extend(a,{order:0,settings:t,$input:e,tabIndex:e.attr(\"tabindex\")||\"\",tagType:\"select\"===o.tagName.toLowerCase()?1:2,rtl:/rtl/i.test(i),eventNS:\".selectize\"+ ++_.count,highlightedValue:null,isBlurring:!1,isOpen:!1,isDisabled:!1,isRequired:e.is(\"[required]\"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:\"\",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===t.loadThrottle?a.onSearchChange:function(n,r){var i;return function(){var e=this,t=arguments;window.clearTimeout(i),i=window.setTimeout(function(){n.apply(e,t)},r)}}(a.onSearchChange,t.loadThrottle)}),a.sifter=new u(this.options,{diacritics:t.diacritics}),a.settings.options){for(n=0,r=a.settings.options.length;n<r;n++)a.registerOption(a.settings.options[n]);delete a.settings.options}if(a.settings.optgroups){for(n=0,r=a.settings.optgroups.length;n<r;n++)a.registerOptionGroup(a.settings.optgroups[n]);delete a.settings.optgroups}a.settings.mode=a.settings.mode||(1===a.settings.maxItems?\"single\":\"multi\"),\"boolean\"!=typeof a.settings.hideSelected&&(a.settings.hideSelected=\"multi\"===a.settings.mode),a.initializePlugins(a.settings.plugins),a.setupCallbacks(),a.setupTemplates(),a.setup()};return r.mixin(_),void 0!==e?e.mixin(_):function(e,t){t||(t={});console.error(\"Selectize: \"+e),t.explanation&&(console.group&&console.group(),console.error(t.explanation),console.group&&console.groupEnd())}(\"Dependency MicroPlugin is missing\",{explanation:'Make sure you either: (1) are using the \"standalone\" version of Selectize, or (2) require MicroPlugin before you load Selectize.'}),C.extend(_.prototype,{setup:function(){var e,t,n,r,i,o,a,s,u,l,c=this,d=c.settings,p=c.eventNS,h=C(window),f=C(document),g=c.$input;if(a=c.settings.mode,s=g.attr(\"class\")||\"\",e=C(\"<div>\").addClass(d.wrapperClass).addClass(s).addClass(a),t=C(\"<div>\").addClass(d.inputClass).addClass(\"items\").appendTo(e),n=C('<input type=\"text\" autocomplete=\"off\" />').appendTo(t).attr(\"tabindex\",g.is(\":disabled\")?\"-1\":c.tabIndex),o=C(d.dropdownParent||e),r=C(\"<div>\").addClass(d.dropdownClass).addClass(a).hide().appendTo(o),i=C(\"<div>\").addClass(d.dropdownContentClass).appendTo(r),(l=g.attr(\"id\"))&&(n.attr(\"id\",l+\"-selectized\"),C(\"label[for='\"+l+\"']\").attr(\"for\",l+\"-selectized\")),c.settings.copyClassesToDropdown&&r.addClass(s),e.css({width:g[0].style.width}),c.plugins.names.length&&(u=\"plugin-\"+c.plugins.names.join(\" plugin-\"),e.addClass(u),r.addClass(u)),(null===d.maxItems||1<d.maxItems)&&1===c.tagType&&g.attr(\"multiple\",\"multiple\"),c.settings.placeholder&&n.attr(\"placeholder\",d.placeholder),!c.settings.splitOn&&c.settings.delimiter){var m=c.settings.delimiter.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\");c.settings.splitOn=new RegExp(\"\\\\s*\"+m+\"+\\\\s*\")}g.attr(\"autocorrect\")&&n.attr(\"autocorrect\",g.attr(\"autocorrect\")),g.attr(\"autocapitalize\")&&n.attr(\"autocapitalize\",g.attr(\"autocapitalize\")),n[0].type=g[0].type,c.$wrapper=e,c.$control=t,c.$control_input=n,c.$dropdown=r,c.$dropdown_content=i,r.on(\"mouseenter mousedown click\",\"[data-disabled]>[data-selectable]\",function(e){e.stopImmediatePropagation()}),r.on(\"mouseenter\",\"[data-selectable]\",function(){return c.onOptionHover.apply(c,arguments)}),r.on(\"mousedown click\",\"[data-selectable]\",function(){return c.onOptionSelect.apply(c,arguments)}),function(n,e,t,r){n.on(e,t,function(e){for(var t=e.target;t&&t.parentNode!==n[0];)t=t.parentNode;return e.currentTarget=t,r.apply(this,[e])})}(t,\"mousedown\",\"*:not(input)\",function(){return c.onItemSelect.apply(c,arguments)}),$(n),t.on({mousedown:function(){return c.onMouseDown.apply(c,arguments)},click:function(){return c.onClick.apply(c,arguments)}}),n.on({mousedown:function(e){e.stopPropagation()},keydown:function(){return c.onKeyDown.apply(c,arguments)},keyup:function(){return c.onKeyUp.apply(c,arguments)},keypress:function(){return c.onKeyPress.apply(c,arguments)},resize:function(){c.positionDropdown.apply(c,[])},blur:function(){return c.onBlur.apply(c,arguments)},focus:function(){return c.ignoreBlur=!1,c.onFocus.apply(c,arguments)},paste:function(){return c.onPaste.apply(c,arguments)}}),f.on(\"keydown\"+p,function(e){c.isCmdDown=e[v?\"metaKey\":\"ctrlKey\"],c.isCtrlDown=e[v?\"altKey\":\"ctrlKey\"],c.isShiftDown=e.shiftKey}),f.on(\"keyup\"+p,function(e){e.keyCode===w&&(c.isCtrlDown=!1),16===e.keyCode&&(c.isShiftDown=!1),e.keyCode===y&&(c.isCmdDown=!1)}),f.on(\"mousedown\"+p,function(e){if(c.isFocused){if(e.target===c.$dropdown[0]||e.target.parentNode===c.$dropdown[0])return!1;c.$control.has(e.target).length||e.target===c.$control[0]||c.blur(e.target)}}),h.on([\"scroll\"+p,\"resize\"+p].join(\" \"),function(){c.isOpen&&c.positionDropdown.apply(c,arguments)}),h.on(\"mousemove\"+p,function(){c.ignoreHover=!1}),this.revertSettings={$children:g.children().detach(),tabindex:g.attr(\"tabindex\")},g.attr(\"tabindex\",-1).hide().after(c.$wrapper),C.isArray(d.items)&&(c.setValue(d.items),delete d.items),b&&g.on(\"invalid\"+p,function(e){e.preventDefault(),c.isInvalid=!0,c.refreshState()}),c.updateOriginalInput(),c.refreshItems(),c.refreshState(),c.updatePlaceholder(),c.isSetup=!0,g.is(\":disabled\")&&c.disable(),c.on(\"change\",this.onChange),g.data(\"selectize\",c),g.addClass(\"selectized\"),c.trigger(\"initialize\"),!0===d.preload&&c.onSearchChange(\"\")},setupTemplates:function(){var n=this.settings.labelField,r=this.settings.optgroupLabelField,e={optgroup:function(e){return'<div class=\"optgroup\">'+e.html+\"</div>\"},optgroup_header:function(e,t){return'<div class=\"optgroup-header\">'+t(e[r])+\"</div>\"},option:function(e,t){return'<div class=\"option\">'+t(e[n])+\"</div>\"},item:function(e,t){return'<div class=\"item\">'+t(e[n])+\"</div>\"},option_create:function(e,t){return'<div class=\"create\">Add <strong>'+t(e.input)+\"</strong>&hellip;</div>\"}};this.settings.render=C.extend({},e,this.settings.render)},setupCallbacks:function(){var e,t,n={initialize:\"onInitialize\",change:\"onChange\",item_add:\"onItemAdd\",item_remove:\"onItemRemove\",clear:\"onClear\",option_add:\"onOptionAdd\",option_remove:\"onOptionRemove\",option_clear:\"onOptionClear\",optgroup_add:\"onOptionGroupAdd\",optgroup_remove:\"onOptionGroupRemove\",optgroup_clear:\"onOptionGroupClear\",dropdown_open:\"onDropdownOpen\",dropdown_close:\"onDropdownClose\",type:\"onType\",load:\"onLoad\",focus:\"onFocus\",blur:\"onBlur\"};for(e in n)n.hasOwnProperty(e)&&(t=this.settings[n[e]])&&this.on(e,t)},onClick:function(e){this.isFocused&&this.isOpen||(this.focus(),e.preventDefault())},onMouseDown:function(e){var t=this,n=e.isDefaultPrevented();C(e.target);if(t.isFocused){if(e.target!==t.$control_input[0])return\"single\"===t.settings.mode?t.isOpen?t.close():t.open():n||t.setActiveItem(null),!1}else n||window.setTimeout(function(){t.focus()},0)},onChange:function(){this.$input.trigger(\"change\")},onPaste:function(e){var i=this;i.isFull()||i.isInputHidden||i.isLocked?e.preventDefault():i.settings.splitOn&&setTimeout(function(){var e=i.$control_input.val();if(e.match(i.settings.splitOn))for(var t=C.trim(e).split(i.settings.splitOn),n=0,r=t.length;n<r;n++)i.createItem(t[n])},0)},onKeyPress:function(e){if(this.isLocked)return e&&e.preventDefault();var t=String.fromCharCode(e.keyCode||e.which);return this.settings.create&&\"multi\"===this.settings.mode&&t===this.settings.delimiter?(this.createItem(),e.preventDefault(),!1):void 0},onKeyDown:function(e){e.target,this.$control_input[0];var t=this;if(t.isLocked)9!==e.keyCode&&e.preventDefault();else{switch(e.keyCode){case 65:if(t.isCmdDown)return void t.selectAll();break;case 27:return void(t.isOpen&&(e.preventDefault(),e.stopPropagation(),t.close()));case 78:if(!e.ctrlKey||e.altKey)break;case 40:if(!t.isOpen&&t.hasOptions)t.open();else if(t.$activeOption){t.ignoreHover=!0;var n=t.getAdjacentOption(t.$activeOption,1);n.length&&t.setActiveOption(n,!0,!0)}return void e.preventDefault();case 80:if(!e.ctrlKey||e.altKey)break;case 38:if(t.$activeOption){t.ignoreHover=!0;var r=t.getAdjacentOption(t.$activeOption,-1);r.length&&t.setActiveOption(r,!0,!0)}return void e.preventDefault();case 13:return void(t.isOpen&&t.$activeOption&&(t.onOptionSelect({currentTarget:t.$activeOption}),e.preventDefault()));case 37:return void t.advanceSelection(-1,e);case 39:return void t.advanceSelection(1,e);case 9:return t.settings.selectOnTab&&t.isOpen&&t.$activeOption&&(t.onOptionSelect({currentTarget:t.$activeOption}),t.isFull()||e.preventDefault()),void(t.settings.create&&t.createItem()&&e.preventDefault());case 8:case 46:return void t.deleteSelection(e)}!t.isFull()&&!t.isInputHidden||(v?e.metaKey:e.ctrlKey)||e.preventDefault()}},onKeyUp:function(e){if(this.isLocked)return e&&e.preventDefault();var t=this.$control_input.val()||\"\";this.lastValue!==t&&(this.lastValue=t,this.onSearchChange(t),this.refreshOptions(),this.trigger(\"type\",t))},onSearchChange:function(t){var n=this,r=n.settings.load;r&&(n.loadedSearches.hasOwnProperty(t)||(n.loadedSearches[t]=!0,n.load(function(e){r.apply(n,[t,e])})))},onFocus:function(e){var t=this,n=t.isFocused;if(t.isDisabled)return t.blur(),e&&e.preventDefault(),!1;t.ignoreFocus||(t.isFocused=!0,\"focus\"===t.settings.preload&&t.onSearchChange(\"\"),n||t.trigger(\"focus\"),t.$activeItems.length||(t.showInput(),t.setActiveItem(null),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())},onBlur:function(e,t){var n=this;if(n.isFocused&&(n.isFocused=!1,!n.ignoreFocus)){if(!n.ignoreBlur&&document.activeElement===n.$dropdown_content[0])return n.ignoreBlur=!0,void n.onFocus(e);var r=function(){n.close(),n.setTextboxValue(\"\"),n.setActiveItem(null),n.setActiveOption(null),n.setCaret(n.items.length),n.refreshState(),t&&t.focus&&t.focus(),n.isBlurring=!1,n.ignoreFocus=!1,n.trigger(\"blur\")};n.isBlurring=!0,n.ignoreFocus=!0,n.settings.create&&n.settings.createOnBlur?n.createItem(null,!1,r):r()}},onOptionHover:function(e){this.ignoreHover||this.setActiveOption(e.currentTarget,!1)},onOptionSelect:function(e){var t,n,r=this;e.preventDefault&&(e.preventDefault(),e.stopPropagation()),(n=C(e.currentTarget)).hasClass(\"create\")?r.createItem(null,function(){r.settings.closeAfterSelect&&r.close()}):void 0!==(t=n.attr(\"data-value\"))&&(r.lastQuery=null,r.setTextboxValue(\"\"),r.addItem(t),r.settings.closeAfterSelect?r.close():!r.settings.hideSelected&&e.type&&/mouse/.test(e.type)&&r.setActiveOption(r.getOption(t)))},onItemSelect:function(e){this.isLocked||\"multi\"===this.settings.mode&&(e.preventDefault(),this.setActiveItem(e.currentTarget,e))},load:function(e){var t=this,n=t.$wrapper.addClass(t.settings.loadingClass);t.loading++,e.apply(t,[function(e){t.loading=Math.max(t.loading-1,0),e&&e.length&&(t.addOption(e),t.refreshOptions(t.isFocused&&!t.isInputHidden)),t.loading||n.removeClass(t.settings.loadingClass),t.trigger(\"load\",e)}])},setTextboxValue:function(e){var t=this.$control_input;t.val()!==e&&(t.val(e).triggerHandler(\"update\"),this.lastValue=e)},getValue:function(){return 1===this.tagType&&this.$input.attr(\"multiple\")?this.items:this.items.join(this.settings.delimiter)},setValue:function(e,t){n(this,t?[]:[\"change\"],function(){this.clear(t),this.addItems(e,t)})},setActiveItem:function(e,t){var n,r,i,o,a,s,u,l,c=this;if(\"single\"!==c.settings.mode){if(!(e=C(e)).length)return C(c.$activeItems).removeClass(\"active\"),c.$activeItems=[],void(c.isFocused&&c.showInput());if(\"mousedown\"===(n=t&&t.type.toLowerCase())&&c.isShiftDown&&c.$activeItems.length){for(l=c.$control.children(\".active:last\"),o=Array.prototype.indexOf.apply(c.$control[0].childNodes,[l[0]]),(a=Array.prototype.indexOf.apply(c.$control[0].childNodes,[e[0]]))<o&&(u=o,o=a,a=u),r=o;r<=a;r++)s=c.$control[0].childNodes[r],-1===c.$activeItems.indexOf(s)&&(C(s).addClass(\"active\"),c.$activeItems.push(s));t.preventDefault()}else\"mousedown\"===n&&c.isCtrlDown||\"keydown\"===n&&this.isShiftDown?e.hasClass(\"active\")?(i=c.$activeItems.indexOf(e[0]),c.$activeItems.splice(i,1),e.removeClass(\"active\")):c.$activeItems.push(e.addClass(\"active\")[0]):(C(c.$activeItems).removeClass(\"active\"),c.$activeItems=[e.addClass(\"active\")[0]]);c.hideInput(),this.isFocused||c.focus()}},setActiveOption:function(e,t,n){var r,i,o,a,s,u=this;u.$activeOption&&u.$activeOption.removeClass(\"active\"),u.$activeOption=null,(e=C(e)).length&&(u.$activeOption=e.addClass(\"active\"),!t&&l(t)||(r=u.$dropdown_content.height(),i=u.$activeOption.outerHeight(!0),t=u.$dropdown_content.scrollTop()||0,s=(a=o=u.$activeOption.offset().top-u.$dropdown_content.offset().top+t)-r+i,r+t<o+i?u.$dropdown_content.stop().animate({scrollTop:s},n?u.settings.scrollDuration:0):o<t&&u.$dropdown_content.stop().animate({scrollTop:a},n?u.settings.scrollDuration:0)))},selectAll:function(){\"single\"!==this.settings.mode&&(this.$activeItems=Array.prototype.slice.apply(this.$control.children(\":not(input)\").addClass(\"active\")),this.$activeItems.length&&(this.hideInput(),this.close()),this.focus())},hideInput:function(){this.setTextboxValue(\"\"),this.$control_input.css({opacity:0,position:\"absolute\",left:this.rtl?1e4:-1e4}),this.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:\"relative\",left:0}),this.isInputHidden=!1},focus:function(){var e=this;e.isDisabled||(e.ignoreFocus=!0,e.$control_input[0].focus(),window.setTimeout(function(){e.ignoreFocus=!1,e.onFocus()},0))},blur:function(e){this.$control_input[0].blur(),this.onBlur(null,e)},getScoreFunction:function(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())},getSearchOptions:function(){var e=this.settings,t=e.sortField;return\"string\"==typeof t&&(t=[{field:t}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}},search:function(e){var t,n,r,i=this.settings,o=this.getSearchOptions();if(i.score&&\"function\"!=typeof(r=this.settings.score.apply(this,[e])))throw new Error('Selectize \"score\" setting must be a function that returns a function');if(e!==this.lastQuery?(this.lastQuery=e,n=this.sifter.search(e,C.extend(o,{score:r})),this.currentResults=n):n=C.extend(!0,{},this.currentResults),i.hideSelected)for(t=n.items.length-1;0<=t;t--)-1!==this.items.indexOf(D(n.items[t].id))&&n.items.splice(t,1);return n},refreshOptions:function(e){var t,n,r,i,o,a,s,u,l,c,d,p,h,f,g,m;void 0===e&&(e=!0);var v,y,w=this,b=C.trim(w.$control_input.val()),$=w.search(b),_=w.$dropdown_content,S=w.$activeOption&&D(w.$activeOption.attr(\"data-value\"));for(i=$.items.length,\"number\"==typeof w.settings.maxOptions&&(i=Math.min(i,w.settings.maxOptions)),o={},a=[],t=0;t<i;t++)for(s=w.options[$.items[t].id],u=w.render(\"option\",s),l=s[w.settings.optgroupField]||\"\",n=0,r=(c=C.isArray(l)?l:[l])&&c.length;n<r;n++)l=c[n],w.optgroups.hasOwnProperty(l)||(l=\"\"),o.hasOwnProperty(l)||(o[l]=document.createDocumentFragment(),a.push(l)),o[l].appendChild(u);for(this.settings.lockOptgroupOrder&&a.sort(function(e,t){return(w.optgroups[e].$order||0)-(w.optgroups[t].$order||0)}),d=document.createDocumentFragment(),t=0,i=a.length;t<i;t++)l=a[t],w.optgroups.hasOwnProperty(l)&&o[l].childNodes.length?((p=document.createDocumentFragment()).appendChild(w.render(\"optgroup_header\",w.optgroups[l])),p.appendChild(o[l]),d.appendChild(w.render(\"optgroup\",C.extend({},w.optgroups[l],{html:(v=p,y=void 0,y=document.createElement(\"div\"),y.appendChild(v.cloneNode(!0)),y.innerHTML),dom:p})))):d.appendChild(o[l]);if(_.html(d),w.settings.highlight&&(_.removeHighlight(),$.query.length&&$.tokens.length))for(t=0,i=$.tokens.length;t<i;t++)k(_,$.tokens[t].regex);if(!w.settings.hideSelected)for(t=0,i=w.items.length;t<i;t++)w.getOption(w.items[t]).addClass(\"selected\");(h=w.canCreate(b))&&(_.prepend(w.render(\"option_create\",{input:b})),m=C(_[0].childNodes[0])),w.hasOptions=0<$.items.length||h,w.hasOptions?(0<$.items.length?((g=S&&w.getOption(S))&&g.length?f=g:\"single\"===w.settings.mode&&w.items.length&&(f=w.getOption(w.items[0])),f&&f.length||(f=m&&!w.settings.addPrecedence?w.getAdjacentOption(m,1):_.find(\"[data-selectable]:first\"))):f=m,w.setActiveOption(f),e&&!w.isOpen&&w.open()):(w.setActiveOption(null),e&&w.isOpen&&w.close())},addOption:function(e){var t,n,r;if(C.isArray(e))for(t=0,n=e.length;t<n;t++)this.addOption(e[t]);else(r=this.registerOption(e))&&(this.userOptions[r]=!0,this.lastQuery=null,this.trigger(\"option_add\",r,e))},registerOption:function(e){var t=D(e[this.settings.valueField]);return null!=t&&!this.options.hasOwnProperty(t)&&(e.$order=e.$order||++this.order,this.options[t]=e,t)},registerOptionGroup:function(e){var t=D(e[this.settings.optgroupValueField]);return!!t&&(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)},addOptionGroup:function(e,t){t[this.settings.optgroupValueField]=e,(e=this.registerOptionGroup(t))&&this.trigger(\"optgroup_add\",e,t)},removeOptionGroup:function(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.renderCache={},this.trigger(\"optgroup_remove\",e))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger(\"optgroup_clear\")},updateOption:function(e,t){var n,r,i,o,a,s,u,l=this;if(e=D(e),i=D(t[l.settings.valueField]),null!==e&&l.options.hasOwnProperty(e)){if(\"string\"!=typeof i)throw new Error(\"Value must be set in option data\");u=l.options[e].$order,i!==e&&(delete l.options[e],-1!==(o=l.items.indexOf(e))&&l.items.splice(o,1,i)),t.$order=t.$order||u,l.options[i]=t,a=l.renderCache.item,s=l.renderCache.option,a&&(delete a[e],delete a[i]),s&&(delete s[e],delete s[i]),-1!==l.items.indexOf(i)&&(n=l.getItem(e),r=C(l.render(\"item\",t)),n.hasClass(\"active\")&&r.addClass(\"active\"),n.replaceWith(r)),l.lastQuery=null,l.isOpen&&l.refreshOptions(!1)}},removeOption:function(e,t){e=D(e);var n=this.renderCache.item,r=this.renderCache.option;n&&delete n[e],r&&delete r[e],delete this.userOptions[e],delete this.options[e],this.lastQuery=null,this.trigger(\"option_remove\",e),this.removeItem(e,t)},clearOptions:function(){var n=this;n.loadedSearches={},n.userOptions={},n.renderCache={};var r=n.options;C.each(n.options,function(e,t){-1==n.items.indexOf(e)&&delete r[e]}),n.options=n.sifter.items=r,n.lastQuery=null,n.trigger(\"option_clear\")},getOption:function(e){return this.getElementWithValue(e,this.$dropdown_content.find(\"[data-selectable]\"))},getAdjacentOption:function(e,t){var n=this.$dropdown.find(\"[data-selectable]\"),r=n.index(e)+t;return 0<=r&&r<n.length?n.eq(r):C()},getElementWithValue:function(e,t){if(null!=(e=D(e)))for(var n=0,r=t.length;n<r;n++)if(t[n].getAttribute(\"data-value\")===e)return C(t[n]);return C()},getItem:function(e){return this.getElementWithValue(e,this.$control.children())},addItems:function(e,t){this.buffer=document.createDocumentFragment();for(var n=this.$control[0].childNodes,r=0;r<n.length;r++)this.buffer.appendChild(n[r]);for(var i=C.isArray(e)?e:[e],o=(r=0,i.length);r<o;r++)this.isPending=r<o-1,this.addItem(i[r],t);var a=this.$control[0];a.insertBefore(this.buffer,a.firstChild),this.buffer=null},addItem:function(s,u){n(this,u?[]:[\"change\"],function(){var e,t,n,r,i,o=this,a=o.settings.mode;s=D(s),-1===o.items.indexOf(s)?o.options.hasOwnProperty(s)&&(\"single\"===a&&o.clear(u),\"multi\"===a&&o.isFull()||(e=C(o.render(\"item\",o.options[s])),i=o.isFull(),o.items.splice(o.caretPos,0,s),o.insertAtCaret(e),(!o.isPending||!i&&o.isFull())&&o.refreshState(),o.isSetup&&(n=o.$dropdown_content.find(\"[data-selectable]\"),o.isPending||(t=o.getOption(s),r=o.getAdjacentOption(t,1).attr(\"data-value\"),o.refreshOptions(o.isFocused&&\"single\"!==a),r&&o.setActiveOption(o.getOption(r))),!n.length||o.isFull()?o.close():o.isPending||o.positionDropdown(),o.updatePlaceholder(),o.trigger(\"item_add\",s,e),o.isPending||o.updateOriginalInput({silent:u})))):\"single\"===a&&o.close()})},removeItem:function(e,t){var n,r,i,o=this;n=e instanceof C?e:o.getItem(e),e=D(n.attr(\"data-value\")),-1!==(r=o.items.indexOf(e))&&(n.remove(),n.hasClass(\"active\")&&(i=o.$activeItems.indexOf(n[0]),o.$activeItems.splice(i,1)),o.items.splice(r,1),o.lastQuery=null,!o.settings.persist&&o.userOptions.hasOwnProperty(e)&&o.removeOption(e,t),r<o.caretPos&&o.setCaret(o.caretPos-1),o.refreshState(),o.updatePlaceholder(),o.updateOriginalInput({silent:t}),o.positionDropdown(),o.trigger(\"item_remove\",e,n))},createItem:function(e,n){var r=this,i=r.caretPos;e=e||C.trim(r.$control_input.val()||\"\");var o=arguments[arguments.length-1];if(\"function\"!=typeof o&&(o=function(){}),\"boolean\"!=typeof n&&(n=!0),!r.canCreate(e))return o(),!1;r.lock();var t=\"function\"==typeof r.settings.create?this.settings.create:function(e){var t={};return t[r.settings.labelField]=e,t[r.settings.valueField]=e,t},a=function(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}(function(e){if(r.unlock(),!e||\"object\"!=typeof e)return o();var t=D(e[r.settings.valueField]);if(\"string\"!=typeof t)return o();r.setTextboxValue(\"\"),r.addOption(e),r.setCaret(i),r.addItem(t),r.refreshOptions(n&&\"single\"!==r.settings.mode),o(e)}),s=t.apply(this,[e,a]);return void 0!==s&&a(s),!0},refreshItems:function(){this.lastQuery=null,this.isSetup&&this.addItem(this.items),this.refreshState(),this.updateOriginalInput()},refreshState:function(){this.refreshValidityState(),this.refreshClasses()},refreshValidityState:function(){if(!this.isRequired)return!1;var e=!this.items.length;this.isInvalid=e,this.$control_input.prop(\"required\",e),this.$input.prop(\"required\",!e)},refreshClasses:function(){var e=this,t=e.isFull(),n=e.isLocked;e.$wrapper.toggleClass(\"rtl\",e.rtl),e.$control.toggleClass(\"focus\",e.isFocused).toggleClass(\"disabled\",e.isDisabled).toggleClass(\"required\",e.isRequired).toggleClass(\"invalid\",e.isInvalid).toggleClass(\"locked\",n).toggleClass(\"full\",t).toggleClass(\"not-full\",!t).toggleClass(\"input-active\",e.isFocused&&!e.isInputHidden).toggleClass(\"dropdown-active\",e.isOpen).toggleClass(\"has-options\",!C.isEmptyObject(e.options)).toggleClass(\"has-items\",0<e.items.length),e.$control_input.data(\"grow\",!t&&!n)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(e){var t,n,r,i,o=this;if(e=e||{},1===o.tagType){for(r=[],t=0,n=o.items.length;t<n;t++)i=o.options[o.items[t]][o.settings.labelField]||\"\",r.push('<option value=\"'+s(o.items[t])+'\" selected=\"selected\">'+s(i)+\"</option>\");r.length||this.$input.attr(\"multiple\")||r.push('<option value=\"\" selected=\"selected\"></option>'),o.$input.html(r.join(\"\"))}else o.$input.val(o.getValue()),o.$input.attr(\"value\",o.$input.val());o.isSetup&&(e.silent||o.trigger(\"change\",o.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var e=this.$control_input;this.items.length?e.removeAttr(\"placeholder\"):e.attr(\"placeholder\",this.settings.placeholder),e.triggerHandler(\"update\",{force:!0})}},open:function(){var e=this;e.isLocked||e.isOpen||\"multi\"===e.settings.mode&&e.isFull()||(e.focus(),e.isOpen=!0,e.refreshState(),e.$dropdown.css({visibility:\"hidden\",display:\"block\"}),e.positionDropdown(),e.$dropdown.css({visibility:\"visible\"}),e.trigger(\"dropdown_open\",e.$dropdown))},close:function(){var e=this,t=e.isOpen;\"single\"===e.settings.mode&&e.items.length&&(e.hideInput(),e.isBlurring||e.$control_input.blur()),e.isOpen=!1,e.$dropdown.hide(),e.setActiveOption(null),e.refreshState(),t&&e.trigger(\"dropdown_close\",e.$dropdown)},positionDropdown:function(){var e=this.$control,t=\"body\"===this.settings.dropdownParent?e.offset():e.position();t.top+=e.outerHeight(!0),this.$dropdown.css({width:e[0].getBoundingClientRect().width,top:t.top,left:t.left})},clear:function(e){var t=this;t.items.length&&(t.$control.children(\":not(input)\").remove(),t.items=[],t.lastQuery=null,t.setCaret(0),t.setActiveItem(null),t.updatePlaceholder(),t.updateOriginalInput({silent:e}),t.refreshState(),t.showInput(),t.trigger(\"clear\"))},insertAtCaret:function(e){var t=Math.min(this.caretPos,this.items.length),n=e[0],r=this.buffer||this.$control[0];0===t?r.insertBefore(n,r.firstChild):r.insertBefore(n,r.childNodes[t]),this.setCaret(t+1)},deleteSelection:function(e){var t,n,r,i,o,a,s,u,l,c=this;if(r=e&&8===e.keyCode?-1:1,i=p(c.$control_input[0]),c.$activeOption&&!c.settings.hideSelected&&(s=c.getAdjacentOption(c.$activeOption,-1).attr(\"data-value\")),o=[],c.$activeItems.length){for(l=c.$control.children(\".active:\"+(0<r?\"last\":\"first\")),a=c.$control.children(\":not(input)\").index(l),0<r&&a++,t=0,n=c.$activeItems.length;t<n;t++)o.push(C(c.$activeItems[t]).attr(\"data-value\"));e&&(e.preventDefault(),e.stopPropagation())}else(c.isFocused||\"single\"===c.settings.mode)&&c.items.length&&(r<0&&0===i.start&&0===i.length?o.push(c.items[c.caretPos-1]):0<r&&i.start===c.$control_input.val().length&&o.push(c.items[c.caretPos]));if(!o.length||\"function\"==typeof c.settings.onDelete&&!1===c.settings.onDelete.apply(c,[o]))return!1;for(void 0!==a&&c.setCaret(a);o.length;)c.removeItem(o.pop());return c.showInput(),c.positionDropdown(),c.refreshOptions(!0),s&&(u=c.getOption(s)).length&&c.setActiveOption(u),!0},advanceSelection:function(e,t){var n,r,i,o,a,s=this;0!==e&&(s.rtl&&(e*=-1),n=0<e?\"last\":\"first\",r=p(s.$control_input[0]),s.isFocused&&!s.isInputHidden?(o=s.$control_input.val().length,(e<0?0===r.start&&0===r.length:r.start===o)&&!o&&s.advanceCaret(e,t)):(a=s.$control.children(\".active:\"+n)).length&&(i=s.$control.children(\":not(input)\").index(a),s.setActiveItem(null),s.setCaret(0<e?i+1:i)))},advanceCaret:function(e,t){var n,r;0!==e&&(n=0<e?\"next\":\"prev\",this.isShiftDown?(r=this.$control_input[n]()).length&&(this.hideInput(),this.setActiveItem(r),t&&t.preventDefault()):this.setCaret(this.caretPos+e))},setCaret:function(e){var t,n,r,i;if(e=\"single\"===this.settings.mode?this.items.length:Math.max(0,Math.min(this.items.length,e)),!this.isPending)for(t=0,n=(r=this.$control.children(\":not(input)\")).length;t<n;t++)i=C(r[t]).detach(),t<e?this.$control_input.before(i):this.$control.append(i);this.caretPos=e},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){this.$input.prop(\"disabled\",!0),this.$control_input.prop(\"disabled\",!0).prop(\"tabindex\",-1),this.isDisabled=!0,this.lock()},enable:function(){this.$input.prop(\"disabled\",!1),this.$control_input.prop(\"disabled\",!1).prop(\"tabindex\",this.tabIndex),this.isDisabled=!1,this.unlock()},destroy:function(){var e=this,t=e.eventNS,n=e.revertSettings;e.trigger(\"destroy\"),e.off(),e.$wrapper.remove(),e.$dropdown.remove(),e.$input.html(\"\").append(n.$children).removeAttr(\"tabindex\").removeClass(\"selectized\").attr({tabindex:n.tabindex}).show(),e.$control_input.removeData(\"grow\"),e.$input.removeData(\"selectize\"),0==--_.count&&_.$testInput&&(_.$testInput.remove(),_.$testInput=void 0),C(window).off(t),C(document).off(t),C(document.body).off(t),delete e.$input[0].selectize},render:function(e,t){var n,r,i=\"\",o=!1,a=this;return\"option\"!==e&&\"item\"!==e||(o=!!(n=D(t[a.settings.valueField]))),o&&(l(a.renderCache[e])||(a.renderCache[e]={}),a.renderCache[e].hasOwnProperty(n))?a.renderCache[e][n]:(i=C(a.settings.render[e].apply(this,[t,s])),\"option\"===e||\"option_create\"===e?t[a.settings.disabledField]||i.attr(\"data-selectable\",\"\"):\"optgroup\"===e&&(r=t[a.settings.optgroupValueField]||\"\",i.attr(\"data-group\",r),t[a.settings.disabledField]&&i.attr(\"data-disabled\",\"\")),\"option\"!==e&&\"item\"!==e||i.attr(\"data-value\",n||\"\"),o&&(a.renderCache[e][n]=i[0]),i[0])},clearCache:function(e){void 0===e?this.renderCache={}:delete this.renderCache[e]},canCreate:function(e){if(!this.settings.create)return!1;var t=this.settings.createFilter;return e.length&&(\"function\"!=typeof t||t.apply(this,[e]))&&(\"string\"!=typeof t||new RegExp(t).test(e))&&(!(t instanceof RegExp)||t.test(e))}}),_.count=0,_.defaults={options:[],optgroups:[],plugins:[],delimiter:\",\",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:\"loading\",dataAttr:\"data-data\",optgroupField:\"optgroup\",valueField:\"value\",labelField:\"text\",disabledField:\"disabled\",optgroupLabelField:\"label\",optgroupValueField:\"value\",lockOptgroupOrder:!1,sortField:\"$order\",searchField:[\"text\"],searchConjunction:\"and\",mode:null,wrapperClass:\"selectize-control\",inputClass:\"selectize-input\",dropdownClass:\"selectize-dropdown\",dropdownContentClass:\"selectize-dropdown-content\",dropdownParent:null,copyClassesToDropdown:!0,render:{}},C.fn.selectize=function(i){var o=C.fn.selectize.defaults,d=C.extend({},o,i),p=d.dataAttr,h=d.labelField,f=d.valueField,g=d.disabledField,m=d.optgroupField,v=d.optgroupLabelField,y=d.optgroupValueField;return this.each(function(){if(!this.selectize){var e=C(this),t=this.tagName.toLowerCase(),n=e.attr(\"placeholder\")||e.attr(\"data-placeholder\");n||d.allowEmptyOption||(n=e.children('option[value=\"\"]').text());var r={placeholder:n,options:[],optgroups:[],items:[]};\"select\"===t?function(e,a){var t,n,r,i,o=a.options,s={},u=function(e){var t=p&&e.attr(p);return\"string\"==typeof t&&t.length?JSON.parse(t):null},l=function(e,t){e=C(e);var n=D(e.val());if(n||d.allowEmptyOption)if(s.hasOwnProperty(n)){if(t){var r=s[n][m];r?C.isArray(r)?r.push(t):s[n][m]=[r,t]:s[n][m]=t}}else{var i=u(e)||{};i[h]=i[h]||e.text(),i[f]=i[f]||n,i[g]=i[g]||e.prop(\"disabled\"),i[m]=i[m]||t,s[n]=i,o.push(i),e.is(\":selected\")&&a.items.push(n)}},c=function(e){var t,n,r,i,o;for((r=(e=C(e)).attr(\"label\"))&&((i=u(e)||{})[v]=r,i[y]=r,i[g]=e.prop(\"disabled\"),a.optgroups.push(i)),t=0,n=(o=C(\"option\",e)).length;t<n;t++)l(o[t],r)};for(a.maxItems=e.attr(\"multiple\")?null:1,t=0,n=(i=e.children()).length;t<n;t++)\"optgroup\"===(r=i[t].tagName.toLowerCase())?c(i[t]):\"option\"===r&&l(i[t])}(e,r):function(e,t){var n,r,i,o,a=e.attr(p);if(a)for(t.options=JSON.parse(a),n=0,r=t.options.length;n<r;n++)t.items.push(t.options[n][f]);else{var s=C.trim(e.val()||\"\");if(!d.allowEmptyOption&&!s.length)return;for(n=0,r=(i=s.split(d.delimiter)).length;n<r;n++)(o={})[h]=i[n],o[f]=i[n],t.options.push(o);t.items=i}}(e,r),new _(e,C.extend(!0,{},o,r,i))}})},C.fn.selectize.defaults=_.defaults,C.fn.selectize.support={validity:b},_.define(\"drag_drop\",function(e){if(!C.fn.sortable)throw new Error('The \"drag_drop\" plugin requires jQuery UI \"sortable\".');if(\"multi\"===this.settings.mode){var t,n,r,i=this;i.lock=(t=i.lock,function(){var e=i.$control.data(\"sortable\");return e&&e.disable(),t.apply(i,arguments)}),i.unlock=(n=i.unlock,function(){var e=i.$control.data(\"sortable\");return e&&e.enable(),n.apply(i,arguments)}),i.setup=(r=i.setup,function(){r.apply(this,arguments);var n=i.$control.sortable({items:\"[data-value]\",forcePlaceholderSize:!0,disabled:i.isLocked,start:function(e,t){t.placeholder.css(\"width\",t.helper.css(\"width\")),n.css({overflow:\"visible\"})},stop:function(){n.css({overflow:\"hidden\"});var e=i.$activeItems?i.$activeItems.slice():null,t=[];n.children(\"[data-value]\").each(function(){t.push(C(this).attr(\"data-value\"))}),i.setValue(t),i.setActiveItem(e)}})})}}),_.define(\"dropdown_header\",function(e){var t,n=this;e=C.extend({title:\"Untitled\",headerClass:\"selectize-dropdown-header\",titleRowClass:\"selectize-dropdown-header-title\",labelClass:\"selectize-dropdown-header-label\",closeClass:\"selectize-dropdown-header-close\",html:function(e){return'<div class=\"'+e.headerClass+'\"><div class=\"'+e.titleRowClass+'\"><span class=\"'+e.labelClass+'\">'+e.title+'</span><a href=\"javascript:void(0)\" class=\"'+e.closeClass+'\">&times;</a></div></div>'}},e),n.setup=(t=n.setup,function(){t.apply(n,arguments),n.$dropdown_header=C(e.html(e)),n.$dropdown.prepend(n.$dropdown_header)})}),_.define(\"optgroup_columns\",function(s){var o,u=this;s=C.extend({equalizeWidth:!0,equalizeHeight:!0},s),this.getAdjacentOption=function(e,t){var n=e.closest(\"[data-group]\").find(\"[data-selectable]\"),r=n.index(e)+t;return 0<=r&&r<n.length?n.eq(r):C()},this.onKeyDown=(o=u.onKeyDown,function(e){var t,n,r,i;return!this.isOpen||37!==e.keyCode&&39!==e.keyCode?o.apply(this,arguments):(u.ignoreHover=!0,t=(i=this.$activeOption.closest(\"[data-group]\")).find(\"[data-selectable]\").index(this.$activeOption),void((n=(r=(i=37===e.keyCode?i.prev(\"[data-group]\"):i.next(\"[data-group]\")).find(\"[data-selectable]\")).eq(Math.min(r.length-1,t))).length&&this.setActiveOption(n)))});var l=function(){var e,t=l.width,n=document;return void 0===t&&((e=n.createElement(\"div\")).innerHTML='<div style=\"width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;\"><div style=\"width:1px;height:100px;\"></div></div>',e=e.firstChild,n.body.appendChild(e),t=l.width=e.offsetWidth-e.clientWidth,n.body.removeChild(e)),t},e=function(){var e,t,n,r,i,o,a;if((t=(a=C(\"[data-group]\",u.$dropdown_content)).length)&&u.$dropdown_content.width()){if(s.equalizeHeight){for(e=n=0;e<t;e++)n=Math.max(n,a.eq(e).height());a.css({height:n})}s.equalizeWidth&&(o=u.$dropdown_content.innerWidth()-l(),r=Math.round(o/t),a.css({width:r}),1<t&&(i=o-r*(t-1),a.eq(t-1).css({width:i})))}};(s.equalizeHeight||s.equalizeWidth)&&(t.after(this,\"positionDropdown\",e),t.after(this,\"refreshOptions\",e))}),_.define(\"remove_button\",function(e){e=C.extend({label:\"&times;\",title:\"Remove\",className:\"remove\",append:!0},e);var n,r,i,o,a;\"single\"!==this.settings.mode?(o=n=this,a='<a href=\"javascript:void(0)\" class=\"'+(r=e).className+'\" tabindex=\"-1\" title=\"'+s(r.title)+'\">'+r.label+\"</a>\",n.setup=(i=o.setup,function(){if(r.append){var t=o.settings.render.item;o.settings.render.item=function(e){return function(e,t){var n=e.search(/(<\\/[^>]+>\\s*)$/);return e.substring(0,n)+t+e.substring(n)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on(\"click\",\".\"+r.className,function(e){if(e.preventDefault(),!o.isLocked){var t=C(e.currentTarget).parent();o.setActiveItem(t),o.deleteSelection()&&o.setCaret(o.items.length)}})})):function(n,r){r.className=\"remove-single\";var i,o=n,a='<a href=\"javascript:void(0)\" class=\"'+r.className+'\" tabindex=\"-1\" title=\"'+s(r.title)+'\">'+r.label+\"</a>\";n.setup=(i=o.setup,function(){if(r.append){var e=C(o.$input.context).attr(\"id\"),t=(C(\"#\"+e),o.settings.render.item);o.settings.render.item=function(e){return function(e,t){return C(\"<span>\").append(e).append(t)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on(\"click\",\".\"+r.className,function(e){e.preventDefault(),o.isLocked||o.clear()})})}(this,e)}),_.define(\"restore_on_backspace\",function(r){var i,e=this;r.text=r.text||function(e){return e[this.settings.labelField]},this.onKeyDown=(i=e.onKeyDown,function(e){var t,n;return 8===e.keyCode&&\"\"===this.$control_input.val()&&!this.$activeItems.length&&0<=(t=this.caretPos-1)&&t<this.items.length?(n=this.options[this.items[t]],this.deleteSelection(e)&&(this.setTextboxValue(r.text.apply(this,[n])),this.refreshOptions(!0)),void e.preventDefault()):i.apply(this,arguments)})}),_}),angular.module(\"selectize\",[]).value(\"selectizeConfig\",{}).directive(\"selectize\",[\"selectizeConfig\",function(p){return{restrict:\"EA\",require:\"^ngModel\",scope:{ngModel:\"=\",config:\"=?\",options:\"=?\",ngDisabled:\"=\",ngRequired:\"&\"},link:function(n,e,t,r){Selectize.defaults.maxItems=null;var i,o=angular.extend({},Selectize.defaults,p,n.config);function a(e){e?i.disable():i.enable()}r.$isEmpty=function(e){return null==e||!e.length};function s(e){return e?(e=angular.isArray(e)||angular.isObject(e)?e:[e],$.map(e,function(e){return\"string\"==typeof e?function(e){var t={};return t[o.labelField]=e,t[o.valueField]=e,t}(e):e})):[]}function u(){!function(){var e=(n.ngRequired()||t.required||o.required)&&r.$isEmpty(n.ngModel);r.$setValidity(\"required\",!e)}(),i.$control.toggleClass(\"ng-valid\",r.$valid),i.$control.toggleClass(\"ng-invalid\",r.$invalid),i.$control.toggleClass(\"ng-dirty\",r.$dirty),i.$control.toggleClass(\"ng-pristine\",r.$pristine),angular.equals(i.items,n.ngModel)||(i.addOption(s(n.ngModel)),i.setValue(n.ngModel))}var l=o.onChange,c=o.onOptionAdd;o.onChange=function(){n.disableOnChange||(angular.equals(i.items,n.ngModel)||n.$evalAsync(function(){var e=angular.copy(i.items);1==o.maxItems&&(e=e[0]),r.$setViewValue(e)}),l&&l.apply(this,arguments))},o.onOptionAdd=function(e,t){-1===n.options.indexOf(t)&&(n.options.push(t),c&&c.apply(this,arguments))},n.options?n.options.splice(0,n.options.length,s(n.options)):n.options=s(angular.copy(n.ngModel));var d=o.onInitialize;o.onInitialize=function(){(i=e[0].selectize).addOption(n.options),i.setValue(n.ngModel),d&&d(i),n.$watch(\"options\",function(){n.disableOnChange=!0,i.clearOptions(),i.addOption(n.options),i.setValue(n.ngModel),n.disableOnChange=!1},!0),n.$watchCollection(\"ngModel\",u),n.$watch(\"ngDisabled\",a)},e.selectize(o),e.on(\"$destroy\",function(){i&&(i.destroy(),e=null)})}}}]),function(e,t){\"function\"==typeof define&&define.amd?define([\"jquery\"],t):\"undefined\"!=typeof exports?t(require(\"jquery\")):(t(e.jquery),e.bootstrapSwitch={})}(this,function(e){\"use strict\";var t,n=(t=e)&&t.__esModule?t:{default:t},r=Object.assign||function(e){for(var t,n=1;n<arguments.length;n++)for(var r in t=arguments[n])Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e},i=function(){function r(e,t){for(var n,r=0;r<t.length;r++)(n=t[r]).enumerable=n.enumerable||!1,n.configurable=!0,\"value\"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}return function(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}}(),s=n.default||window.jQuery||window.$,u=function(){function t(r){var i=this,e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};(function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")})(this,t),this.$element=s(r),this.options=s.extend({},s.fn.bootstrapSwitch.defaults,this._getElementOptions(),e),this.prevOptions={},this.$wrapper=s(\"<div>\",{class:function(){var e=[];return e.push(i.options.state?\"on\":\"off\"),i.options.size&&e.push(i.options.size),i.options.disabled&&e.push(\"disabled\"),i.options.readonly&&e.push(\"readonly\"),i.options.indeterminate&&e.push(\"indeterminate\"),i.options.inverse&&e.push(\"inverse\"),i.$element.attr(\"id\")&&e.push(\"id-\"+i.$element.attr(\"id\")),e.map(i._getClass.bind(i)).concat([i.options.baseClass],i._getClasses(i.options.wrapperClass)).join(\" \")}}),this.$container=s(\"<div>\",{class:this._getClass(\"container\")}),this.$on=s(\"<span>\",{html:this.options.onText,class:this._getClass(\"handle-on\")+\" \"+this._getClass(this.options.onColor)}),this.$off=s(\"<span>\",{html:this.options.offText,class:this._getClass(\"handle-off\")+\" \"+this._getClass(this.options.offColor)}),this.$label=s(\"<span>\",{html:this.options.labelText,class:this._getClass(\"label\")}),this.$element.on(\"init.bootstrapSwitch\",this.options.onInit.bind(this,r)),this.$element.on(\"switchChange.bootstrapSwitch\",function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];!1===i.options.onSwitchChange.apply(r,t)&&(i.$element.is(\":radio\")?s('[name=\"'+i.$element.attr(\"name\")+'\"]').trigger(\"previousState.bootstrapSwitch\",!0):i.$element.trigger(\"previousState.bootstrapSwitch\",!0))}),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop(\"indeterminate\",!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger(\"init.bootstrapSwitch\",this.options.state)}return i(t,[{key:\"setPrevOptions\",value:function(){this.prevOptions=r({},this.options)}},{key:\"state\",value:function(e,t){return void 0===e?this.options.state:(this.options.disabled||this.options.readonly||this.options.state&&!this.options.radioAllOff&&this.$element.is(\":radio\")||(this.$element.is(\":radio\")?s('[name=\"'+this.$element.attr(\"name\")+'\"]').trigger(\"setPreviousOptions.bootstrapSwitch\"):this.$element.trigger(\"setPreviousOptions.bootstrapSwitch\"),this.options.indeterminate&&this.indeterminate(!1),this.$element.prop(\"checked\",!!e).trigger(\"change.bootstrapSwitch\",t)),this.$element)}},{key:\"toggleState\",value:function(e){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop(\"checked\",!this.options.state).trigger(\"change.bootstrapSwitch\",e)}},{key:\"size\",value:function(e){return void 0===e?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this._getClass(this.options.size)),e&&this.$wrapper.addClass(this._getClass(e)),this._width(),this._containerPosition(),this.options.size=e,this.$element)}},{key:\"animate\",value:function(e){return void 0===e?this.options.animate:this.options.animate===!!e?this.$element:this.toggleAnimate()}},{key:\"toggleAnimate\",value:function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this._getClass(\"animate\")),this.$element}},{key:\"disabled\",value:function(e){return void 0===e?this.options.disabled:this.options.disabled===!!e?this.$element:this.toggleDisabled()}},{key:\"toggleDisabled\",value:function(){return this.options.disabled=!this.options.disabled,this.$element.prop(\"disabled\",this.options.disabled),this.$wrapper.toggleClass(this._getClass(\"disabled\")),this.$element}},{key:\"readonly\",value:function(e){return void 0===e?this.options.readonly:this.options.readonly===!!e?this.$element:this.toggleReadonly()}},{key:\"toggleReadonly\",value:function(){return this.options.readonly=!this.options.readonly,this.$element.prop(\"readonly\",this.options.readonly),this.$wrapper.toggleClass(this._getClass(\"readonly\")),this.$element}},{key:\"indeterminate\",value:function(e){return void 0===e?this.options.indeterminate:this.options.indeterminate===!!e?this.$element:this.toggleIndeterminate()}},{key:\"toggleIndeterminate\",value:function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop(\"indeterminate\",this.options.indeterminate),this.$wrapper.toggleClass(this._getClass(\"indeterminate\")),this._containerPosition(),this.$element}},{key:\"inverse\",value:function(e){return void 0===e?this.options.inverse:this.options.inverse===!!e?this.$element:this.toggleInverse()}},{key:\"toggleInverse\",value:function(){this.$wrapper.toggleClass(this._getClass(\"inverse\"));var e=this.$on.clone(!0),t=this.$off.clone(!0);return this.$on.replaceWith(t),this.$off.replaceWith(e),this.$on=t,this.$off=e,this.options.inverse=!this.options.inverse,this.$element}},{key:\"onColor\",value:function(e){return void 0===e?this.options.onColor:(this.options.onColor&&this.$on.removeClass(this._getClass(this.options.onColor)),this.$on.addClass(this._getClass(e)),this.options.onColor=e,this.$element)}},{key:\"offColor\",value:function(e){return void 0===e?this.options.offColor:(this.options.offColor&&this.$off.removeClass(this._getClass(this.options.offColor)),this.$off.addClass(this._getClass(e)),this.options.offColor=e,this.$element)}},{key:\"onText\",value:function(e){return void 0===e?this.options.onText:(this.$on.html(e),this._width(),this._containerPosition(),this.options.onText=e,this.$element)}},{key:\"offText\",value:function(e){return void 0===e?this.options.offText:(this.$off.html(e),this._width(),this._containerPosition(),this.options.offText=e,this.$element)}},{key:\"labelText\",value:function(e){return void 0===e?this.options.labelText:(this.$label.html(e),this._width(),this.options.labelText=e,this.$element)}},{key:\"handleWidth\",value:function(e){return void 0===e?this.options.handleWidth:(this.options.handleWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:\"labelWidth\",value:function(e){return void 0===e?this.options.labelWidth:(this.options.labelWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:\"baseClass\",value:function(){return this.options.baseClass}},{key:\"wrapperClass\",value:function(e){return void 0===e?this.options.wrapperClass:(e||(e=s.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(\" \")),this.$wrapper.addClass(this._getClasses(e).join(\" \")),this.options.wrapperClass=e,this.$element)}},{key:\"radioAllOff\",value:function(e){if(void 0===e)return this.options.radioAllOff;var t=!!e;return this.options.radioAllOff===t||(this.options.radioAllOff=t),this.$element}},{key:\"onInit\",value:function(e){return void 0===e?this.options.onInit:(e||(e=s.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=e,this.$element)}},{key:\"onSwitchChange\",value:function(e){return void 0===e?this.options.onSwitchChange:(e||(e=s.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=e,this.$element)}},{key:\"destroy\",value:function(){var e=this.$element.closest(\"form\");return e.length&&e.off(\"reset.bootstrapSwitch\").removeData(\"bootstrap-switch\"),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off(\".bootstrapSwitch\").removeData(\"bootstrap-switch\"),this.$element}},{key:\"_getElementOptions\",value:function(){return{state:this.$element.is(\":checked\"),size:this.$element.data(\"size\"),animate:this.$element.data(\"animate\"),disabled:this.$element.is(\":disabled\"),readonly:this.$element.is(\"[readonly]\"),indeterminate:this.$element.data(\"indeterminate\"),inverse:this.$element.data(\"inverse\"),radioAllOff:this.$element.data(\"radio-all-off\"),onColor:this.$element.data(\"on-color\"),offColor:this.$element.data(\"off-color\"),onText:this.$element.data(\"on-text\"),offText:this.$element.data(\"off-text\"),labelText:this.$element.data(\"label-text\"),handleWidth:this.$element.data(\"handle-width\"),labelWidth:this.$element.data(\"label-width\"),baseClass:this.$element.data(\"base-class\"),wrapperClass:this.$element.data(\"wrapper-class\")}}},{key:\"_width\",value:function(){var n=this,e=this.$on.add(this.$off).add(this.$label).css(\"width\",\"\"),r=\"auto\"===this.options.handleWidth?Math.round(Math.max(this.$on.width(),this.$off.width())):this.options.handleWidth;return e.width(r),this.$label.width(function(e,t){return\"auto\"===n.options.labelWidth?t<r?r:t:n.options.labelWidth}),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)}},{key:\"_containerPosition\",value:function(){var t=this,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:this.options.state;arguments[1];this.$container.css(\"margin-left\",function(){var e=[0,\"-\"+t._handleWidth+\"px\"];return t.options.indeterminate?\"-\"+t._handleWidth/2+\"px\":n?t.options.inverse?e[1]:e[0]:t.options.inverse?e[0]:e[1]})}},{key:\"_init\",value:function(){var e=this,t=function(){e.setPrevOptions(),e._width(),e._containerPosition(),setTimeout(function(){if(e.options.animate)return e.$wrapper.addClass(e._getClass(\"animate\"))},50)};if(this.$wrapper.is(\":visible\"))t();else var n=window.setInterval(function(){if(e.$wrapper.is(\":visible\"))return t(),window.clearInterval(n)},50)}},{key:\"_elementHandlers\",value:function(){var r=this;return this.$element.on({\"setPreviousOptions.bootstrapSwitch\":this.setPrevOptions.bind(this),\"previousState.bootstrapSwitch\":function(){r.options=r.prevOptions,r.options.indeterminate&&r.$wrapper.addClass(r._getClass(\"indeterminate\")),r.$element.prop(\"checked\",r.options.state).trigger(\"change.bootstrapSwitch\",!0)},\"change.bootstrapSwitch\":function(e,t){e.preventDefault(),e.stopImmediatePropagation();var n=r.$element.is(\":checked\");r._containerPosition(n),n===r.options.state||(r.options.state=n,r.$wrapper.toggleClass(r._getClass(\"off\")).toggleClass(r._getClass(\"on\")),!t&&(r.$element.is(\":radio\")&&s('[name=\"'+r.$element.attr(\"name\")+'\"]').not(r.$element).prop(\"checked\",!1).trigger(\"change.bootstrapSwitch\",!0),r.$element.trigger(\"switchChange.bootstrapSwitch\",[n])))},\"focus.bootstrapSwitch\":function(e){e.preventDefault(),r.$wrapper.addClass(r._getClass(\"focused\"))},\"blur.bootstrapSwitch\":function(e){e.preventDefault(),r.$wrapper.removeClass(r._getClass(\"focused\"))},\"keydown.bootstrapSwitch\":function(e){!e.which||r.options.disabled||r.options.readonly||(37===e.which||39===e.which)&&(e.preventDefault(),e.stopImmediatePropagation(),r.state(39===e.which))}})}},{key:\"_handleHandlers\",value:function(){var t=this;return this.$on.on(\"click.bootstrapSwitch\",function(e){return e.preventDefault(),e.stopPropagation(),t.state(!1),t.$element.trigger(\"focus.bootstrapSwitch\")}),this.$off.on(\"click.bootstrapSwitch\",function(e){return e.preventDefault(),e.stopPropagation(),t.state(!0),t.$element.trigger(\"focus.bootstrapSwitch\")})}},{key:\"_labelHandlers\",value:function(){var n=this;this.$label.on({click:function(e){e.stopPropagation()},\"mousedown.bootstrapSwitch touchstart.bootstrapSwitch\":function(e){n._dragStart||n.options.disabled||n.options.readonly||(e.preventDefault(),e.stopPropagation(),n._dragStart=(e.pageX||e.originalEvent.touches[0].pageX)-parseInt(n.$container.css(\"margin-left\"),10),n.options.animate&&n.$wrapper.removeClass(n._getClass(\"animate\")),n.$element.trigger(\"focus.bootstrapSwitch\"))},\"mousemove.bootstrapSwitch touchmove.bootstrapSwitch\":function(e){if(null!=n._dragStart){var t=(e.pageX||e.originalEvent.touches[0].pageX)-n._dragStart;e.preventDefault(),t<-n._handleWidth||0<t||(n._dragEnd=t,n.$container.css(\"margin-left\",n._dragEnd+\"px\"))}},\"mouseup.bootstrapSwitch touchend.bootstrapSwitch\":function(e){if(n._dragStart){if(e.preventDefault(),n.options.animate&&n.$wrapper.addClass(n._getClass(\"animate\")),n._dragEnd){var t=n._dragEnd>-n._handleWidth/2;n._dragEnd=!1,n.state(n.options.inverse?!t:t)}else n.state(!n.options.state);n._dragStart=!1}},\"mouseleave.bootstrapSwitch\":function(){n.$label.trigger(\"mouseup.bootstrapSwitch\")}})}},{key:\"_externalLabelHandler\",value:function(){var t=this,n=this.$element.closest(\"label\");n.on(\"click\",function(e){e.preventDefault(),e.stopImmediatePropagation(),e.target===n[0]&&t.toggleState()})}},{key:\"_formHandler\",value:function(){var e=this.$element.closest(\"form\");e.data(\"bootstrap-switch\")||e.on(\"reset.bootstrapSwitch\",function(){window.setTimeout(function(){e.find(\"input\").filter(function(){return s(this).data(\"bootstrap-switch\")}).each(function(){return s(this).bootstrapSwitch(\"state\",this.checked)})},1)}).data(\"bootstrap-switch\",!0)}},{key:\"_getClass\",value:function(e){return this.options.baseClass+\"-\"+e}},{key:\"_getClasses\",value:function(e){return s.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),t}();s.fn.bootstrapSwitch=function(o){for(var e=arguments.length,a=Array(1<e?e-1:0),t=1;t<e;t++)a[t-1]=arguments[t];return Array.prototype.reduce.call(this,function(e,t){var n=s(t),r=n.data(\"bootstrap-switch\"),i=r||new u(t,o);return r||n.data(\"bootstrap-switch\",i),\"string\"==typeof o?i[o].apply(i,a):e},this)},s.fn.bootstrapSwitch.Constructor=u,s.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:\"primary\",offColor:\"default\",onText:\"ON\",offText:\"OFF\",labelText:\"&nbsp\",handleWidth:\"auto\",labelWidth:\"auto\",baseClass:\"bootstrap-switch\",wrapperClass:\"wrapper\",onInit:function(){},onSwitchChange:function(){}}}),function(e,t){\"undefined\"!=typeof module&&module.exports?(\"undefined\"==typeof angular?t(require(\"angular\")):t(angular),module.exports=\"ngDialog\"):\"function\"==typeof define&&define.amd?define([\"angular\"],t):t(e.angular)}(this,function(O){\"use strict\";var e=O.module(\"ngDialog\",[]),T=O.element,t=O.isDefined,n=(document.body||document.documentElement).style,l=t(n.animation)||t(n.WebkitAnimation)||t(n.MozAnimation)||t(n.MsAnimation)||t(n.OAnimation),c=\"animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend\",r={html:!1,body:!1},A={},E=[],P=!1,M=!1;return e.provider(\"ngDialog\",function(){var S=this.defaults={className:\"ngdialog-theme-default\",appendClassName:\"\",disableAnimation:!1,plain:!1,showClose:!0,closeByDocument:!0,closeByEscape:!0,closeByNavigation:!1,appendTo:!1,preCloseCallback:!1,overlay:!0,cache:!0,trapFocus:!0,preserveFocus:!0,ariaAuto:!0,ariaRole:null,ariaLabelledById:null,ariaLabelledBySelector:null,ariaDescribedById:null,ariaDescribedBySelector:null,bodyClassName:\"ngdialog-open\",width:null,height:null};this.setForceHtmlReload=function(e){r.html=e||!1},this.setForceBodyReload=function(e){r.body=e||!1},this.setDefaults=function(e){O.extend(S,e)},this.setOpenOnePerName=function(e){M=e||!1};var C,k=0,D=0,x={};this.$get=[\"$document\",\"$templateCache\",\"$compile\",\"$q\",\"$http\",\"$rootScope\",\"$timeout\",\"$window\",\"$controller\",\"$injector\",function(n,o,g,a,s,m,v,y,w,u){var b=[],$={onDocumentKeydown:function(e){27===e.keyCode&&_.close(\"$escape\")},activate:function(e){e.data(\"$ngDialogOptions\").trapFocus&&(e.on(\"keydown\",$.onTrapFocusKeydown),b.body.on(\"keydown\",$.onTrapFocusKeydown))},deactivate:function(e){e.off(\"keydown\",$.onTrapFocusKeydown),b.body.off(\"keydown\",$.onTrapFocusKeydown)},deactivateAll:function(e){O.forEach(e,function(e){var t=O.element(e);$.deactivate(t)})},setBodyPadding:function(e){var t=parseInt(b.body.css(\"padding-right\")||0,10);b.body.css(\"padding-right\",t+e+\"px\"),b.body.data(\"ng-dialog-original-padding\",t),m.$broadcast(\"ngDialog.setPadding\",e)},resetBodyPadding:function(){var e=b.body.data(\"ng-dialog-original-padding\");e?b.body.css(\"padding-right\",e+\"px\"):b.body.css(\"padding-right\",\"\"),m.$broadcast(\"ngDialog.setPadding\",0)},performCloseDialog:function(e,t){var n=e.data(\"$ngDialogOptions\"),r=e.attr(\"id\"),i=A[r];if(i){if(void 0!==y.Hammer){var o=i.hammerTime;o.off(\"tap\",C),o.destroy&&o.destroy(),delete i.hammerTime}else e.unbind(\"click\");1===D&&b.body.unbind(\"keydown\",$.onDocumentKeydown),e.hasClass(\"ngdialog-closing\")||(D-=1);var a=e.data(\"$ngDialogPreviousFocus\");a&&a.focus&&a.focus(),m.$broadcast(\"ngDialog.closing\",e,t),D=D<0?0:D,l&&!n.disableAnimation?(i.$destroy(),e.unbind(c).bind(c,function(){$.closeDialogElement(e,t)}).addClass(\"ngdialog-closing\")):(i.$destroy(),$.closeDialogElement(e,t)),x[r]&&(x[r].resolve({id:r,value:t,$dialog:e,remainingDialogs:D}),delete x[r]),A[r]&&delete A[r],E.splice(E.indexOf(r),1),E.length||(b.body.unbind(\"keydown\",$.onDocumentKeydown),P=!1)}},closeDialogElement:function(e,t){var n=e.data(\"$ngDialogOptions\");e.remove(),0===D&&(b.html.removeClass(n.bodyClassName),b.body.removeClass(n.bodyClassName),$.resetBodyPadding()),m.$broadcast(\"ngDialog.closed\",e,t)},closeDialog:function(e,t){var n=e.data(\"$ngDialogPreCloseCallback\");if(n&&O.isFunction(n)){var r=n.call(e,t);if(O.isObject(r))r.closePromise?r.closePromise.then(function(){$.performCloseDialog(e,t)},function(){return!1}):r.then(function(){$.performCloseDialog(e,t)},function(){return!1});else{if(!1===r)return!1;$.performCloseDialog(e,t)}}else $.performCloseDialog(e,t)},onTrapFocusKeydown:function(e){var t,n=O.element(e.currentTarget);if(n.hasClass(\"ngdialog\"))t=n;else if(null===(t=$.getActiveDialog()))return;var r=9===e.keyCode,i=!0===e.shiftKey;r&&$.handleTab(t,e,i)},handleTab:function(e,t,n){var r=$.getFocusableElements(e);if(0!==r.length){var i=document.activeElement,o=Array.prototype.indexOf.call(r,i),a=-1===o,s=0===o,u=o===r.length-1,l=!1;n?(a||s)&&(r[r.length-1].focus(),l=!0):(a||u)&&(r[0].focus(),l=!0),l&&(t.preventDefault(),t.stopPropagation())}else document.activeElement&&document.activeElement.blur&&document.activeElement.blur()},autoFocus:function(e){var t=e[0],n=t.querySelector(\"*[autofocus]\");if(null===n||(n.focus(),document.activeElement!==n)){var r=$.getFocusableElements(e);if(0<r.length)r[0].focus();else{var i=$.filterVisibleElements(t.querySelectorAll(\"h1,h2,h3,h4,h5,h6,p,span\"));if(0<i.length){var o=i[0];T(o).attr(\"tabindex\",\"-1\").css(\"outline\",\"0\"),o.focus()}}}},getFocusableElements:function(e){var t=e[0].querySelectorAll(\"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]\"),n=$.filterTabbableElements(t);return $.filterVisibleElements(n)},filterTabbableElements:function(e){for(var t=[],n=0;n<e.length;n++){var r=e[n];\"-1\"!==T(r).attr(\"tabindex\")&&t.push(r)}return t},filterVisibleElements:function(e){for(var t=[],n=0;n<e.length;n++){var r=e[n];(0<r.offsetWidth||0<r.offsetHeight)&&t.push(r)}return t},getActiveDialog:function(){var e=document.querySelectorAll(\".ngdialog\");return 0===e.length?null:T(e[e.length-1])},applyAriaAttributes:function(e,t){if(t.ariaAuto){if(!t.ariaRole){var n=0<$.getFocusableElements(e).length?\"dialog\":\"alertdialog\";t.ariaRole=n}t.ariaLabelledBySelector||(t.ariaLabelledBySelector=\"h1,h2,h3,h4,h5,h6\"),t.ariaDescribedBySelector||(t.ariaDescribedBySelector=\"article,section,p\")}t.ariaRole&&e.attr(\"role\",t.ariaRole),$.applyAriaAttribute(e,\"aria-labelledby\",t.ariaLabelledById,t.ariaLabelledBySelector),$.applyAriaAttribute(e,\"aria-describedby\",t.ariaDescribedById,t.ariaDescribedBySelector)},applyAriaAttribute:function(e,t,n,r){if(n&&e.attr(t,n),r){var i=e.attr(\"id\"),o=e[0].querySelector(r);if(!o)return;var a=i+\"-\"+t;return T(o).attr(\"id\",a),e.attr(t,a),a}},detectUIRouter:function(){try{return O.module(\"ui.router\"),!0}catch(e){return!1}},getRouterLocationEventName:function(){return $.detectUIRouter()?\"$stateChangeStart\":\"$locationChangeStart\"}},_={__PRIVATE__:$,open:function(e){var l=null;if(e=e||{},!(M&&e.name&&(l=e.name.toLowerCase().replace(/\\s/g,\"-\")+\"-dialog\",this.isOpen(l)))){var t,c,d,p,h,f=O.copy(S),n=++k;l=l||\"ngdialog\"+n,E.push(l),void 0!==f.data&&(void 0===e.data&&(e.data={}),e.data=O.merge(O.copy(f.data),e.data)),O.extend(f,e),x[l]=t=a.defer(),A[l]=c=O.isObject(f.scope)?f.scope.$new():m.$new();var r=O.extend({},f.resolve);return O.forEach(r,function(e,t){r[t]=O.isString(e)?u.get(e):u.invoke(e,null,null,t)}),a.all({template:function(e){if(!e)return\"Empty template\";if(O.isString(e)&&f.plain)return e;return\"boolean\"!=typeof f.cache||f.cache?i(e,{cache:o}):i(e,{cache:!1})}(f.template||f.templateUrl),locals:a.all(r)}).then(function(e){var t=e.template,n=e.locals;f.showClose&&(t+='<div class=\"ngdialog-close\"></div>');var r,i=f.overlay?\"\":\" ngdialog-no-overlay\";if((d=T('<div id=\"'+l+'\" class=\"ngdialog'+i+'\"></div>')).html(f.overlay?'<div class=\"ngdialog-overlay\"></div><div class=\"ngdialog-content\" role=\"document\">'+t+\"</div>\":'<div class=\"ngdialog-content\" role=\"document\">'+t+\"</div>\"),d.data(\"$ngDialogOptions\",f),c.ngDialogId=l,f.data&&O.isString(f.data)){var o=f.data.replace(/^\\s*/,\"\")[0];c.ngDialogData=\"{\"===o||\"[\"===o?O.fromJson(f.data):new String(f.data),c.ngDialogData.ngDialogId=l}else f.data&&O.isObject(f.data)&&(c.ngDialogData=f.data,c.ngDialogData.ngDialogId=l);(f.className&&d.addClass(f.className),f.appendClassName&&d.addClass(f.appendClassName),f.width&&(h=d[0].querySelector(\".ngdialog-content\"),O.isString(f.width)?h.style.width=f.width:h.style.width=f.width+\"px\"),f.height&&(h=d[0].querySelector(\".ngdialog-content\"),O.isString(f.height)?h.style.height=f.height:h.style.height=f.height+\"px\"),f.disableAnimation&&d.addClass(\"ngdialog-disabled-animation\"),p=f.appendTo&&O.isString(f.appendTo)?O.element(document.querySelector(f.appendTo)):b.body,$.applyAriaAttributes(d,f),f.preCloseCallback)&&(O.isFunction(f.preCloseCallback)?r=f.preCloseCallback:O.isString(f.preCloseCallback)&&c&&(O.isFunction(c[f.preCloseCallback])?r=c[f.preCloseCallback]:c.$parent&&O.isFunction(c.$parent[f.preCloseCallback])?r=c.$parent[f.preCloseCallback]:m&&O.isFunction(m[f.preCloseCallback])&&(r=m[f.preCloseCallback])),r&&d.data(\"$ngDialogPreCloseCallback\",r));if(c.closeThisDialog=function(e){$.closeDialog(d,e)},f.controller&&(O.isString(f.controller)||O.isArray(f.controller)||O.isFunction(f.controller))){var a;f.controllerAs&&O.isString(f.controllerAs)&&(a=f.controllerAs);var s=w(f.controller,O.extend(n,{$scope:c,$element:d}),!0,a);f.bindToController&&O.extend(s.instance,{ngDialogId:c.ngDialogId,ngDialogData:c.ngDialogData,closeThisDialog:c.closeThisDialog,confirm:c.confirm}),\"function\"==typeof s?d.data(\"$ngDialogControllerController\",s()):d.data(\"$ngDialogControllerController\",s)}if(v(function(){var e=document.querySelectorAll(\".ngdialog\");$.deactivateAll(e),g(d)(c);var t=y.innerWidth-b.body.prop(\"clientWidth\");b.html.addClass(f.bodyClassName),b.body.addClass(f.bodyClassName);var n=t-(y.innerWidth-b.body.prop(\"clientWidth\"));0<n&&$.setBodyPadding(n),p.append(d),$.activate(d),f.trapFocus&&$.autoFocus(d),f.name?m.$broadcast(\"ngDialog.opened\",{dialog:d,name:f.name}):m.$broadcast(\"ngDialog.opened\",d)}),P||(b.body.bind(\"keydown\",$.onDocumentKeydown),P=!0),f.closeByNavigation){var u=$.getRouterLocationEventName();m.$on(u,function(e){!1===$.closeDialog(d)&&e.preventDefault()})}(f.preserveFocus&&d.data(\"$ngDialogPreviousFocus\",document.activeElement),C=function(e){var t=!!f.closeByDocument&&T(e.target).hasClass(\"ngdialog-overlay\"),n=T(e.target).hasClass(\"ngdialog-close\");(t||n)&&_.close(d.attr(\"id\"),n?\"$closeButton\":\"$document\")},void 0!==y.Hammer)?(c.hammerTime=y.Hammer(d[0])).on(\"tap\",C):d.bind(\"click\",C);return D+=1,_}),{id:l,closePromise:t.promise,close:function(e){$.closeDialog(d,e)}}}function i(t,e){return(e=e||{}).headers=e.headers||{},O.extend(e.headers,{Accept:\"text/html\"}),m.$broadcast(\"ngDialog.templateLoading\",t),s.get(t,e).then(function(e){return m.$broadcast(\"ngDialog.templateLoaded\",t),e.data||\"\"})}},openConfirm:function(e){var n=a.defer(),t=O.copy(S);e=e||{},void 0!==t.data&&(void 0===e.data&&(e.data={}),e.data=O.merge(O.copy(t.data),e.data)),O.extend(t,e),t.scope=O.isObject(t.scope)?t.scope.$new():m.$new(),t.scope.confirm=function(e){n.resolve(e);var t=T(document.getElementById(r.id));$.performCloseDialog(t,e)};var r=_.open(t);if(r)return r.closePromise.then(function(e){return e?n.reject(e.value):n.reject()}),n.promise},isOpen:function(e){return 0<T(document.getElementById(e)).length},close:function(e,t){var n=T(document.getElementById(e));if(n.length)$.closeDialog(n,t);else if(\"$escape\"===e){var r=E[E.length-1];(n=T(document.getElementById(r))).data(\"$ngDialogOptions\").closeByEscape&&$.closeDialog(n,\"$escape\")}else _.closeAll(t);return _},closeAll:function(e){for(var t=document.querySelectorAll(\".ngdialog\"),n=t.length-1;0<=n;n--){var r=t[n];$.closeDialog(T(r),e)}},getOpenDialogs:function(){return E},getDefaults:function(){return S}};return O.forEach([\"html\",\"body\"],function(e){if(b[e]=n.find(e),r[e]){var t=$.getRouterLocationEventName();m.$on(t,function(){b[e]=n.find(e)})}}),_}]}),e.directive(\"ngDialog\",[\"ngDialog\",function(o){return{restrict:\"A\",scope:{ngDialogScope:\"=\"},link:function(r,e,i){e.on(\"click\",function(e){e.preventDefault();var t=O.isDefined(r.ngDialogScope)?r.ngDialogScope:\"noScope\";O.isDefined(i.ngDialogClosePrevious)&&o.close(i.ngDialogClosePrevious);var n=o.getDefaults();o.open({template:i.ngDialog,className:i.ngDialogClass||n.className,appendClassName:i.ngDialogAppendClass,controller:i.ngDialogController,controllerAs:i.ngDialogControllerAs,bindToController:i.ngDialogBindToController,disableAnimation:i.ngDialogDisableAnimation,scope:t,data:i.ngDialogData,showClose:\"false\"!==i.ngDialogShowClose&&(\"true\"===i.ngDialogShowClose||n.showClose),closeByDocument:\"false\"!==i.ngDialogCloseByDocument&&(\"true\"===i.ngDialogCloseByDocument||n.closeByDocument),closeByEscape:\"false\"!==i.ngDialogCloseByEscape&&(\"true\"===i.ngDialogCloseByEscape||n.closeByEscape),overlay:\"false\"!==i.ngDialogOverlay&&(\"true\"===i.ngDialogOverlay||n.overlay),preCloseCallback:i.ngDialogPreCloseCallback||n.preCloseCallback,bodyClassName:i.ngDialogBodyClass||n.bodyClassName})})}}}]),e}),angular.module(\"ui-notification\",[]),angular.module(\"ui-notification\").provider(\"Notification\",function(){this.options={delay:5e3,startTop:10,startRight:10,verticalSpacing:10,horizontalSpacing:10,positionX:\"right\",positionY:\"top\",replaceMessage:!1,templateUrl:\"angular-ui-notification.html\",onClose:void 0,closeOnClick:!0,maxCount:0,container:\"body\",priority:10},this.setOptions=function(e){if(!angular.isObject(e))throw new Error(\"Options should be an object!\");this.options=angular.extend({},this.options,e)},this.$get=[\"$timeout\",\"$http\",\"$compile\",\"$templateCache\",\"$rootScope\",\"$injector\",\"$sce\",\"$q\",\"$window\",function(l,r,c,i,o,e,f,a,g){var m=this.options,v=m.startTop,y=m.startRight,w=m.verticalSpacing,b=m.horizontalSpacing,s=m.delay,$=[],_=!1,t=function(h,e){function t(e){function t(t){[\"-webkit-transition\",\"-o-transition\",\"transition\"].forEach(function(e){i.css(e,t)})}var n=h.scope.$new();n.message=f.trustAsHtml(h.message),n.title=f.trustAsHtml(h.title),n.t=h.type.substr(0,1),n.delay=h.delay,n.onClose=h.onClose;var d=function(e,t){return e._priority-t._priority},p=function(e,t){return t._priority-e._priority},r=function(){var e=0,t=0,n=y,r=[];\"top\"===h.positionY?$.sort(d):\"bottom\"===h.positionY&&$.sort(p);for(var i=$.length-1;0<=i;i--){var o=$[i];if(h.replaceMessage&&i<$.length-1)o.addClass(\"killed\");else{var a=parseInt(o[0].offsetHeight),s=parseInt(o[0].offsetWidth),u=r[o._positionY+o._positionX];l+a>window.innerHeight&&(u=v,t++,e=0);var l=u?0===e?u:u+w:v,c=n+t*(b+s);o.css(o._positionY,l+\"px\"),\"center\"==o._positionX?o.css(\"left\",parseInt(window.innerWidth/2-s/2)+\"px\"):o.css(o._positionX,c+\"px\"),r[o._positionY+o._positionX]=l+a,0<m.maxCount&&$.length>m.maxCount&&0===i&&o.scope().kill(!0),e++}}},i=c(e)(n);i._positionY=h.positionY,i._positionX=h.positionX,i._priority=h.priority,i.addClass(h.type);var o=function(e){(\"click\"===(e=e.originalEvent||e).type||\"opacity\"===e.propertyName&&1<=e.elapsedTime)&&(n.onClose&&n.$apply(n.onClose(i)),i.remove(),$.splice($.indexOf(i),1),n.$destroy(),r())};h.closeOnClick&&(i.addClass(\"clickable\"),i.bind(\"click\",o)),i.bind(\"webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd\",o),angular.isNumber(h.delay)&&l(function(){i.addClass(\"killed\")},h.delay),t(\"none\"),angular.element(document.querySelector(h.container)).append(i);var a=-(parseInt(i[0].offsetHeight)+50);if(i.css(i._positionY,a+\"px\"),$.push(i),\"center\"==h.positionX){var s=parseInt(i[0].offsetWidth);i.css(\"left\",parseInt(window.innerWidth/2-s/2)+\"px\")}l(function(){t(\"\")}),n._templateElement=i,n.kill=function(e){e?(n.onClose&&n.$apply(n.onClose(n._templateElement)),$.splice($.indexOf(n._templateElement),1),n._templateElement.remove(),n.$destroy(),l(r)):n._templateElement.addClass(\"killed\")},l(r),_||(angular.element(g).bind(\"resize\",function(e){l(r)}),_=!0),u.resolve(n)}var u=a.defer();\"object\"==typeof h&&null!==h||(h={message:h}),h.scope=h.scope?h.scope:o,h.template=h.templateUrl?h.templateUrl:m.templateUrl,h.delay=angular.isUndefined(h.delay)?s:h.delay,h.type=e||h.type||m.type||\"\",h.positionY=h.positionY?h.positionY:m.positionY,h.positionX=h.positionX?h.positionX:m.positionX,h.replaceMessage=h.replaceMessage?h.replaceMessage:m.replaceMessage,h.onClose=h.onClose?h.onClose:m.onClose,h.closeOnClick=null!==h.closeOnClick&&void 0!==h.closeOnClick?h.closeOnClick:m.closeOnClick,h.container=h.container?h.container:m.container,h.priority=h.priority?h.priority:m.priority;var n=i.get(h.template);return n?t(n):r.get(h.template,{cache:!0}).then(function(e){t(e.data)}).catch(function(e){throw new Error(\"Template (\"+h.template+\") could not be loaded. \"+e)}),u.promise};return t.primary=function(e){return this(e,\"primary\")},t.error=function(e){return this(e,\"error\")},t.success=function(e){return this(e,\"success\")},t.info=function(e){return this(e,\"info\")},t.warning=function(e){return this(e,\"warning\")},t.clearAll=function(){angular.forEach($,function(e){e.addClass(\"killed\")})},t}]}),angular.module(\"ui-notification\").run([\"$templateCache\",function(e){e.put(\"angular-ui-notification.html\",'<div class=\"ui-notification\"><h3 ng-show=\"title\" ng-bind-html=\"title\"></h3><div class=\"message\" ng-bind-html=\"message\"></div></div>')}]),function(){var g=\"__default\";angular.module(\"angularUtils.directives.dirPagination\",[]).directive(\"dirPaginate\",[\"$compile\",\"$parse\",\"paginationService\",function(l,c,d){return{terminal:!0,multiElement:!0,priority:100,compile:function(e,t){var s=t.dirPaginate,n=s.match(/^\\s*([\\s\\S]+?)\\s+in\\s+([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?(?:\\s+track\\s+by\\s+([\\s\\S]+?))?\\s*$/),r=/\\|\\s*itemsPerPage\\s*:\\s*(.*\\(\\s*\\w*\\)|([^\\)]*?(?=\\s+as\\s+))|[^\\)]*)/;if(null===n[2].match(r))throw\"pagination directive: the 'itemsPerPage' filter must be set.\";var i=n[2].replace(r,\"\"),u=c(i);!function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).attr(\"dir-paginate-no-compile\",!0)})}(e);var o=t.paginationId||g;return d.registerInstance(o),function(e,t,n){var r=c(n.paginationId)(e)||n.paginationId||g;d.registerInstance(r);var i=function(e,t){var n=!!e.match(/(\\|\\s*itemsPerPage\\s*:[^|]*:[^|]*)/);return t===g||n?e:e.replace(/(\\|\\s*itemsPerPage\\s*:\\s*[^|\\s]*)/,\"$1 : '\"+t+\"'\")}(s,r);!function(e,t,n){e[0].hasAttribute(\"dir-paginate-start\")||e[0].hasAttribute(\"data-dir-paginate-start\")?(t.$set(\"ngRepeatStart\",n),e.eq(e.length-1).attr(\"ng-repeat-end\",!0)):t.$set(\"ngRepeat\",n)}(t,n,i),function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).removeAttr(\"dir-paginate-no-compile\")}),e.eq(0).removeAttr(\"dir-paginate-start\").removeAttr(\"dir-paginate\").removeAttr(\"data-dir-paginate-start\").removeAttr(\"data-dir-paginate\"),e.eq(e.length-1).removeAttr(\"dir-paginate-end\").removeAttr(\"data-dir-paginate-end\")}(t);var o=l(t),a=function(e,t,n){var r;if(t.currentPage)r=c(t.currentPage);else{var i=(n+\"__currentPage\").replace(/\\W/g,\"_\");e[i]=1,r=c(i)}return r}(e,n,r);d.setCurrentPageParser(r,a,e),void 0!==n.totalItems?(d.setAsyncModeTrue(r),e.$watch(function(){return c(n.totalItems)(e)},function(e){0<=e&&d.setCollectionLength(r,e)})):(d.setAsyncModeFalse(r),e.$watchCollection(function(){return u(e)},function(e){if(e){var t=e instanceof Array?e.length:Object.keys(e).length;d.setCollectionLength(r,t)}})),o(e)}}}}]).directive(\"dirPaginateNoCompile\",function(){return{priority:5e3,terminal:!0}}).directive(\"dirPaginationControls\",[\"paginationService\",\"paginationTemplate\",function(d,n){var p=/^\\d+$/,e={restrict:\"AE\",scope:{maxSize:\"=?\",onPageChange:\"&?\",paginationId:\"=?\",autoHide:\"=?\"},link:function(r,e,t){var n=t.paginationId||g,i=r.paginationId||t.paginationId||g;if(!d.isRegistered(i)&&!d.isRegistered(n)){var o=i!==g?\" (id: \"+i+\") \":\" \";window.console&&console.warn(\"Pagination directive: the pagination controls\"+o+\"cannot be used without the corresponding pagination directive, which was not found at link time.\")}r.maxSize||(r.maxSize=9);r.autoHide=void 0===r.autoHide||r.autoHide,r.directionLinks=!angular.isDefined(t.directionLinks)||r.$parent.$eval(t.directionLinks),r.boundaryLinks=!!angular.isDefined(t.boundaryLinks)&&r.$parent.$eval(t.boundaryLinks);var a=Math.max(r.maxSize,5);function s(e){if(d.isRegistered(i)&&c(e)){var t=r.pagination.current;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,l(),r.onPageChange&&r.onPageChange({newPageNumber:e,oldPageNumber:t})}}function u(){if(d.isRegistered(i)){var e=parseInt(d.getCurrentPage(i))||1;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,r.pagination.last=r.pages[r.pages.length-1],r.pagination.last<r.pagination.current?r.setCurrent(r.pagination.last):l()}}function l(){if(d.isRegistered(i)){var e=d.getCurrentPage(i),t=d.getItemsPerPage(i),n=d.getCollectionLength(i);r.range.lower=(e-1)*t+1,r.range.upper=Math.min(e*t,n),r.range.total=n}}function c(e){return p.test(e)&&0<e&&e<=r.pagination.last}r.pages=[],r.pagination={last:1,current:1},r.range={lower:1,upper:1,total:1},r.$watch(\"maxSize\",function(e){e&&(a=Math.max(r.maxSize,5),u())}),r.$watch(function(){if(d.isRegistered(i))return(d.getCollectionLength(i)+1)*d.getItemsPerPage(i)},function(e){0<e&&u()}),r.$watch(function(){if(d.isRegistered(i))return d.getItemsPerPage(i)},function(e,t){e!=t&&void 0!==t&&s(r.pagination.current)}),r.$watch(function(){if(d.isRegistered(i))return d.getCurrentPage(i)},function(e,t){e!=t&&s(e)}),r.setCurrent=function(e){d.isRegistered(i)&&c(e)&&(e=parseInt(e,10),d.setCurrentPage(i,e))},r.tracker=function(e,t){return e+\"_\"+t}}},t=n.getString();void 0!==t?e.template=t:e.templateUrl=function(e,t){return t.templateUrl||n.getPath()};return e;function h(e,t,n,r){var i,o=[],a=Math.ceil(t/n),s=Math.ceil(r/2);i=e<=s?\"start\":a-s<e?\"end\":\"middle\";for(var u=r<a,l=1;l<=a&&l<=r;){var c=f(l,e,r,a),d=2===l&&(\"middle\"===i||\"end\"===i),p=l===r-1&&(\"middle\"===i||\"start\"===i);u&&(d||p)?o.push(\"...\"):o.push(c),l++}return o}function f(e,t,n,r){var i=Math.ceil(n/2);return e===n?r:1===e?e:n<r?r-i<t?r-n+e:i<t?t-i+e:e:e}}]).filter(\"itemsPerPage\",[\"paginationService\",function(a){return function(t,e,n){if(void 0===n&&(n=g),!a.isRegistered(n))throw\"pagination directive: the itemsPerPage id argument (id: \"+n+\") does not match a registered pagination-id.\";var r,i;if(angular.isObject(t)){if(e=parseInt(e)||9999999999,i=a.isAsyncMode(n)?0:(a.getCurrentPage(n)-1)*e,r=i+e,a.setItemsPerPage(n,e),t instanceof Array)return t.slice(i,r);var o={};return angular.forEach(function(e){{if(Object.keys)return Object.keys(e);var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}}(t).slice(i,r),function(e){o[e]=t[e]}),o}return t}}]).service(\"paginationService\",function(){var t,r={};this.registerInstance=function(e){void 0===r[e]&&(r[e]={asyncMode:!1},t=e)},this.deregisterInstance=function(e){delete r[e]},this.isRegistered=function(e){return void 0!==r[e]},this.getLastInstanceId=function(){return t},this.setCurrentPageParser=function(e,t,n){r[e].currentPageParser=t,r[e].context=n},this.setCurrentPage=function(e,t){r[e].currentPageParser.assign(r[e].context,t)},this.getCurrentPage=function(e){var t=r[e].currentPageParser;return t?t(r[e].context):1},this.setItemsPerPage=function(e,t){r[e].itemsPerPage=t},this.getItemsPerPage=function(e){return r[e].itemsPerPage},this.setCollectionLength=function(e,t){r[e].collectionLength=t},this.getCollectionLength=function(e){return r[e].collectionLength},this.setAsyncModeTrue=function(e){r[e].asyncMode=!0},this.setAsyncModeFalse=function(e){r[e].asyncMode=!1},this.isAsyncMode=function(e){return r[e].asyncMode}}).provider(\"paginationTemplate\",function(){var t,n=\"angularUtils.directives.dirPagination.template\";this.setPath=function(e){n=e},this.setString=function(e){t=e},this.$get=function(){return{getPath:function(){return n},getString:function(){return t}}}}).run([\"$templateCache\",function(e){e.put(\"angularUtils.directives.dirPagination.template\",'<ul class=\"pagination\" ng-if=\"1 < pages.length || !autoHide\"><li ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == 1 }\"><a href=\"\" ng-click=\"setCurrent(1)\">&laquo;</a></li><li ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == 1 }\"><a href=\"\" ng-click=\"setCurrent(pagination.current - 1)\">&lsaquo;</a></li><li ng-repeat=\"pageNumber in pages track by tracker(pageNumber, $index)\" ng-class=\"{ active : pagination.current == pageNumber, disabled : pageNumber == \\'...\\' || ( ! autoHide && pages.length === 1 ) }\"><a href=\"\" ng-click=\"setCurrent(pageNumber)\">{{ pageNumber }}</a></li><li ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"><a href=\"\" ng-click=\"setCurrent(pagination.current + 1)\">&rsaquo;</a></li><li ng-if=\"boundaryLinks\"  ng-class=\"{ disabled : pagination.current == pagination.last }\"><a href=\"\" ng-click=\"setCurrent(pagination.last)\">&raquo;</a></li></ul>')}])}();var com_github_culmat_jsTreeTable=function(){function u(e,r,i){return i=i||\"children\",$.each(e,function(e,t){!function n(e){e[i]&&$.each(e[i],function(e,t){n(t)}),r(e)}(t)}),e}function t(e,n,o,a){var t=e;n=n||\"id\",o=o||\"parent\",a=a||\"children\";var s=[];$.each(t,function(e,t){s[t[n]]=t});var u=[];return $.each(t,function(e,r){var t=r[o];if($.isArray(t)||(t=[t]),0==t.length)u.push(r);else{var i=!1;$.each(t,function(e,t){var n=s[t];n&&(n[a]||(n[a]=[]),$.inArray(r,n[a])<0&&n[a].push(r),i=!0)}),i||u.push(r)}}),u}function l(e,a,s,u,l,t){a=a||\"children\",s=s||\"id\",t=t||{};var n=0,r=$(\"<table>\");$.each(t,function(e,t){\"class\"==e&&\"jsTT\"!=t?r.addClass(t):r.attr(e,t)});var i=$(\"<thead>\"),o=$(\"<tr>\"),c=$(\"<tbody>\");return r.append(i),i.append(o),r.append(c),u?$.each(u,function(e,t){$(o).append($(\"<th>\"+t+\"</th>\"))}):($(o).append($(\"<th>\"+s+\"</th>\")),$.each(e[0],function(e,t){e!=a&&e!=s&&$(o).append($(\"<th>\"+e+\"</th>\"))})),function r(e,i,o,t){n=Math.max(n,o),$.each(e,function(e,n){n[\"data-tt-level\"]=o,function(n,e){var r=$(\"<tr>\");$(r).attr(\"data-tt-id\",n[s]),$(r).attr(\"data-tt-level\",n[\"data-tt-level\"]),n[a]&&0!=n[a].length?$(r).attr(\"data-tt-isnode\",!0):$(r).attr(\"data-tt-isleaf\",!0),e&&$(r).attr(\"data-tt-parent-id\",e[s]),l?l($(r),n):u?$.each(u,function(e,t){$(r).append($(\"<td>\"+n[e]+\"</td>\"))}):($(r).append($(\"<td>\"+n[s]+\"</td>\")),$.each(n,function(e,t){e!=a&&e!=s&&\"data-tt-level\"!=e&&$(r).append($(\"<td>\"+t+\"</td>\"))})),c.append(r)}(n,t),n[i]&&$.each(n[i],function(e,t){r([t],i,o+1,n)})})}(e,a,1),e[0]&&(e[0].maxLevel=n),r}function n(e,t){return $.each(e,function(e,n){$.each(t,function(e,t){n[t]=$(n).attr(t)})}),e}function c(i){i.addClass(\"jsTT\"),i.expandLevel=function(n){$(\"tr[data-tt-level]\",i).each(function(e){var t=parseInt($(this).attr(\"data-tt-level\"));n-1<t?this.trCollapse(!0):t==n-1&&this.trExpand(!0)})},$(\"tr[data-tt-id]\",i).each(function(e,t){(t=$(t)).attr(\"data-tt-level\",function e(t){var n=t.attr(\"data-tt-level\");if(null!=n)return parseInt(n);var r=t.attr(\"data-tt-parent-id\");return null==r?0:e($('tr[data-tt-id=\"'+r+'\"]',i).first())+1}(t))});var e=$(\"tr[data-tt-level]\",i).get();$.each(e,function(e,t){t.trChildrenVisible=!0,t.trChildren=[]}),e=t(e=n(e,[\"data-tt-id\",\"data-tt-parent-id\"]),\"data-tt-id\",\"data-tt-parent-id\",\"trChildren\");var o=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC\";return $(\"tr[data-tt-level]\",i).each(function(e,t){var n=$(t).attr(\"data-tt-level\"),r=$(\"td\",t).first();0<t.trChildren.length?r.prepend($('<img id=\"state\" style=\"cursor:pointer\" src=\"'+o+'\"/>')):r.prepend($('<span style=\"padding-left:16px;\" /></span>')),r.prepend($('<span style=\"padding-left:'+15*parseInt(n-1)+'px;\" /></span>')),t.trExpand=function(e){if(!(this.trChildren.length<1)){e&&(this.trChildrenVisible=!0,$(\"#state\",this).get(0).src=o);var n=e||this.trChildrenVisible;$.each(this.trChildren,function(e,t){n&&$(t).css(\"display\",\"table-row\"),t.trExpand()})}},t.trCollapse=function(e){this.trChildren.length<1||(e&&(this.trChildrenVisible=!1,$(\"#state\",this).get(0).src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=\"),$.each(this.trChildren,function(e,t){$(t).css(\"display\",\"none\"),t.trCollapse()}))},$(t).click(function(){this.trChildrenVisible?this.trCollapse(!0):this.trExpand(!0)})}),i}return{depthFirst:u,makeTree:t,renderTree:l,attr2attr:n,treeTable:c,appendTreetable:function(e,t){(t=t||{}).idAttr=t.idAttr||\"id\",t.childrenAttr=t.childrenAttr||\"children\";var n=t.controls||[];t.mountPoint||(t.mountPoint=$(\"body\")),t.depthFirst&&u(e,t.depthFirst,t.childrenAttr);var r=l(e,t.childrenAttr,t.idAttr,t.renderedAttr,t.renderer,t.tableAttributes);c(r),t.replaceContent&&t.mountPoint.html(\"\");var i,o,a=t.initialExpandLevel?parseInt(t.initialExpandLevel):-1;if(a=Math.min(a,e[0].maxLevel),r.expandLevel(a),t.slider){var s=$('<div style=\"margin-right: 15px;\">');s.width(\"200px\"),s.slider({min:1,max:e[0].maxLevel,range:\"min\",value:a,slide:function(e,t){r.expandLevel(t.value)}}),n=[s].concat(t.controls)}return 0<n.length&&t.mountPoint.append((i=n,o=$(\"<tr>\"),$.each(i,function(e,t){o.append($('<td style=\"padding-right: 20px;\">').append(t))}),$('<table border=\"0\"/>').append(o))),t.mountPoint.append(r),r},jsTreeTable:\"1.0\",register:function(n){$.each(this,function(e,t){\"register\"!=e&&(n[e]=t)})}}}();"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/gulpfile.js",
    "content": "const gulp = require('gulp');\nconst plugins = require('gulp-load-plugins')();\nconst open = require('open');\nconst app = {\n  srcPath: 'app/', // 源代码\n  devPath: 'tmp/', // 开发打包\n  prdPath: 'dist/' // 生产打包\n};\n\nconst JS_LIBS = [\n  'node_modules/angular-ui-router/release/angular-ui-router.js',\n  'node_modules/oclazyload/dist/ocLazyLoad.min.js',\n  'node_modules/angular-loading-bar/build/loading-bar.min.js',\n  'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js',\n  'node_modules/moment/moment.js',\n  'node_modules/angular-date-time-input/src/dateTimeInput.js',\n  'node_modules/angularjs-bootstrap-datetimepicker/src/js/datetimepicker.js',\n  'node_modules/angular-table-resize/dist/angular-table-resize.min.js',\n  'node_modules/angular-clipboard/angular-clipboard.js',\n  'node_modules/selectize/dist/js/standalone/selectize.js',\n  'node_modules/angular-selectize2/dist/selectize.js',\n  'node_modules/bootstrap-switch/dist/js/bootstrap-switch.min.js',\n  'node_modules/ng-dialog/js/ngDialog.js',\n  'node_modules/angular-ui-notification/dist/angular-ui-notification.min.js',\n  'node_modules/angular-utils-pagination/dirPagination.js',\n  'app/scripts/libs/treeTable.js',\n];\n\nconst CSS_APP = [\n  'node_modules/angular-loading-bar/build/loading-bar.min.css',\n  'node_modules/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css',\n  'node_modules/ng-dialog/css/ngDialog.min.css',\n  'node_modules/ng-dialog/css/ngDialog-theme-default.css',\n  'node_modules/angularjs-bootstrap-datetimepicker/src/css/datetimepicker.css',\n  'node_modules/angular-ui-notification/dist/angular-ui-notification.min.css',\n  'node_modules/angular-table-resize/dist/angular-table-resize.css',\n  'node_modules/selectize/dist/css/selectize.css',\n  'app/styles/page.css',\n  'app/styles/timeline.css',\n  'app/styles/main.css'\n];\n\nconst JS_APP = [\n  'app/scripts/app.js',\n  'app/scripts/filters/filters.js',\n  'app/scripts/services/auth_service.js',\n  'app/scripts/services/appservice.js',\n  'app/scripts/services/flow_service_v1.js',\n  'app/scripts/services/flow_service_v2.js',\n  'app/scripts/services/degradeservice.js',\n  'app/scripts/services/systemservice.js',\n  'app/scripts/services/machineservice.js',\n  'app/scripts/services/identityservice.js',\n  'app/scripts/services/metricservice.js',\n  'app/scripts/services/param_flow_service.js',\n  'app/scripts/services/authority_service.js',\n  'app/scripts/services/cluster_state_service.js',\n];\n\ngulp.task('lib', function () {\n  gulp.src(JS_LIBS)\n    .pipe(plugins.concat('app.vendor.js'))\n    .pipe(gulp.dest(app.devPath + 'js'))\n    .pipe(plugins.uglify())\n    .pipe(gulp.dest(app.prdPath + 'js'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* css任务\n* 在src下创建style文件夹，里面存放less文件。 \n*/\ngulp.task('css', function () {\n  gulp.src(CSS_APP)\n    .pipe(plugins.concat('app.css'))\n    .pipe(gulp.dest(app.devPath + 'css'))\n    .pipe(plugins.cssmin())\n    .pipe(gulp.dest(app.prdPath + 'css'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* js任务\n* 在src目录下创建script文件夹，里面存放所有的js文件\n*/\ngulp.task('js', function () {\n  gulp.src(JS_APP)\n    .pipe(plugins.concat('app.js'))\n    .pipe(gulp.dest(app.devPath + 'js'))\n    .pipe(plugins.uglify())\n    .pipe(gulp.dest(app.prdPath + 'js'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* js任务\n* 在src目录下创建script文件夹，里面存放所有的js文件\n*/\ngulp.task('jshint', function () {\n  gulp.src(JS_APP)\n    .pipe(plugins.jshint())\n    .pipe(plugins.jshint.reporter());\n});\n\n// 每次发布的时候，可能需要把之前目录内的内容清除，避免旧的文件对新的容有所影响。 需要在每次发布前删除dist和build目录\ngulp.task('clean', function () {\n  gulp.src([app.devPath, app.prdPath])\n    .pipe(plugins.clean());\n});\n\n// 总任务\ngulp.task('build', ['clean', 'jshint', 'lib', 'js', 'css']);\n\n// 服务\ngulp.task('serve', ['build'], function () {\n  plugins.connect.server({ //启动一个服务器\n    root: [app.devPath], // 服务器从哪个路径开始读取，默认从开发路径读取\n    livereload: true, // 自动刷新\n    port: 1234\n  });\n  // 打开浏览器\n  setTimeout(() => {\n    open('http://localhost:8080/index_dev.htm')\n  }, 200);\n  // 监听\n  gulp.watch(app.srcPath + '**/*.js', ['js']);\n  gulp.watch(app.srcPath + '**/*.css', ['css']);\n});\n\n// 定义default任务\ngulp.task('default', ['serve']);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/index.htm",
    "content": "<!doctype html>\n<html class=\"no-js\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Sentinel Dashboard</title>\n  <meta name=\"description\" content=\"Sentinel 控制台,Sentinel Dashboard,Admin\">\n  <meta name=\"viewport\" content=\"width=device-width\">\n\n  <link rel=\"stylesheet\" href=\"./lib/css/bootstrap.min.css\" />\n  <link rel=\"stylesheet\" href=\"./lib/css/font-awesome.min.css\">\n  <link rel=\"stylesheet\" href=\"./dist/css/app.css\">\n</head>\n\n<body>\n  <div ng-app=\"sentinelDashboardApp\">\n    <div ui-view></div>\n  </div>\n  <script src=\"./lib/js/jquery.min.js\"></script>\n  <script src=\"./lib/js/bootstrap.min.js\"></script>\n  <script src=\"./lib/js/angular.min.js\"></script>\n  <script src=\"./lib/js/g2.min.js\"></script>\n\n  <script src=\"./dist/js/app.vendor.js\"></script>\n  <script src=\"./dist/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/index_dev.htm",
    "content": "<!doctype html>\n<html class=\"no-js\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Sentinel 控制台</title>\n  <meta name=\"description\" content=\"Sentinel 控制台,Dashboard,Admin\">\n  <meta name=\"viewport\" content=\"width=device-width\">\n\n  <link rel=\"stylesheet\" href=\"//cdn.bootcss.com/bootstrap/3.0.3/css/bootstrap.min.css\" />\n  <link rel=\"stylesheet\" href=\"//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css\">\n  <link rel=\"stylesheet\" href=\"http://localhost:1234/css/app.css\">\n</head>\n\n<body>\n  <div ng-app=\"sentinelDashboardApp\">\n    <div ui-view></div>\n  </div>\n  <script src=\"//cdn.bootcss.com/jquery/2.1.4/jquery.min.js\"></script>\n  <script src=\"//cdn.bootcss.com/bootstrap/3.0.3/js/bootstrap.min.js\"></script>\n  <script src=\"//cdn.bootcss.com/angular.js/1.4.8/angular.js\"></script>\n  <script src=\"//gw.alipayobjects.com/os/antv/pkg/_antv.g2-3.4.10/dist/g2.min.js\"></script>\n\n  <script src=\"http://localhost:1234/js/app.vendor.js\"></script>\n  <script src=\"http://localhost:1234/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/license-stat.csv",
    "content": "Type,Package,License\nnpm,angular,MIT License\nnpm,angular-animate,MIT License\nnpm,angular-bootstrap,MIT License\nnpm,angular-clipboard,MIT License\nnpm,angular-cookies,MIT License\nnpm,angular-date-time-input,MIT License\nnpm,angular-loading-bar,MIT License\nnpm,angular-mocks,MIT License\nnpm,angular-resource,MIT License\nnpm,angular-route,MIT License\nnpm,angular-selectize2,MIT License\nnpm,angular-table-resize,MIT License\nnpm,angular-touch,MIT License\nnpm,angular-ui-notification,MIT License\nnpm,angular-ui-router,MIT License\nnpm,angular-utils-pagination,MIT License\nnpm,angularjs-bootstrap-datetimepicker,MIT License\nnpm,bootstrap-switch,Apache License 2.0\nnpm,bootstrap-tagsinput,MIT License\nnpm,moment,MIT License\nnpm,ng-dialog,MIT License\nnpm,ng-tags-input,MIT License\nnpm,oclazyload,MIT License\nnpm,selectize,Apache License 2.0\nlib,jsTreeTable,MIT License"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/package.json",
    "content": "{\n  \"name\": \"sentinel-dashboard\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo no test case\",\n    \"build\": \"gulp build\",\n    \"start\": \"gulp\"\n  },\n  \"author\": \"x-cold <lxstart@outlook.com>\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"angular\": \"^1.4.8\",\n    \"angular-animate\": \"^1.4.0\",\n    \"angular-bootstrap\": \"^0.12.2\",\n    \"angular-clipboard\": \"^1.6.2\",\n    \"angular-cookies\": \"^1.4.0\",\n    \"angular-date-time-input\": \"^1.2.1\",\n    \"angular-loading-bar\": \"^0.9.0\",\n    \"angular-mocks\": \"^1.4.0\",\n    \"angular-resource\": \"^1.4.0\",\n    \"angular-route\": \"^1.4.0\",\n    \"angular-selectize2\": \"^v1.2.3\",\n    \"angular-table-resize\": \"^2.0.1\",\n    \"angular-touch\": \"^1.4.0\",\n    \"angular-ui-notification\": \"^0.3.6\",\n    \"angular-ui-router\": \"^1.0.18\",\n    \"angular-utils-pagination\": \"^0.11.1\",\n    \"angularjs-bootstrap-datetimepicker\": \"^1.1.4\",\n    \"bootstrap-switch\": \"^3.3.4\",\n    \"bootstrap-tagsinput\": \"~0.7.1\",\n    \"moment\": \"^2.12.0\",\n    \"ng-dialog\": \"^0.6.6\",\n    \"ng-tags-input\": \"~3.0.0\",\n    \"oclazyload\": \"^1.1.0\",\n    \"selectize\": \"^0.12.1\",\n    \"lodash\": \">=4.17.11\"\n  },\n  \"devDependencies\": {\n    \"gulp\": \"^3.9.1\",\n    \"gulp-clean\": \"^0.4.0\",\n    \"gulp-concat\": \"^2.6.1\",\n    \"gulp-connect\": \"^5.7.0\",\n    \"gulp-csscomb\": \"^3.0.8\",\n    \"gulp-cssmin\": \"^0.2.0\",\n    \"gulp-jshint\": \"^2.1.0\",\n    \"gulp-load-plugins\": \"^1.5.0\",\n    \"gulp-serv\": \"0.0.1\",\n    \"gulp-uglify\": \"^3.0.0\",\n    \"jshint\": \"^2.10.2\",\n    \"open\": \"0.0.5\",\n    \"source-map\": \"^0.7.3\"\n  }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfigTest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\n\npublic class DashboardConfigTest {\n    @Rule\n    public final EnvironmentVariables environmentVariables = new EnvironmentVariables();\n\n    @Test\n    public void testGetConfigStr() {\n        // clear cache\n        DashboardConfig.clearCache();\n\n        // if not set, return null\n        assertEquals(null, DashboardConfig.getConfigStr(\"a\"));\n\n        // test property\n        System.setProperty(\"a\", \"111\");\n        assertEquals(\"111\", DashboardConfig.getConfigStr(\"a\"));\n\n        // test env\n        environmentVariables.set(\"a\", \"222\");\n        // return value in cache\n        assertEquals(\"111\", DashboardConfig.getConfigStr(\"a\"));\n\n        // clear cache and then test\n        DashboardConfig.clearCache();\n        assertEquals(\"222\", DashboardConfig.getConfigStr(\"a\"));\n    }\n\n    @Test\n    public void testGetConfigInt() {\n        // clear cache\n        DashboardConfig.clearCache();\n\n        // default value\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(1, DashboardConfig.getConfigInt(\"t\", 1, 10));\n        \n        // property, wrong format\n        System.setProperty(\"t\", \"asdf\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        System.setProperty(\"t\", \"\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // min value\n        System.setProperty(\"t\", \"2\");\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        DashboardConfig.clearCache();\n        assertEquals(10, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, -1));\n        \n        // env\n        environmentVariables.set(\"t\", \"20\");\n        DashboardConfig.clearCache();\n        assertEquals(20, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // wrong format env var, but it will override property\n        environmentVariables.set(\"t\", \"20dddd\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // clear env, it will take property\n        environmentVariables.set(\"t\", \"\");\n        DashboardConfig.clearCache();\n        assertEquals(10, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        \n        // enable cache\n        System.setProperty(\"t\", \"666\");\n        DashboardConfig.clearCache();\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        System.setProperty(\"t\", \"777\");\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        System.setProperty(\"t\", \"555\");\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersionTest.java",
    "content": "package com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\npublic class SentinelVersionTest {\n    @Test\n    public void testEqual() {\n        assertEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0));\n        assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 2, 3));\n        assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0, \"\"));\n        assertEquals(new SentinelVersion(1, 0, 0, \"\"), new SentinelVersion(1, 0, 0, \"\"));\n        assertNotEquals(new SentinelVersion(1, 0, 0, \"\"), new SentinelVersion(1, 0, 0, null));\n        assertEquals(new SentinelVersion(1, 0, 0, null), new SentinelVersion(1, 0, 0, null));\n    }\n    \n    @Test\n    public void testGreater() {\n        assertTrue(new SentinelVersion(2, 0, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 1, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 0)));\n        assertTrue(new SentinelVersion(1, 1, 4).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 1, 0)));\n        assertFalse(new SentinelVersion(1, 1, 3).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 0, 0, \"\").greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 0, 1).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 0, 1, \"a\").greaterThan(new SentinelVersion(1, 0, 0, \"b\")));\n        assertFalse(new SentinelVersion(1, 0, 0, \"b\").greaterThan(new SentinelVersion(1, 0, 0, \"a\")));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfoTest.java",
    "content": "/*\n * Copyright 1999-2019 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.Set;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\nimport static org.junit.Assert.*;\n\npublic class AppInfoTest {\n\n    @Test\n    public void testConcurrentGetMachines() throws Exception {\n        AppInfo appInfo = new AppInfo(\"testApp\");\n        appInfo.addMachine(genMachineInfo(\"hostName1\", \"10.18.129.91\"));\n        appInfo.addMachine(genMachineInfo(\"hostName2\", \"10.18.129.92\"));\n        Set<MachineInfo> machines = appInfo.getMachines();\n        new Thread(() -> {\n            try {\n                for (MachineInfo m : machines) {\n                    System.out.println(m);\n                    try {\n                        Thread.sleep(200);\n                    } catch (InterruptedException e) {\n                    }\n                }\n            } catch (ConcurrentModificationException e) {\n                e.printStackTrace();\n                fail();\n            }\n\n        }).start();\n        Thread.sleep(100);\n        try {\n            appInfo.addMachine(genMachineInfo(\"hostName3\", \"10.18.129.93\"));\n        } catch (ConcurrentModificationException e) {\n            e.printStackTrace();\n            fail();\n        }\n        Thread.sleep(1000);\n    }\n\n    private MachineInfo genMachineInfo(String hostName, String ip) {\n        MachineInfo machine = new MachineInfo();\n        machine.setApp(\"testApp\");\n        machine.setHostname(hostName);\n        machine.setIp(ip);\n        machine.setPort(8719);\n        machine.setVersion(String.valueOf(System.currentTimeMillis()));\n        return machine;\n    }\n\n    @Test\n    public void addRemoveMachineTest() {\n        AppInfo appInfo = new AppInfo(\"default\");\n        assertEquals(\"default\", appInfo.getApp());\n        assertEquals(0, appInfo.getMachines().size());\n        //add one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3389);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.1\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(1, appInfo.getMachines().size());\n        //add duplicated one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3389);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.2\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(1, appInfo.getMachines().size());\n        //add different one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3390);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.3\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(2, appInfo.getMachines().size());\n        appInfo.removeMachine(\"127.0.0.1\", 3389);\n        assertEquals(1, appInfo.getMachines().size());\n        appInfo.removeMachine(\"127.0.0.1\", 3390);\n        assertEquals(0, appInfo.getMachines().size());\n    }\n\n    @Test\n    public void testHealthyAndDead() {\n        System.setProperty(DashboardConfig.CONFIG_HIDE_APP_NO_MACHINE_MILLIS, \"60000\");\n        System.setProperty(DashboardConfig.CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, \"600000\");\n        DashboardConfig.clearCache();\n        String appName = \"default\";\n        AppInfo appInfo = new AppInfo();\n        appInfo.setApp(appName);\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            appInfo.addMachine(machineInfo);\n        }\n        assertTrue(appInfo.isShown());\n        assertFalse(appInfo.isDead());\n\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis() - 70000);\n            appInfo.addMachine(machineInfo);\n        }\n        assertFalse(appInfo.isShown());\n        assertFalse(appInfo.isDead());\n\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis() - 700000);\n            appInfo.addMachine(machineInfo);\n        }\n        assertFalse(appInfo.isShown());\n        assertTrue(appInfo.isDead());\n    }\n\n}\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfoTest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\n/**\n * @author Jason Joo\n */\npublic class MachineInfoTest {\n\n    @Test\n    public void testHealthyAndDead() {\n        System.setProperty(DashboardConfig.CONFIG_UNHEALTHY_MACHINE_MILLIS, \"60000\");\n        System.setProperty(DashboardConfig.CONFIG_AUTO_REMOVE_MACHINE_MILLIS, \"600000\");\n        DashboardConfig.clearCache();\n        MachineInfo machineInfo = new MachineInfo();\n        machineInfo.setHeartbeatVersion(1);\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 10000);\n        assertTrue(machineInfo.isHealthy());\n        assertFalse(machineInfo.isDead());\n\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 100000);\n        assertFalse(machineInfo.isHealthy());\n        assertFalse(machineInfo.isDead());\n\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 1000000);\n        assertFalse(machineInfo.isHealthy());\n        assertTrue(machineInfo.isDead());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepositoryTest.java",
    "content": "/*\n * Copyright 1999-2019 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\n\nimport org.assertj.core.util.Lists;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * Test cases for {@link InMemoryMetricsRepository}.\n *\n * @author Nick Tan\n */\npublic class InMemoryMetricsRepositoryTest {\n\n    private static final String DEFAULT_APP = \"default\";\n    private static final String DEFAULT_EXPIRE_APP = \"default_expire_app\";\n    private static final String DEFAULT_RESOURCE = \"test\";\n    private static final long EXPIRE_TIME = 1000 * 60 * 5L;\n\n    private InMemoryMetricsRepository inMemoryMetricsRepository;\n\n    private ExecutorService executorService;\n\n    @Before\n    public void setUp() throws Exception {\n        inMemoryMetricsRepository = new InMemoryMetricsRepository();\n        executorService = Executors.newFixedThreadPool(8);\n    }\n\n    @After\n    public void tearDown() {\n        executorService.shutdownNow();\n    }\n\n    private void testSave() {\n        for (int i = 0; i < 1000000; i++) {\n            MetricEntity entry = new MetricEntity();\n            entry.setApp(DEFAULT_APP);\n            entry.setResource(DEFAULT_RESOURCE);\n            entry.setTimestamp(new Date(System.currentTimeMillis()));\n            entry.setPassQps(1L);\n            entry.setExceptionQps(1L);\n            entry.setBlockQps(0L);\n            entry.setSuccessQps(1L);\n            inMemoryMetricsRepository.save(entry);\n        }\n    }\n\n    @Test\n    public void testExpireMetric() {\n        long now = System.currentTimeMillis();\n        MetricEntity expireEntry = new MetricEntity();\n        expireEntry.setApp(DEFAULT_EXPIRE_APP);\n        expireEntry.setResource(DEFAULT_RESOURCE);\n        expireEntry.setTimestamp(new Date(now - EXPIRE_TIME - 10L));\n        expireEntry.setPassQps(1L);\n        expireEntry.setExceptionQps(1L);\n        expireEntry.setBlockQps(0L);\n        expireEntry.setSuccessQps(1L);\n        inMemoryMetricsRepository.save(expireEntry);\n\n        MetricEntity entry = new MetricEntity();\n        entry.setApp(DEFAULT_EXPIRE_APP);\n        entry.setResource(DEFAULT_RESOURCE);\n        entry.setTimestamp(new Date(now));\n        entry.setPassQps(1L);\n        entry.setExceptionQps(1L);\n        entry.setBlockQps(0L);\n        entry.setSuccessQps(1L);\n        inMemoryMetricsRepository.save(entry);\n\n        List<MetricEntity> list = inMemoryMetricsRepository.queryByAppAndResourceBetween(\n            DEFAULT_EXPIRE_APP, DEFAULT_RESOURCE, now - 2 * EXPIRE_TIME, now + EXPIRE_TIME);\n\n        assertFalse(CollectionUtils.isEmpty(list));\n        assertEquals(1, list.size());\n    }\n\n    @Test\n    public void testListResourcesOfApp() {\n        // prepare basic test data\n        testSave();\n        System.out.println( \"[\" + System.currentTimeMillis() + \"] Basic test data ready in testListResourcesOfApp\");\n\n        List<CompletableFuture> futures = Lists.newArrayList();\n\n        // concurrent query resources of app\n        final CyclicBarrier cyclicBarrier = new CyclicBarrier(8);\n        for (int j = 0; j < 10000; j++) {\n            futures.add(\n                CompletableFuture.runAsync(() -> {\n                        try {\n                            cyclicBarrier.await();\n                            inMemoryMetricsRepository.listResourcesOfApp(DEFAULT_APP);\n                        } catch (InterruptedException | BrokenBarrierException e) {\n                            e.printStackTrace();\n                        }\n                }, executorService)\n            );\n        }\n\n        // batch add metric entity\n        for (int i = 0; i < 10000; i++) {\n            MetricEntity entry = new MetricEntity();\n            entry.setApp(DEFAULT_APP);\n            entry.setResource(DEFAULT_RESOURCE);\n            entry.setTimestamp(new Date(System.currentTimeMillis() - EXPIRE_TIME - 1000L));\n            entry.setPassQps(1L);\n            entry.setExceptionQps(1L);\n            entry.setBlockQps(0L);\n            entry.setSuccessQps(1L);\n            inMemoryMetricsRepository.save(entry);\n        }\n\n        CompletableFuture all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));\n\n        try {\n            all.get(10, TimeUnit.SECONDS);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        } catch (ExecutionException e) {\n            e.getCause().printStackTrace();\n            if (e.getCause() instanceof ConcurrentModificationException) {\n                fail(\"concurrent error occurred\");\n            } else {\n                fail(\"unexpected exception\");\n            }\n        } catch (TimeoutException e) {\n            fail(\"allOf future timeout\");\n        }\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.List;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Configuration\npublic class ApolloConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ApolloOpenApiClient apolloOpenApiClient() {\n        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()\n            .withPortalUrl(\"http://localhost:10034\")\n            .withToken(\"token\")\n            .build();\n        return client;\n\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfigUtil.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\npublic final class ApolloConfigUtil {\n\n    public static final String FLOW_DATA_ID_POSTFIX = \"-flow-rules\";\n\n    private ApolloConfigUtil() {\n    }\n\n    public static String getFlowDataId(String appName) {\n        return String.format(\"%s%s\", appName, FLOW_DATA_ID_POSTFIX);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloProvider\")\npublic class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String appId = \"appId\";\n        String flowDataId = ApolloConfigUtil.getFlowDataId(appName);\n        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appId, \"DEV\", \"default\", \"application\");\n        String rules = openNamespaceDTO\n            .getItems()\n            .stream()\n            .filter(p -> p.getKey().equals(flowDataId))\n            .map(OpenItemDTO::getValue)\n            .findFirst()\n            .orElse(\"\");\n\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloPublisher\")\npublic class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n\n        // Increase the configuration\n        String appId = \"appId\";\n        String flowDataId = ApolloConfigUtil.getFlowDataId(app);\n        OpenItemDTO openItemDTO = new OpenItemDTO();\n        openItemDTO.setKey(flowDataId);\n        openItemDTO.setValue(converter.convert(rules));\n        openItemDTO.setComment(\"Program auto-join\");\n        openItemDTO.setDataChangeCreatedBy(\"some-operator\");\n        apolloOpenApiClient.createOrUpdateItem(appId, \"DEV\", \"default\", \"application\", openItemDTO);\n\n        // Release configuration\n        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();\n        namespaceReleaseDTO.setEmergencyPublish(true);\n        namespaceReleaseDTO.setReleaseComment(\"Modify or add configurations\");\n        namespaceReleaseDTO.setReleasedBy(\"some-operator\");\n        namespaceReleaseDTO.setReleaseTitle(\"Modify or add configurations\");\n        apolloOpenApiClient.publishNamespace(appId, \"DEV\", \"default\", \"application\", namespaceReleaseDTO);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosProvider\")\npublic class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,\n            NacosConfigUtil.GROUP_ID, 3000);\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosPublisher\")\npublic class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,\n            NacosConfigUtil.GROUP_ID, converter.convert(rules));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.nacos.api.config.ConfigFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Configuration\npublic class NacosConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ConfigService nacosConfigService() throws Exception {\n        return ConfigFactory.createConfigService(\"localhost\");\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic final class NacosConfigUtil {\n\n    public static final String GROUP_ID = \"SENTINEL_GROUP\";\n    \n    public static final String FLOW_DATA_ID_POSTFIX = \"-flow-rules\";\n    public static final String PARAM_FLOW_DATA_ID_POSTFIX = \"-param-rules\";\n    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = \"-cluster-map\";\n\n    /**\n     * cc for `cluster-client`\n     */\n    public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = \"-cc-config\";\n    /**\n     * cs for `cluster-server`\n     */\n    public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = \"-cs-transport-config\";\n    public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = \"-cs-flow-config\";\n    public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = \"-cs-namespace-set\";\n\n    private NacosConfigUtil() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtilsTest.java",
    "content": "package com.alibaba.csp.sentinel.dashboard.util;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Optional;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\n\npublic class VersionUtilsTest {\n    @Test\n    public void test() {\n        Optional<SentinelVersion> version = VersionUtils.parseVersion(\"1.2.3\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(0, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3.4\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(0, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3-\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"-\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"-t\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(null);\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"1.2.3-SNAPSHOTS\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertEquals(\"SNAPSHOTS\", version.get().getPostfix());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>com.alibaba.csp</groupId>\n        <artifactId>sentinel-parent</artifactId>\n        <version>1.6.0</version>\n    </parent>\n\n    <artifactId>alibaba-sentinel-dashboard-nacos</artifactId>\n    <packaging>jar</packaging>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <spring.boot.version>2.0.5.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-web-servlet</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-logging</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n            <version>1.2.14</version>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-lang</groupId>\n            <artifactId>commons-lang</artifactId>\n            <version>2.6</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n            <version>4.5.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpcore</artifactId>\n            <version>4.4.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpasyncclient</artifactId>\n            <version>4.1.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpcore-nio</artifactId>\n            <version>4.4.6</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n\n        <!-- for Nacos rule publisher sample -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-nacos</artifactId>\n            <!--<scope>test</scope>-->\n        </dependency>\n\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-openapi</artifactId>\n            <version>1.2.0</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n        \t<groupId>com.github.stefanbirkner</groupId>\n        \t<artifactId>system-rules</artifactId>\n        \t<version>1.16.1</version>\n        \t<scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <finalName>sentinel-dashboard</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                    <mainClass>com.alibaba.csp.sentinel.dashboard.DashboardApplication</mainClass>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>${maven.compiler.source}</source>\n                    <target>${maven.compiler.target}</target>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <version>${maven.deploy.version}</version>\n                <configuration>\n                    <skip>true</skip>\n                </configuration>\n            </plugin>\n        </plugins>\n\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n\n            <resource>\n                <directory>src/main/webapp/</directory>\n                <excludes>\n                    <exclude>resources/node_modules/**</exclude>\n                </excludes>\n            </resource>\n        </resources>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/DashboardApplication.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard;\n\nimport com.alibaba.csp.sentinel.init.InitExecutor;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Sentinel dashboard application.\n *\n * @author Carpenter Lee\n */\n@SpringBootApplication\npublic class DashboardApplication {\n\n    public static void main(String[] args) {\n        triggerSentinelInit();\n        SpringApplication.run(DashboardApplication.class, args);\n    }\n\n    private static void triggerSentinelInit() {\n        new Thread(() -> InitExecutor.doInit()).start();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\n/**\n * Interface for authentication and authorization.\n *\n * @author Carpenter Lee\n * @since 1.5.0\n */\npublic interface AuthService<R> {\n\n    /**\n     * Get the authentication user.\n     *\n     * @param request the request contains the user information\n     * @return the auth user represent the current user, when the user is illegal, a null value will return.\n     */\n    AuthUser getAuthUser(R request);\n\n    /**\n     * Privilege type.\n     */\n    enum PrivilegeType {\n        /**\n         * Read rule\n         */\n        READ_RULE,\n        /**\n         * Create or modify rule\n         */\n        WRITE_RULE,\n        /**\n         * Delete rule\n         */\n        DELETE_RULE,\n        /**\n         * Read metrics\n         */\n        READ_METRIC,\n        /**\n         * Add machine\n         */\n        ADD_MACHINE,\n        /**\n         * All privileges above are granted.\n         */\n        ALL\n    }\n\n    /**\n     * Represents the current user.\n     */\n    interface AuthUser {\n\n        /**\n         * Query whether current user has the specific privilege to the target, the target\n         * may be an app name or an ip address, or other destination.\n         * <p>\n         * This method will use return value to represent  whether user has the specific\n         * privileges to the target, but to throw a RuntimeException to represent no auth\n         * is also a good way.\n         * </p>\n         *\n         * @param target        the target to check\n         * @param privilegeType the privilege type to check\n         * @return if current user has the specific privileges to the target, return true,\n         * otherwise return false.\n         */\n        boolean authTarget(String target, PrivilegeType privilegeType);\n\n        /**\n         * Check whether current user is a super-user.\n         *\n         * @return if current user is super user return true, else return false.\n         */\n        boolean isSuperUser();\n\n        /**\n         * Get current user's nick name.\n         *\n         * @return current user's nick name.\n         */\n        String getNickName();\n\n        /**\n         * Get current user's login name.\n         *\n         * @return current user's login name.\n         */\n        String getLoginName();\n\n        /**\n         * Get current user's ID.\n         *\n         * @return ID of current user\n         */\n        String getId();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * A fake AuthService implementation, which will pass all user auth checking.\n *\n * @author Carpenter Lee\n * @since 1.5.0\n */\n@Component\npublic class FakeAuthServiceImpl implements AuthService<HttpServletRequest> {\n\n    @Override\n    public AuthUser getAuthUser(HttpServletRequest request) {\n        return new AuthUserImpl();\n    }\n\n    static final class AuthUserImpl implements AuthUser {\n\n        @Override\n        public boolean authTarget(String target, PrivilegeType privilegeType) {\n            // fake implementation, always return true\n            return true;\n        }\n\n        @Override\n        public boolean isSuperUser() {\n            // fake implementation, always return true\n            return true;\n        }\n\n        @Override\n        public String getNickName() {\n            return \"FAKE_NICK_NAME\";\n        }\n\n        @Override\n        public String getLoginName() {\n            return \"FAKE_LOGIN_NAME\";\n        }\n\n        @Override\n        public String getId() {\n            return \"FAKE_EMP_ID\";\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.auth;\n\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\n/**\n * @author cdfive\n * @since 1.6.0\n */\n@Primary\n@Component\npublic class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> {\n\n    public static final String WEB_SESSTION_KEY = \"session_sentinel_admin\";\n\n    @Override\n    public AuthUser getAuthUser(HttpServletRequest request) {\n        HttpSession session = request.getSession();\n        Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY);\n        if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) {\n            return (AuthUser) sentinelUserObj;\n        }\n\n        return null;\n    }\n\n    public static final class SimpleWebAuthUserImpl implements AuthUser {\n\n        private String username;\n\n        public SimpleWebAuthUserImpl(String username) {\n            this.username = username;\n        }\n\n        @Override\n        public boolean authTarget(String target, PrivilegeType privilegeType) {\n            return true;\n        }\n\n        @Override\n        public boolean isSuperUser() {\n            return true;\n        }\n\n        @Override\n        public String getNickName() {\n            return username;\n        }\n\n        @Override\n        public String getLoginName() {\n            return username;\n        }\n\n        @Override\n        public String getId() {\n            return username;\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandFailedException.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\n/**\n * @author Eric Zhao\n */\npublic class CommandFailedException extends RuntimeException {\n\n    public CommandFailedException() {}\n\n    public CommandFailedException(String message) {\n        super(message);\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandNotFoundException.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class CommandNotFoundException extends Exception {\n\n    public CommandNotFoundException() { }\n\n    public CommandNotFoundException(String message) {\n        super(message);\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.client;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.command.CommandConstants;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\nimport com.alibaba.csp.sentinel.dashboard.util.AsyncUtils;\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;\nimport com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;\nimport com.alibaba.csp.sentinel.slots.system.SystemRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.concurrent.FutureCallback;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.client.DefaultRedirectStrategy;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.apache.http.impl.nio.reactor.IOReactorConfig;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.apache.http.util.EntityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.lang.Nullable;\nimport org.springframework.stereotype.Component;\n\n/**\n * Communicate with Sentinel client.\n *\n * @author leyou\n */\n@Component\npublic class SentinelApiClient {\n    private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);\n\n    private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());\n\n    private static final String RESOURCE_URL_PATH = \"jsonTree\";\n    private static final String CLUSTER_NODE_PATH = \"clusterNode\";\n    private static final String GET_RULES_PATH = \"getRules\";\n    private static final String SET_RULES_PATH = \"setRules\";\n    private static final String GET_PARAM_RULE_PATH = \"getParamFlowRules\";\n    private static final String SET_PARAM_RULE_PATH = \"setParamFlowRules\";\n\n    private static final String FETCH_CLUSTER_MODE_PATH = \"getClusterMode\";\n    private static final String MODIFY_CLUSTER_MODE_PATH = \"setClusterMode\";\n    private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = \"cluster/client/fetchConfig\";\n    private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = \"cluster/client/modifyConfig\";\n\n    private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = \"cluster/server/fetchConfig\";\n    private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = \"cluster/server/info\";\n\n    private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = \"cluster/server/modifyTransportConfig\";\n    private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = \"cluster/server/modifyFlowConfig\";\n    private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = \"cluster/server/modifyNamespaceSet\";\n\n    private static final String FLOW_RULE_TYPE = \"flow\";\n    private static final String DEGRADE_RULE_TYPE = \"degrade\";\n    private static final String SYSTEM_RULE_TYPE = \"system\";\n    private static final String AUTHORITY_TYPE = \"authority\";\n\n    private CloseableHttpAsyncClient httpClient;\n\n    private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0);\n    \n    @Autowired\n    private AppManagement appManagement;\n\n    public SentinelApiClient() {\n        IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000)\n            .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();\n        httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {\n            @Override\n            protected boolean isRedirectable(final String method) {\n                return false;\n            }\n        }).setMaxConnTotal(4000).setMaxConnPerRoute(1000).setDefaultIOReactorConfig(ioConfig).build();\n        httpClient.start();\n    }\n\n    private boolean isSuccess(int statusCode) {\n        return statusCode >= 200 && statusCode < 300;\n    }\n    \n    private boolean isCommandNotFound(int statusCode, String body) {\n        return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);\n    }\n    \n    private StringBuilder queryString(Map<String, String> params) {\n        StringBuilder queryStringBuilder = new StringBuilder();\n        for (Entry<String, String> entry : params.entrySet()) {\n            if (StringUtil.isEmpty(entry.getValue())) {\n                continue;\n            }\n            String name = urlEncode(entry.getKey());\n            String value = urlEncode(entry.getValue());\n            if (name != null && value != null) {\n                if (queryStringBuilder.length() > 0) {\n                    queryStringBuilder.append('&');\n                }\n                queryStringBuilder.append(name).append('=').append(value);\n            }\n        }\n        return queryStringBuilder;\n    }\n    \n    private HttpUriRequest postRequest(String url, Map<String, String> params) {\n        HttpPost httpPost = new HttpPost(url);\n        if (params != null && params.size() > 0) {\n            List<NameValuePair> list = new ArrayList<>(params.size());\n            for (Entry<String, String> entry : params.entrySet()) {\n                list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));\n            }\n            try {\n                httpPost.setEntity(new UrlEncodedFormEntity(list));\n            } catch (UnsupportedEncodingException e) {\n                logger.warn(\"httpPostContent encode entity error: {}\", params, e);\n                return null;\n            }\n        }\n        return httpPost;\n    }\n    \n    private String urlEncode(String str) {\n        try {\n            return URLEncoder.encode(str, DEFAULT_CHARSET.name());\n        } catch (UnsupportedEncodingException e) {\n            logger.info(\"encode string error: {}\", str, e);\n            return null;\n        }\n    }\n    \n    private String getBody(HttpResponse response) throws Exception {\n        Charset charset = null;\n        try {\n            String contentTypeStr = response.getFirstHeader(\"Content-type\").getValue();\n            if (StringUtil.isNotEmpty(contentTypeStr)) {\n                ContentType contentType = ContentType.parse(contentTypeStr);\n                charset = contentType.getCharset();\n            }\n        } catch (Exception ignore) {\n        }\n        return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);\n    }\n    \n    /**\n     * With no param\n     * \n     * @param ip\n     * @param port\n     * @param api\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String ip, int port, String api, boolean useHttpPost) {\n        return executeCommand(ip, port, api, null, useHttpPost);\n    }\n    \n    /**\n     * No app specified, force to GET\n     * \n     * @param ip\n     * @param port\n     * @param api\n     * @param params\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String ip, int port, String api, Map<String, String> params, boolean useHttpPost) {\n        return executeCommand(null, ip, port, api, params, useHttpPost);\n    }\n\n    /**\n     * Prefer to execute request using POST\n     * \n     * @param app\n     * @param ip\n     * @param port\n     * @param api\n     * @param params\n     * @return\n     */\n    private CompletableFuture<String> executeCommand(String app, String ip, int port, String api, Map<String, String> params, boolean useHttpPost) {\n        CompletableFuture<String> future = new CompletableFuture<>();\n        if (StringUtil.isBlank(ip) || StringUtil.isBlank(api)) {\n            future.completeExceptionally(new IllegalArgumentException(\"Bad URL or command name\"));\n            return future;\n        }\n        StringBuilder urlBuilder = new StringBuilder();\n        urlBuilder.append(\"http://\");\n        urlBuilder.append(ip).append(':').append(port).append('/').append(api);\n        if (params == null) {\n            params = Collections.emptyMap();\n        }\n        boolean supportPost = StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version160)))\n                .orElse(false);\n        if (!useHttpPost || !supportPost) {\n            // Using GET in older versions, append parameters after url\n            if (!params.isEmpty()) {\n                if (urlBuilder.indexOf(\"?\") == -1) {\n                    urlBuilder.append('?');\n                } else {\n                    urlBuilder.append('&');\n                }\n                urlBuilder.append(queryString(params));\n            }\n            return executeCommand(new HttpGet(urlBuilder.toString()));\n        } else {\n            // Using POST\n            return executeCommand(postRequest(urlBuilder.toString(), params));\n        }\n    }\n    \n    private CompletableFuture<String> executeCommand(HttpUriRequest request) {\n        CompletableFuture<String> future = new CompletableFuture<>();\n        httpClient.execute(request, new FutureCallback<HttpResponse>() {\n            @Override\n            public void completed(final HttpResponse response) {\n                int statusCode = response.getStatusLine().getStatusCode();\n                try {\n                    String value = getBody(response);\n                    if (isSuccess(statusCode)) {\n                        future.complete(value);\n                    } else {\n                        if (isCommandNotFound(statusCode, value)) {\n                            future.completeExceptionally(new CommandNotFoundException(request.getURI().getPath()));\n                        } else {\n                            future.completeExceptionally(new CommandFailedException(value));\n                        }\n                    }\n\n                } catch (Exception ex) {\n                    future.completeExceptionally(ex);\n                    logger.error(\"HTTP request failed: {}\", request.getURI().toString(), ex);\n                }\n            }\n\n            @Override\n            public void failed(final Exception ex) {\n                future.completeExceptionally(ex);\n                logger.error(\"HTTP request failed: {}\", request.getURI().toString(), ex);\n            }\n\n            @Override\n            public void cancelled() {\n                future.complete(null);\n            }\n        });\n        return future;\n    }\n    \n    public void close() throws Exception {\n        httpClient.close();\n    }\n    \n    @Nullable\n    private <T> CompletableFuture<List<T>> fetchItemsAsync(String ip, int port, String api, String type, Class<T> ruleType) {\n        AssertUtil.notEmpty(ip, \"Bad machine IP\");\n        AssertUtil.isTrue(port > 0, \"Bad machine port\");\n        Map<String, String> params = null;\n        if (StringUtil.isNotEmpty(type)) {\n            params = new HashMap<>(1);\n            params.put(\"type\", type);\n        }\n        return executeCommand(ip, port, api, params, false)\n                .thenApply(json -> JSON.parseArray(json, ruleType));\n    }\n    \n    @Nullable\n    private <T> List<T> fetchItems(String ip, int port, String api, String type, Class<T> ruleType) {\n        try {\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            Map<String, String> params = null;\n            if (StringUtil.isNotEmpty(type)) {\n                params = new HashMap<>(1);\n                params.put(\"type\", type);\n            }\n            return fetchItemsAsync(ip, port, api, type, ruleType).get();\n        } catch (InterruptedException | ExecutionException e) {\n            logger.error(\"Error when fetching items from api: {} -> {}\", api, type, e);\n            return null;\n        } catch (Exception e) {\n            logger.error(\"Error when fetching items: {} -> {}\", api, type, e);\n            return null;\n        }\n    }\n    \n    private <T extends Rule> List<T> fetchRules(String ip, int port, String type, Class<T> ruleType) {\n        return fetchItems(ip, port, GET_RULES_PATH, type, ruleType);\n    }\n    \n    private boolean setRules(String app, String ip, int port, String type, List<? extends RuleEntity> entities) {\n        if (entities == null) {\n            return true;\n        }\n        try {\n            AssertUtil.notEmpty(app, \"Bad app name\");\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            String data = JSON.toJSONString(\n                    entities.stream().map(r -> r.toRule()).collect(Collectors.toList()));\n            Map<String, String> params = new HashMap<>(2);\n            params.put(\"type\", type);\n            params.put(\"data\", data);\n            String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get();\n            logger.info(\"setRules: {}\", result);\n            return true;\n        } catch (InterruptedException | ExecutionException e) {\n            logger.warn(\"setRules api failed: {}\", type, e);\n            return false;\n        } catch (Exception e) {\n            logger.warn(\"setRules failed\", e);\n            return false;\n        }\n    }\n\n    public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {\n        return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class);\n    }\n\n    /**\n     * Fetch cluster node.\n     *\n     * @param ip          ip to fetch\n     * @param port        port of the ip\n     * @param includeZero whether zero value should in the result list.\n     * @return\n     */\n    public List<NodeVo> fetchClusterNodeOfMachine(String ip, int port, boolean includeZero) {\n        String type = \"noZero\";\n        if (includeZero) {\n            type = \"zero\";\n        }\n        return fetchItems(ip, port, CLUSTER_NODE_PATH, type, NodeVo.class);\n    }\n\n    public List<FlowRuleEntity> fetchFlowRuleOfMachine(String app, String ip, int port) {\n        List<FlowRule> rules = fetchRules(ip, port, FLOW_RULE_TYPE, FlowRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    public List<DegradeRuleEntity> fetchDegradeRuleOfMachine(String app, String ip, int port) {\n        List<DegradeRule> rules = fetchRules(ip, port, DEGRADE_RULE_TYPE, DegradeRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    public List<SystemRuleEntity> fetchSystemRuleOfMachine(String app, String ip, int port) {\n        List<SystemRule> rules = fetchRules(ip, port, SYSTEM_RULE_TYPE, SystemRule.class);\n        if (rules != null) {\n            return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule))\n                .collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Fetch all parameter flow rules from provided machine.\n     *\n     * @param app  application name\n     * @param ip   machine client IP\n     * @param port machine client port\n     * @return all retrieved parameter flow rules\n     * @since 0.2.1\n     */\n    public CompletableFuture<List<ParamFlowRuleEntity>> fetchParamFlowRulesOfMachine(String app, String ip, int port) {\n        try {\n            AssertUtil.notEmpty(app, \"Bad app name\");\n            AssertUtil.notEmpty(ip, \"Bad machine IP\");\n            AssertUtil.isTrue(port > 0, \"Bad machine port\");\n            return fetchItemsAsync(ip, port, GET_PARAM_RULE_PATH, null, ParamFlowRule.class)\n                .thenApply(rules -> rules.stream()\n                    .map(e -> ParamFlowRuleEntity.fromAuthorityRule(app, ip, port, e))\n                    .collect(Collectors.toList())\n                );\n        } catch (Exception e) {\n            logger.error(\"Error when fetching parameter flow rules\", e);\n            return AsyncUtils.newFailedFuture(e);\n        }\n    }\n\n    /**\n     * Fetch all authority rules from provided machine.\n     *\n     * @param app  application name\n     * @param ip   machine client IP\n     * @param port machine client port\n     * @return all retrieved authority rules\n     * @since 0.2.1\n     */\n    public List<AuthorityRuleEntity> fetchAuthorityRulesOfMachine(String app, String ip, int port) {\n        AssertUtil.notEmpty(app, \"Bad app name\");\n        AssertUtil.notEmpty(ip, \"Bad machine IP\");\n        AssertUtil.isTrue(port > 0, \"Bad machine port\");\n        Map<String, String> params = new HashMap<>(1);\n        params.put(\"type\", AUTHORITY_TYPE);\n        List<AuthorityRule> rules = fetchRules(ip, port, AUTHORITY_TYPE, AuthorityRule.class);\n        return Optional.ofNullable(rules).map(r -> r.stream()\n                    .map(e -> AuthorityRuleEntity.fromAuthorityRule(app, ip, port, e))\n                    .collect(Collectors.toList())\n                ).orElse(null);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setFlowRuleOfMachine(String app, String ip, int port, List<FlowRuleEntity> rules) {\n        return setRules(app, ip, port, FLOW_RULE_TYPE, rules);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setDegradeRuleOfMachine(String app, String ip, int port, List<DegradeRuleEntity> rules) {\n        return setRules(app, ip, port, DEGRADE_RULE_TYPE, rules);\n    }\n\n    /**\n     * set rules of the machine. rules == null will return immediately;\n     * rules.isEmpty() means setting the rules to empty.\n     *\n     * @param app\n     * @param ip\n     * @param port\n     * @param rules\n     * @return whether successfully set the rules.\n     */\n    public boolean setSystemRuleOfMachine(String app, String ip, int port, List<SystemRuleEntity> rules) {\n        return setRules(app, ip, port, SYSTEM_RULE_TYPE, rules);\n    }\n\n    public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List<AuthorityRuleEntity> rules) {\n        return setRules(app, ip, port, AUTHORITY_TYPE, rules);\n    }\n\n    public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) {\n        if (rules == null) {\n            return CompletableFuture.completedFuture(null);\n        }\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            String data = JSON.toJSONString(\n                rules.stream().map(ParamFlowRuleEntity::getRule).collect(Collectors.toList())\n            );\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", data);\n            return executeCommand(app, ip, port, SET_PARAM_RULE_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Push parameter flow rules to client failed: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when setting parameter flow rule\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    // Cluster related\n\n    public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_MODE_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterStateSimpleEntity.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster mode\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterMode(String ip, int port, int mode) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"mode\", String.valueOf(mode));\n            return executeCommand(ip, port, MODIFY_CLUSTER_MODE_PATH, params, false)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster mode: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster mode\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<ClusterClientInfoVO> fetchClusterClientInfoAndConfig(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_CLIENT_CONFIG_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster client config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterClientConfig(String app, String ip, int port, ClusterClientConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(config));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_CLIENT_CONFIG_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster client config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster client config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerFlowConfig(String app, String ip, int port, ServerFlowConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(config));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server flow config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server flow config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerTransportConfig(String app, String ip, int port, ServerTransportConfig config) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(2);\n            params.put(\"port\", config.getPort().toString());\n            params.put(\"idleSeconds\", config.getIdleSeconds().toString());\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, params, false)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server transport config: \" + e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server transport config\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<Void> modifyClusterServerNamespaceSet(String app, String ip, int port, Set<String> set) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            Map<String, String> params = new HashMap<>(1);\n            params.put(\"data\", JSON.toJSONString(set));\n            return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, params, true)\n                .thenCompose(e -> {\n                    if (CommandConstants.MSG_SUCCESS.equals(e)) {\n                        return CompletableFuture.completedFuture(null);\n                    } else {\n                        logger.warn(\"Error when modifying cluster server NamespaceSet\", e);\n                        return AsyncUtils.newFailedFuture(new RuntimeException(e));\n                    }\n                });\n        } catch (Exception ex) {\n            logger.warn(\"Error when modifying cluster server NamespaceSet\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n\n    public CompletableFuture<ClusterServerStateVO> fetchClusterServerBasicInfo(String ip, int port) {\n        if (StringUtil.isBlank(ip) || port <= 0) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"Invalid parameter\"));\n        }\n        try {\n            return executeCommand(ip, port, FETCH_CLUSTER_SERVER_BASIC_INFO_PATH, false)\n                .thenApply(r -> JSON.parseObject(r, ClusterServerStateVO.class));\n        } catch (Exception ex) {\n            logger.warn(\"Error when fetching cluster sever all config and basic info\", ex);\n            return AsyncUtils.newFailedFuture(ex);\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.lang.NonNull;\n\n/**\n * <p>Dashboard local config support.</p>\n * <p>\n * Dashboard supports configuration loading by several ways by order:<br>\n * 1. System.properties<br>\n * 2. Env\n * </p>\n *\n * @author jason\n * @since 1.5.0\n */\npublic class DashboardConfig {\n\n    public static final int DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS = 60_000;\n\n    /**\n     * Login username\n     */\n    public static final String CONFIG_AUTH_USERNAME = \"sentinel.dashboard.auth.username\";\n\n    /**\n     * Login password\n     */\n    public static final String CONFIG_AUTH_PASSWORD = \"sentinel.dashboard.auth.password\";\n\n    /**\n     * Hide application name in sidebar when it has no healthy machines after specific period in millisecond.\n     */\n    public static final String CONFIG_HIDE_APP_NO_MACHINE_MILLIS = \"sentinel.dashboard.app.hideAppNoMachineMillis\";\n    /**\n     * Remove application when it has no healthy machines after specific period in millisecond.\n     */\n    public static final String CONFIG_REMOVE_APP_NO_MACHINE_MILLIS = \"sentinel.dashboard.removeAppNoMachineMillis\";\n    /**\n     * Timeout\n     */\n    public static final String CONFIG_UNHEALTHY_MACHINE_MILLIS = \"sentinel.dashboard.unhealthyMachineMillis\";\n    /**\n     * Auto remove unhealthy machine after specific period in millisecond.\n     */\n    public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = \"sentinel.dashboard.autoRemoveMachineMillis\";\n\n    private static final ConcurrentMap<String, Object> cacheMap = new ConcurrentHashMap<>();\n    \n    @NonNull\n    private static String getConfig(String name) {\n        // env\n        String val = System.getenv(name);\n        if (StringUtils.isNotEmpty(val)) {\n            return val;\n        }\n        // properties\n        val = System.getProperty(name);\n        if (StringUtils.isNotEmpty(val)) {\n            return val;\n        }\n        return \"\";\n    }\n\n    protected static String getConfigStr(String name) {\n        if (cacheMap.containsKey(name)) {\n            return (String) cacheMap.get(name);\n        }\n\n        String val = getConfig(name);\n\n        if (StringUtils.isBlank(val)) {\n            return null;\n        }\n\n        cacheMap.put(name, val);\n        return val;\n    }\n\n    protected static int getConfigInt(String name, int defaultVal, int minVal) {\n        if (cacheMap.containsKey(name)) {\n            return (int)cacheMap.get(name);\n        }\n        int val = NumberUtils.toInt(getConfig(name));\n        if (val == 0) {\n            val = defaultVal;\n        } else if (val < minVal) {\n            val = minVal;\n        }\n        cacheMap.put(name, val);\n        return val;\n    }\n\n    public static String getAuthUsername() {\n        return getConfigStr(CONFIG_AUTH_USERNAME);\n    }\n\n    public static String getAuthPassword() {\n        return getConfigStr(CONFIG_AUTH_PASSWORD);\n    }\n\n    public static int getHideAppNoMachineMillis() {\n        return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000);\n    }\n    \n    public static int getRemoveAppNoMachineMillis() {\n        return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000);\n    }\n    \n    public static int getAutoRemoveMachineMillis() {\n        return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000);\n    }\n    \n    public static int getUnhealthyMachineMillis() {\n        return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS, 30000);\n    }\n    \n    public static void clearCache() {\n        cacheMap.clear();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;\nimport com.alibaba.csp.sentinel.dashboard.filter.AuthFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.ViewControllerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport javax.servlet.Filter;\n\n/**\n * @author leyou\n */\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    private final Logger logger = LoggerFactory.getLogger(WebConfig.class);\n\n    @Autowired\n    private AuthFilter authFilter;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        registry.addResourceHandler(\"/**\").addResourceLocations(\"classpath:/resources/\");\n    }\n\n    @Override\n    public void addViewControllers(ViewControllerRegistry registry) {\n        registry.addViewController(\"/\").setViewName(\"forward:/index.htm\");\n    }\n\n    /**\n     * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel\n     * for Web application.\n     */\n    @Bean\n    public FilterRegistrationBean sentinelFilterRegistration() {\n        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();\n        registration.setFilter(new CommonFilter());\n        registration.addUrlPatterns(\"/*\");\n        registration.setName(\"sentinelFilter\");\n        registration.setOrder(1);\n\n        logger.info(\"Sentinel servlet CommonFilter registered\");\n\n        return registration;\n    }\n\n    @Bean\n    public FilterRegistrationBean authenticationFilterRegistration() {\n        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();\n        registration.setFilter(authFilter);\n        registration.addUrlPatterns(\"/*\");\n        registration.setName(\"authenticationFilter\");\n        registration.setOrder(0);\n        return registration;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.MachineInfoVo;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Carpenter Lee\n */\n@RestController\n@RequestMapping(value = \"/app\")\npublic class AppController {\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @GetMapping(\"/names.json\")\n    public Result<List<String>> queryApps(HttpServletRequest request) {\n        return Result.ofSuccess(appManagement.getAppNames());\n    }\n\n    @GetMapping(\"/briefinfos.json\")\n    public Result<List<AppInfo>> queryAppInfos(HttpServletRequest request) {\n        List<AppInfo> list = new ArrayList<>(appManagement.getBriefApps());\n        Collections.sort(list, Comparator.comparing(AppInfo::getApp));\n        return Result.ofSuccess(list);\n    }\n\n    @GetMapping(value = \"/{app}/machines.json\")\n    public Result<List<MachineInfoVo>> getMachinesByApp(@PathVariable(\"app\") String app) {\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null) {\n            return Result.ofSuccess(null);\n        }\n        List<MachineInfo> list = new ArrayList<>(appInfo.getMachines());\n        Collections.sort(list, (o1, o2) -> {\n            int t = o1.getApp().compareTo(o2.getApp());\n            if (t != 0) {\n                return t;\n            }\n            t = o1.getIp().compareTo(o2.getIp());\n            if (t != 0) {\n                return t;\n            }\n            return o1.getPort().compareTo(o2.getPort());\n        });\n        return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list));\n    }\n    \n    @RequestMapping(value = \"/{app}/machine/remove.json\")\n    public Result<String> removeMachineById(\n            @PathVariable(\"app\") String app,\n            @RequestParam(name = \"ip\") String ip,\n            @RequestParam(name = \"port\") int port) {\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null) {\n            return Result.ofSuccess(null);\n        }\n        if (appManagement.removeMachine(app, ip, port)) {\n            return Result.ofSuccessMsg(\"success\");\n        } else {\n            return Result.ofFail(1, \"remove failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl;\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * @author cdfive\n * @since 1.6.0\n */\n@RestController\n@RequestMapping(\"/auth\")\npublic class AuthController {\n\n    private static Logger LOGGER = LoggerFactory.getLogger(AuthController.class);\n\n    @Value(\"${auth.username:sentinel}\")\n    private String authUsername;\n\n    @Value(\"${auth.password:sentinel}\")\n    private String authPassword;\n\n    @PostMapping(\"/login\")\n    public Result login(HttpServletRequest request, String username, String password) {\n        if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) {\n            authUsername = DashboardConfig.getAuthUsername();\n        }\n\n        if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) {\n            authPassword = DashboardConfig.getAuthPassword();\n        }\n\n        /*\n         * If auth.username or auth.password is blank(set in application.properties or VM arguments),\n         * auth will pass, as the front side validate the input which can't be blank,\n         * so user can input any username or password(both are not blank) to login in that case.\n         */\n        if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username)\n                || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) {\n            LOGGER.error(\"Login failed: Invalid username or password, username=\" + username + \", password=\" + password);\n            return Result.ofFail(-1, \"Invalid username or password\");\n        }\n\n        AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username);\n        request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY, authUser);\n        return Result.ofSuccess(authUser);\n    }\n\n    @RequestMapping(value = \"/logout\", method = RequestMethod.POST)\n    public Result logout(HttpServletRequest request) {\n        request.getSession().invalidate();\n        return Result.ofSuccess(null);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@RestController\n@RequestMapping(value = \"/authority\")\npublic class AuthorityRuleController {\n\n    private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private RuleRepository<AuthorityRuleEntity, Long> repository;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @GetMapping(\"/rules\")\n    public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(HttpServletRequest request,\n                                                                        @RequestParam String app,\n                                                                        @RequestParam String ip,\n                                                                        @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        try {\n            List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying authority rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"bad rule body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null || entity.getPort() <= 0) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (entity.getRule() == null) {\n            return Result.ofFail(-1, \"rule can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource name cannot be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp should be valid\");\n        }\n        if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE\n            && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) {\n            return Result.ofFail(-1, \"Unknown strategy (must be blacklist or whitelist)\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<AuthorityRuleEntity> apiAddAuthorityRule(HttpServletRequest request,\n                                                           @RequestBody AuthorityRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add authority rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"Publish authority rules failed after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(HttpServletRequest request,\n                                                              @PathVariable(\"id\") Long id,\n                                                              @RequestBody AuthorityRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(null);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"Failed to save authority rule\");\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to save authority rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"Publish authority rules failed after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id cannot be null\");\n        }\n        AuthorityRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.error(\"Publish authority rules failed after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author leyou\n */\n@Controller\n@RequestMapping(value = \"/degrade\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class DegradeController {\n\n    private final Logger logger = LoggerFactory.getLogger(DegradeController.class);\n\n    @Autowired\n    private InMemDegradeRuleStore repository;\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @ResponseBody\n    @RequestMapping(\"/rules.json\")\n    public Result<List<DegradeRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"queryApps error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/new.json\")\n    public Result<DegradeRuleEntity> add(HttpServletRequest request,\n                                         String app, String ip, Integer port, String limitApp, String resource,\n                                         Double count, Integer timeWindow, Integer grade) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n\n        if (StringUtil.isBlank(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (StringUtil.isBlank(limitApp)) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(resource)) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (count == null) {\n            return Result.ofFail(-1, \"count can't be null\");\n        }\n        if (timeWindow == null) {\n            return Result.ofFail(-1, \"timeWindow can't be null\");\n        }\n        if (grade == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {\n            return Result.ofFail(-1, \"Invalid grade: \" + grade);\n        }\n        DegradeRuleEntity entity = new DegradeRuleEntity();\n        entity.setApp(app.trim());\n        entity.setIp(ip.trim());\n        entity.setPort(port);\n        entity.setLimitApp(limitApp.trim());\n        entity.setResource(resource.trim());\n        entity.setCount(count);\n        entity.setTimeWindow(timeWindow);\n        entity.setGrade(grade);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"add error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(app, ip, port)) {\n            logger.info(\"publish degrade rules fail after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/save.json\")\n    public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,\n                                                     Long id, String app, String limitApp, String resource,\n                                                     Double count, Integer timeWindow, Integer grade) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        if (grade != null) {\n            if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {\n                return Result.ofFail(-1, \"Invalid grade: \" + grade);\n            }\n        }\n        DegradeRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n\n        if (StringUtil.isNotBlank(limitApp)) {\n            entity.setLimitApp(limitApp.trim());\n        }\n        if (StringUtil.isNotBlank(resource)) {\n            entity.setResource(resource.trim());\n        }\n        if (count != null) {\n            entity.setCount(count);\n        }\n        if (timeWindow != null) {\n            entity.setTimeWindow(timeWindow);\n        }\n        if (grade != null) {\n            entity.setGrade(grade);\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish degrade rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/delete.json\")\n    public Result<Long> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n\n        DegradeRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Throwable throwable) {\n            logger.error(\"delete error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish degrade rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DemoController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Random;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.EntryType;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.context.ContextUtil;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\n\n@Controller\n@RequestMapping(value = \"/demo\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class DemoController {\n\n    Logger logger = LoggerFactory.getLogger(MachineRegistryController.class);\n\n    @RequestMapping(\"/greeting\")\n    public String greeting() {\n        return \"index\";\n    }\n\n    @RequestMapping(\"/link\")\n    @ResponseBody\n    public String link() throws BlockException {\n\n        Entry entry = SphU.entry(\"head1\", EntryType.IN);\n\n        Entry entry1 = SphU.entry(\"head2\", EntryType.IN);\n        Entry entry2 = SphU.entry(\"head3\", EntryType.IN);\n        Entry entry3 = SphU.entry(\"head4\", EntryType.IN);\n\n        entry3.exit();\n        entry2.exit();\n        entry1.exit();\n        entry.exit();\n        return \"successfully create a call link\";\n    }\n\n    @RequestMapping(\"/loop\")\n    @ResponseBody\n    public String loop(String name, int time) throws BlockException {\n        for (int i = 0; i < 10; i++) {\n            Thread timer = new Thread(new RunTask(name, time, false));\n            timer.setName(\"false\");\n            timer.start();\n        }\n        return \"successfully create a loop thread\";\n    }\n\n    @RequestMapping(\"/slow\")\n    @ResponseBody\n    public String slow(String name, int time) throws BlockException {\n        for (int i = 0; i < 10; i++) {\n            Thread timer = new Thread(new RunTask(name, time, true));\n            timer.setName(\"false\");\n            timer.start();\n        }\n        return \"successfully create a loop thread\";\n    }\n\n    static class RunTask implements Runnable {\n        int time;\n        boolean stop = false;\n        String name;\n        boolean slow = false;\n\n        public RunTask(String name, int time, boolean slow) {\n            super();\n            this.time = time;\n            this.name = name;\n            this.slow = slow;\n        }\n\n        @Override\n        public void run() {\n            long startTime = System.currentTimeMillis();\n            ContextUtil.enter(String.valueOf(startTime));\n            while (!stop) {\n\n                long now = System.currentTimeMillis();\n                if (now - startTime > time * 1000) {\n                    stop = true;\n                }\n                Entry e1 = null;\n                try {\n                    e1 = SphU.entry(name);\n\n                    if (slow == true) {\n                        TimeUnit.MILLISECONDS.sleep(3000);\n                    }\n\n                } catch (Exception e) {\n                } finally {\n                    if (e1 != null) {\n                        e1.exit();\n                    }\n                }\n                Random random2 = new Random();\n                try {\n                    TimeUnit.MILLISECONDS.sleep(random2.nextInt(200));\n                } catch (InterruptedException e) {\n                    // TODO Auto-generated catch block\n                    e.printStackTrace();\n                }\n\n            }\n            ContextUtil.exit();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Flow rule controller.\n *\n * @author leyou\n * @author Eric Zhao\n */\n@RestController\n@RequestMapping(value = \"/v1/flow\")\npublic class FlowControllerV1 {\n\n    private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class);\n\n    @Autowired\n    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n\n    @GetMapping(\"/rules\")\n    public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request,\n                                                             @RequestParam String app,\n                                                             @RequestParam String ip,\n                                                             @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying flow rules\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (entity.getGrade() == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (entity.getGrade() != 0 && entity.getGrade() != 1) {\n            return Result.ofFail(-1, \"grade must be 0 or 1, but \" + entity.getGrade() + \" got\");\n        }\n        if (entity.getCount() == null || entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be at lease zero\");\n        }\n        if (entity.getStrategy() == null) {\n            return Result.ofFail(-1, \"strategy can't be null\");\n        }\n        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {\n            return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n        }\n        if (entity.getControlBehavior() == null) {\n            return Result.ofFail(-1, \"controlBehavior can't be null\");\n        }\n        int controlBehavior = entity.getControlBehavior();\n        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {\n            return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n        }\n        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {\n            return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n        }\n        if (entity.isClusterMode() && entity.getClusterConfig() == null) {\n            return Result.ofFail(-1, \"cluster config should be valid\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        entity.setLimitApp(entity.getLimitApp().trim());\n        entity.setResource(entity.getResource().trim());\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.error(\"Publish flow rules failed after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/save.json\")\n    public Result<FlowRuleEntity> updateIfNotNull(HttpServletRequest request, Long id, String app,\n                                                  String limitApp, String resource, Integer grade,\n                                                  Double count, Integer strategy, String refResource,\n                                                  Integer controlBehavior, Integer warmUpPeriodSec,\n                                                  Integer maxQueueingTimeMs) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        FlowRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n        if (StringUtil.isNotBlank(limitApp)) {\n            entity.setLimitApp(limitApp.trim());\n        }\n        if (StringUtil.isNotBlank(resource)) {\n            entity.setResource(resource.trim());\n        }\n        if (grade != null) {\n            if (grade != 0 && grade != 1) {\n                return Result.ofFail(-1, \"grade must be 0 or 1, but \" + grade + \" got\");\n            }\n            entity.setGrade(grade);\n        }\n        if (count != null) {\n            entity.setCount(count);\n        }\n        if (strategy != null) {\n            if (strategy != 0 && strategy != 1 && strategy != 2) {\n                return Result.ofFail(-1, \"strategy must be in [0, 1, 2], but \" + strategy + \" got\");\n            }\n            entity.setStrategy(strategy);\n            if (strategy != 0) {\n                if (StringUtil.isBlank(refResource)) {\n                    return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n                }\n                entity.setRefResource(refResource.trim());\n            }\n        }\n        if (controlBehavior != null) {\n            if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) {\n                return Result.ofFail(-1, \"controlBehavior must be in [0, 1, 2], but \" + controlBehavior + \" got\");\n            }\n            if (controlBehavior == 1 && warmUpPeriodSec == null) {\n                return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n            }\n            if (controlBehavior == 2 && maxQueueingTimeMs == null) {\n                return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n            }\n            entity.setControlBehavior(controlBehavior);\n            if (warmUpPeriodSec != null) {\n                entity.setWarmUpPeriodSec(warmUpPeriodSec);\n            }\n            if (maxQueueingTimeMs != null) {\n                entity.setMaxQueueingTimeMs(maxQueueingTimeMs);\n            }\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"save entity fail\");\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish flow rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/delete.json\")\n    public Result<Long> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish flow rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineDiscovery;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Controller\n@RequestMapping(value = \"/registry\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class MachineRegistryController {\n\n    private final Logger logger = LoggerFactory.getLogger(MachineRegistryController.class);\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @ResponseBody\n    @RequestMapping(\"/machine\")\n    public Result<?> receiveHeartBeat(String app, Long version, String v, String hostname, String ip, Integer port) {\n        if (app == null) {\n            app = MachineDiscovery.UNKNOWN_APP_NAME;\n        }\n        if (ip == null) {\n            return Result.ofFail(-1, \"ip can't be null\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (port == -1) {\n            logger.info(\"Receive heartbeat from \" + ip + \" but port not set yet\");\n            return Result.ofFail(-1, \"your port not set yet\");\n        }\n        String sentinelVersion = StringUtil.isEmpty(v) ? \"unknown\" : v;\n        version = version == null ? System.currentTimeMillis() : version;\n        try {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(app);\n            machineInfo.setHostname(hostname);\n            machineInfo.setIp(ip);\n            machineInfo.setPort(port);\n            machineInfo.setHeartbeatVersion(version);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setVersion(sentinelVersion);\n            appManagement.addMachine(machineInfo);\n            return Result.ofSuccessMsg(\"success\");\n        } catch (Exception e) {\n            logger.error(\"Receive heartbeat error\", e);\n            return Result.ofFail(-1, e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MetricController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.MetricVo;\n\n/**\n * @author leyou\n */\n@Controller\n@RequestMapping(value = \"/metric\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class MetricController {\n\n    private static Logger logger = LoggerFactory.getLogger(MetricController.class);\n\n    private static final long maxQueryIntervalMs = 1000 * 60 * 60;\n\n    @Autowired\n    private MetricsRepository<MetricEntity> metricStore;\n\n    @ResponseBody\n    @RequestMapping(\"/queryTopResourceMetric.json\")\n    public Result<?> queryTopResourceMetric(final String app,\n                                            Integer pageIndex,\n                                            Integer pageSize,\n                                            Boolean desc,\n                                            Long startTime, Long endTime, String searchKey) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (pageIndex == null || pageIndex <= 0) {\n            pageIndex = 1;\n        }\n        if (pageSize == null) {\n            pageSize = 6;\n        }\n        if (pageSize >= 20) {\n            pageSize = 20;\n        }\n        if (desc == null) {\n            desc = true;\n        }\n        if (endTime == null) {\n            endTime = System.currentTimeMillis();\n        }\n        if (startTime == null) {\n            startTime = endTime - 1000 * 60 * 5;\n        }\n        if (endTime - startTime > maxQueryIntervalMs) {\n            return Result.ofFail(-1, \"time intervalMs is too big, must <= 1h\");\n        }\n        List<String> resources = metricStore.listResourcesOfApp(app);\n        logger.debug(\"queryTopResourceMetric(), resources.size()={}\", resources.size());\n\n        if (resources == null || resources.isEmpty()) {\n            return Result.ofSuccess(null);\n        }\n        if (!desc) {\n            Collections.reverse(resources);\n        }\n        if (StringUtil.isNotEmpty(searchKey)) {\n            List<String> searched = new ArrayList<>();\n            for (String resource : resources) {\n                if (resource.contains(searchKey)) {\n                    searched.add(resource);\n                }\n            }\n            resources = searched;\n        }\n        int totalPage = (resources.size() + pageSize - 1) / pageSize;\n        List<String> topResource = new ArrayList<>();\n        if (pageIndex <= totalPage) {\n            topResource = resources.subList((pageIndex - 1) * pageSize,\n                Math.min(pageIndex * pageSize, resources.size()));\n        }\n        final Map<String, Iterable<MetricVo>> map = new ConcurrentHashMap<>();\n        logger.debug(\"topResource={}\", topResource);\n        long time = System.currentTimeMillis();\n        for (final String resource : topResource) {\n            List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(\n                app, resource, startTime, endTime);\n            logger.debug(\"resource={}, entities.size()={}\", resource, entities == null ? \"null\" : entities.size());\n            List<MetricVo> vos = MetricVo.fromMetricEntities(entities, resource);\n            Iterable<MetricVo> vosSorted = sortMetricVoAndDistinct(vos);\n            map.put(resource, vosSorted);\n        }\n        logger.debug(\"queryTopResourceMetric() total query time={} ms\", System.currentTimeMillis() - time);\n        Map<String, Object> resultMap = new HashMap<>(16);\n        resultMap.put(\"totalCount\", resources.size());\n        resultMap.put(\"totalPage\", totalPage);\n        resultMap.put(\"pageIndex\", pageIndex);\n        resultMap.put(\"pageSize\", pageSize);\n\n        Map<String, Iterable<MetricVo>> map2 = new LinkedHashMap<>();\n        // order matters.\n        for (String identity : topResource) {\n            map2.put(identity, map.get(identity));\n        }\n        resultMap.put(\"metric\", map2);\n        return Result.ofSuccess(resultMap);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/queryByAppAndResource.json\")\n    public Result<?> queryByAppAndResource(String app, String identity, Long startTime, Long endTime) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(identity)) {\n            return Result.ofFail(-1, \"identity can't be null or empty\");\n        }\n        if (endTime == null) {\n            endTime = System.currentTimeMillis();\n        }\n        if (startTime == null) {\n            startTime = endTime - 1000 * 60;\n        }\n        if (endTime - startTime > maxQueryIntervalMs) {\n            return Result.ofFail(-1, \"time intervalMs is too big, must <= 1h\");\n        }\n        List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(\n            app, identity, startTime, endTime);\n        List<MetricVo> vos = MetricVo.fromMetricEntities(entities, identity);\n        return Result.ofSuccess(sortMetricVoAndDistinct(vos));\n    }\n\n    private Iterable<MetricVo> sortMetricVoAndDistinct(List<MetricVo> vos) {\n        if (vos == null) {\n            return null;\n        }\n        Map<Long, MetricVo> map = new TreeMap<>();\n        for (MetricVo vo : vos) {\n            MetricVo oldVo = map.get(vo.getTimestamp());\n            if (oldVo == null || vo.getGmtCreate() > oldVo.getGmtCreate()) {\n                map.put(vo.getTimestamp(), vo);\n            }\n        }\n        return map.values();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.slots.block.RuleConstant;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@RestController\n@RequestMapping(value = \"/paramFlow\")\npublic class ParamFlowRuleController {\n\n    private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n    @Autowired\n    private RuleRepository<ParamFlowRuleEntity, Long> repository;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    private boolean checkIfSupported(String app, String ip, int port) {\n        try {\n            return Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version020)))\n                .orElse(true);\n            // If error occurred or cannot retrieve machine info, return true.\n        } catch (Exception ex) {\n            return true;\n        }\n    }\n\n    @GetMapping(\"/rules\")\n    public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(HttpServletRequest request,\n                                                                        @RequestParam String app,\n                                                                        @RequestParam String ip,\n                                                                        @RequestParam Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        if (!checkIfSupported(app, ip, port)) {\n            return unsupportedVersion();\n        }\n        try {\n            return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)\n                .thenApply(repository::saveAll)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when querying parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private boolean isNotSupported(Throwable ex) {\n        return ex instanceof CommandNotFoundException;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<ParamFlowRuleEntity> apiAddParamFlowRule(HttpServletRequest request,\n                                                           @RequestBody ParamFlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {\n            return unsupportedVersion();\n        }\n        entity.setId(null);\n        entity.getRule().setResource(entity.getResource().trim());\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();\n            return Result.ofSuccess(entity);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when adding new parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when adding new parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"bad rule body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getIp())) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (entity.getPort() == null || entity.getPort() <= 0) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        if (entity.getRule() == null) {\n            return Result.ofFail(-1, \"rule can't be null\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource name cannot be null or empty\");\n        }\n        if (entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be valid\");\n        }\n        if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) {\n            return Result.ofFail(-1, \"Unknown mode (blockGrade) for parameter flow control\");\n        }\n        if (entity.getParamIdx() == null || entity.getParamIdx() < 0) {\n            return Result.ofFail(-1, \"paramIdx should be valid\");\n        }\n        return null;\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(HttpServletRequest request,\n                                                              @PathVariable(\"id\") Long id,\n                                                              @RequestBody ParamFlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        ParamFlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" does not exist\");\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE);\n        Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {\n            return unsupportedVersion();\n        }\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();\n            return Result.ofSuccess(entity);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when updating parameter flow rules, id=\" + id, ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when updating parameter flow rules, id=\" + id, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id cannot be null\");\n        }\n        ParamFlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n            publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();\n            return Result.ofSuccess(id);\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when deleting parameter flow rules\", ex.getCause());\n            if (isNotSupported(ex.getCause())) {\n                return unsupportedVersion();\n            } else {\n                return Result.ofThrowable(-1, ex.getCause());\n            }\n        } catch (Throwable throwable) {\n            logger.error(\"Error when deleting parameter flow rules\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {\n        List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);\n    }\n\n    private <R> Result<R> unsupportedVersion() {\n        return Result.ofFail(4041,\n            \"Sentinel client not supported for parameter flow control (unsupported version or dependency absent)\");\n    }\n\n    private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ResourceController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.domain.vo.ResourceVo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Carpenter Lee\n */\n@RestController\n@RequestMapping(value = \"/resource\")\npublic class ResourceController {\n\n    private static Logger logger = LoggerFactory.getLogger(ResourceController.class);\n\n    @Autowired\n    private SentinelApiClient httpFetcher;\n\n    /**\n     * Fetch real time statistics info of the machine.\n     *\n     * @param ip        ip to fetch\n     * @param port      port of the ip\n     * @param type      one of [root, default, cluster], 'root' means fetching from tree root node, 'default' means\n     *                  fetching from tree default node, 'cluster' means fetching from cluster node.\n     * @param searchKey key to search\n     * @return node statistics info.\n     */\n    @GetMapping(\"/machineResource.json\")\n    public Result<List<ResourceVo>> fetchResourceChainListOfMachine(String ip, Integer port, String type,\n                                                                    String searchKey) {\n        if (StringUtil.isEmpty(ip) || port == null) {\n            return Result.ofFail(-1, \"invalid param, give ip, port\");\n        }\n        final String ROOT = \"root\";\n        final String DEFAULT = \"default\";\n        if (StringUtil.isEmpty(type)) {\n            type = ROOT;\n        }\n        if (ROOT.equalsIgnoreCase(type) || DEFAULT.equalsIgnoreCase(type)) {\n            List<NodeVo> nodeVos = httpFetcher.fetchResourceOfMachine(ip, port, type);\n            if (nodeVos == null) {\n                return Result.ofSuccess(null);\n            }\n            ResourceTreeNode treeNode = ResourceTreeNode.fromNodeVoList(nodeVos);\n            treeNode.searchIgnoreCase(searchKey);\n            return Result.ofSuccess(ResourceVo.fromResourceTreeNode(treeNode));\n        } else {\n            // Normal (cluster node).\n            List<NodeVo> nodeVos = httpFetcher.fetchClusterNodeOfMachine(ip, port, true);\n            if (nodeVos == null) {\n                return Result.ofSuccess(null);\n            }\n            if (StringUtil.isNotEmpty(searchKey)) {\n                nodeVos = nodeVos.stream().filter(node -> node.getResource()\n                    .toLowerCase().contains(searchKey.toLowerCase()))\n                    .collect(Collectors.toList());\n            }\n            return Result.ofSuccess(ResourceVo.fromNodeVoList(nodeVos));\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author leyou(lihao)\n */\n@Controller\n@RequestMapping(value = \"/system\", produces = MediaType.APPLICATION_JSON_VALUE)\npublic class SystemController {\n    private static Logger logger = LoggerFactory.getLogger(SystemController.class);\n\n    @Autowired\n    private InMemSystemRuleStore repository;\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @ResponseBody\n    @RequestMapping(\"/rules.json\")\n    Result<List<SystemRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        try {\n            List<SystemRuleEntity> rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port);\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"queryApps error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private int countNotNullAndNotNegative(Number... values) {\n        int notNullCount = 0;\n        for (int i = 0; i < values.length; i++) {\n            if (values[i] != null && values[i].doubleValue() >= 0) {\n                notNullCount++;\n            }\n        }\n        return notNullCount;\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/new.json\")\n    Result<?> add(HttpServletRequest request,\n                  String app, String ip, Integer port, Double avgLoad, Long avgRt, Long maxThread, Double qps) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.WRITE_RULE);\n        if (StringUtil.isBlank(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(ip)) {\n            return Result.ofFail(-1, \"ip can't be null or empty\");\n        }\n        if (port == null) {\n            return Result.ofFail(-1, \"port can't be null\");\n        }\n        int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps);\n        if (notNullCount != 1) {\n            return Result.ofFail(-1, \"only one of [avgLoad, avgRt, maxThread, qps] \"\n                + \"value must be set >= 0, but \" + notNullCount + \" values get\");\n        }\n        SystemRuleEntity entity = new SystemRuleEntity();\n        entity.setApp(app.trim());\n        entity.setIp(ip.trim());\n        entity.setPort(port);\n        // -1 is a fake value\n        if (avgLoad != null) {\n            entity.setAvgLoad(avgLoad);\n        } else {\n            entity.setAvgLoad(-1D);\n        }\n        if (avgRt != null) {\n            entity.setAvgRt(avgRt);\n        } else {\n            entity.setAvgRt(-1L);\n        }\n        if (maxThread != null) {\n            entity.setMaxThread(maxThread);\n        } else {\n            entity.setMaxThread(-1L);\n        }\n        if (qps != null) {\n            entity.setQps(qps);\n        } else {\n            entity.setQps(-1D);\n        }\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"add error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(app, ip, port)) {\n            logger.info(\"publish system rules fail after rule add\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/save.json\")\n    Result<?> updateIfNotNull(HttpServletRequest request,\n                              Long id, String app, Double avgLoad, Long avgRt, Long maxThread, Double qps) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        SystemRuleEntity entity = repository.findById(id);\n        if (entity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" dose not exist\");\n        }\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n        if (StringUtil.isNotBlank(app)) {\n            entity.setApp(app.trim());\n        }\n        if (avgLoad != null) {\n            if (avgLoad < 0) {\n                return Result.ofFail(-1, \"avgLoad must >= 0\");\n            }\n            entity.setAvgLoad(avgLoad);\n        }\n        if (avgRt != null) {\n            if (avgRt < 0) {\n                return Result.ofFail(-1, \"avgRt must >= 0\");\n            }\n            entity.setAvgRt(avgRt);\n        }\n        if (maxThread != null) {\n            if (maxThread < 0) {\n                return Result.ofFail(-1, \"maxThread must >= 0\");\n            }\n            entity.setMaxThread(maxThread);\n        }\n        if (qps != null) {\n            if (qps < 0) {\n                return Result.ofFail(-1, \"qps must >= 0\");\n            }\n            entity.setQps(qps);\n        }\n        Date date = new Date();\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n        } catch (Throwable throwable) {\n            logger.error(\"save error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {\n            logger.info(\"publish system rules fail after rule update\");\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @ResponseBody\n    @RequestMapping(\"/delete.json\")\n    Result<?> delete(HttpServletRequest request, Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null) {\n            return Result.ofFail(-1, \"id can't be null\");\n        }\n        SystemRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n        } catch (Throwable throwable) {\n            logger.error(\"delete error:\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {\n            logger.info(\"publish system rules fail after rule delete\");\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private boolean publishRules(String app, String ip, Integer port) {\n        List<SystemRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));\n        return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterAssignController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.cluster;\n\nimport java.util.Collections;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest;\nimport com.alibaba.csp.sentinel.dashboard.service.ClusterAssignService;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\n@RestController\n@RequestMapping(\"/cluster/assign\")\npublic class ClusterAssignController {\n\n    private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class);\n\n    @Autowired\n    private ClusterAssignService clusterAssignService;\n\n    @PostMapping(\"/all_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiAssignAllClusterServersOfApp(@PathVariable String app,\n                                                                            @RequestBody\n                                                                                ClusterAppFullAssignRequest assignRequest) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (assignRequest == null || assignRequest.getClusterMap() == null\n            || assignRequest.getRemainingList() == null) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(),\n                assignRequest.getRemainingList()));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when assigning full cluster servers for app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @PostMapping(\"/single_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiAssignSingleClusterServersOfApp(@PathVariable String app,\n                                                                               @RequestBody ClusterAppSingleServerAssignRequest assignRequest) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (assignRequest == null || assignRequest.getClusterMap() == null) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()),\n                assignRequest.getRemainingList()));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when assigning single cluster servers for app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @PostMapping(\"/unbind_server/{app}\")\n    public Result<ClusterAppAssignResultVO> apiUnbindClusterServersOfApp(@PathVariable String app,\n                                                                         @RequestBody Set<String> machineIds) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (machineIds == null || machineIds.isEmpty()) {\n            return Result.ofFail(-1, \"bad request body\");\n        }\n        try {\n            return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds));\n        } catch (Throwable throwable) {\n            logger.error(\"Error when unbinding cluster server {} for app <{}>\", machineIds, app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterConfigController.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.cluster;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.ExecutionException;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;\nimport com.alibaba.csp.sentinel.dashboard.service.ClusterConfigService;\nimport com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils;\nimport com.alibaba.csp.sentinel.dashboard.util.VersionUtils;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@RestController\n@RequestMapping(value = \"/cluster\")\npublic class ClusterConfigController {\n\n    private final Logger logger = LoggerFactory.getLogger(ClusterConfigController.class);\n\n    private final SentinelVersion version140 = new SentinelVersion().setMajorVersion(1).setMinorVersion(4);\n\n    @Autowired\n    private AppManagement appManagement;\n\n    @Autowired\n    private ClusterConfigService clusterConfigService;\n\n    @PostMapping(\"/config/modify_single\")\n    public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {\n        if (StringUtil.isBlank(payload)) {\n            return Result.ofFail(-1, \"empty request body\");\n        }\n        try {\n            JSONObject body = JSON.parseObject(payload);\n            if (body.containsKey(KEY_MODE)) {\n                int mode = body.getInteger(KEY_MODE);\n                switch (mode) {\n                    case ClusterStateManager.CLUSTER_CLIENT:\n                        ClusterClientModifyRequest data = JSON.parseObject(payload, ClusterClientModifyRequest.class);\n                        Result<Boolean> res = checkValidRequest(data);\n                        if (res != null) {\n                            return res;\n                        }\n                        clusterConfigService.modifyClusterClientConfig(data).get();\n                        return Result.ofSuccess(true);\n                    case ClusterStateManager.CLUSTER_SERVER:\n                        ClusterServerModifyRequest d = JSON.parseObject(payload, ClusterServerModifyRequest.class);\n                        Result<Boolean> r = checkValidRequest(d);\n                        if (r != null) {\n                            return r;\n                        }\n                        // TODO: bad design here, should refactor!\n                        clusterConfigService.modifyClusterServerConfig(d).get();\n                        return Result.ofSuccess(true);\n                    default:\n                        return Result.ofFail(-1, \"invalid mode\");\n                }\n            }\n            return Result.ofFail(-1, \"invalid parameter\");\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when modifying cluster config\", ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable ex) {\n            logger.error(\"Error when modifying cluster config\", ex);\n            return Result.ofFail(-1, ex.getMessage());\n        }\n    }\n\n    private <T> Result<T> errorResponse(ExecutionException ex) {\n        if (isNotSupported(ex.getCause())) {\n            return unsupportedVersion();\n        } else {\n            return Result.ofThrowable(-1, ex.getCause());\n        }\n    }\n\n    @GetMapping(\"/state_single\")\n    public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,\n                                                              @RequestParam String ip,\n                                                              @RequestParam Integer port) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        if (StringUtil.isEmpty(ip)) {\n            return Result.ofFail(-1, \"ip cannot be null or empty\");\n        }\n        if (port == null || port <= 0) {\n            return Result.ofFail(-1, \"Invalid parameter: port\");\n        }\n        if (!checkIfSupported(app, ip, port)) {\n            return unsupportedVersion();\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app, ip, port)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster state\", ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster state\", throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/server_state/{app}\")\n    public Result<List<AppClusterServerStateWrapVO>> apiGetClusterServerStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(ClusterEntityUtils::wrapToAppClusterServerState)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster server state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster server state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/client_state/{app}\")\n    public Result<List<AppClusterClientStateWrapVO>> apiGetClusterClientStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(ClusterEntityUtils::wrapToAppClusterClientState)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster token client state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster token client state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    @GetMapping(\"/state/{app}\")\n    public Result<List<ClusterUniversalStatePairVO>> apiGetClusterStateOfApp(@PathVariable String app) {\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app cannot be null or empty\");\n        }\n        try {\n            return clusterConfigService.getClusterUniversalState(app)\n                .thenApply(Result::ofSuccess)\n                .get();\n        } catch (ExecutionException ex) {\n            logger.error(\"Error when fetching cluster state of app: \" + app, ex.getCause());\n            return errorResponse(ex);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when fetching cluster state of app: \" + app, throwable);\n            return Result.ofFail(-1, throwable.getMessage());\n        }\n    }\n\n    private boolean isNotSupported(Throwable ex) {\n        return ex instanceof CommandNotFoundException;\n    }\n\n    private boolean checkIfSupported(String app, String ip, int port) {\n        try {\n            return Optional.ofNullable(appManagement.getDetailApp(app))\n                .flatMap(e -> e.getMachine(ip, port))\n                .flatMap(m -> VersionUtils.parseVersion(m.getVersion())\n                    .map(v -> v.greaterOrEqual(version140)))\n                .orElse(true);\n            // If error occurred or cannot retrieve machine info, return true.\n        } catch (Exception ex) {\n            return true;\n        }\n    }\n\n    private Result<Boolean> checkValidRequest(ClusterModifyRequest request) {\n        if (StringUtil.isEmpty(request.getApp())) {\n            return Result.ofFail(-1, \"app cannot be empty\");\n        }\n        if (StringUtil.isEmpty(request.getIp())) {\n            return Result.ofFail(-1, \"ip cannot be empty\");\n        }\n        if (request.getPort() == null || request.getPort() < 0) {\n            return Result.ofFail(-1, \"invalid port\");\n        }\n        if (request.getMode() == null || request.getMode() < 0) {\n            return Result.ofFail(-1, \"invalid mode\");\n        }\n        if (!checkIfSupported(request.getApp(), request.getIp(), request.getPort())) {\n            return unsupportedVersion();\n        }\n        return null;\n    }\n\n    private <R> Result<R> unsupportedVersion() {\n        return Result.ofFail(4041, \"Sentinel client not supported for cluster flow control (unsupported version or dependency absent)\");\n    }\n\n    private static final String KEY_MODE = \"mode\";\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.controller.v2;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.dashboard.domain.Result;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Flow rule controller (v2).\n *\n * @author Eric Zhao\n * @since 1.4.0\n */\n@RestController\n@RequestMapping(value = \"/v2/flow\")\npublic class FlowControllerV2 {\n\n    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);\n\n    @Autowired\n    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;\n\n    @Autowired\n    @Qualifier(\"flowRuleNacosProvider\")\n    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;\n    @Autowired\n    @Qualifier(\"flowRuleNacosPublisher\")\n    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;\n\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @GetMapping(\"/rules\")\n    public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(app, PrivilegeType.READ_RULE);\n\n        if (StringUtil.isEmpty(app)) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        try {\n            List<FlowRuleEntity> rules = ruleProvider.getRules(app);\n            if (rules != null && !rules.isEmpty()) {\n                for (FlowRuleEntity entity : rules) {\n                    entity.setApp(app);\n                    if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {\n                        entity.setId(entity.getClusterConfig().getFlowId());\n                    }\n                }\n            }\n            rules = repository.saveAll(rules);\n            return Result.ofSuccess(rules);\n        } catch (Throwable throwable) {\n            logger.error(\"Error when querying flow rules\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n    }\n\n    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {\n        if (entity == null) {\n            return Result.ofFail(-1, \"invalid body\");\n        }\n        if (StringUtil.isBlank(entity.getApp())) {\n            return Result.ofFail(-1, \"app can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getLimitApp())) {\n            return Result.ofFail(-1, \"limitApp can't be null or empty\");\n        }\n        if (StringUtil.isBlank(entity.getResource())) {\n            return Result.ofFail(-1, \"resource can't be null or empty\");\n        }\n        if (entity.getGrade() == null) {\n            return Result.ofFail(-1, \"grade can't be null\");\n        }\n        if (entity.getGrade() != 0 && entity.getGrade() != 1) {\n            return Result.ofFail(-1, \"grade must be 0 or 1, but \" + entity.getGrade() + \" got\");\n        }\n        if (entity.getCount() == null || entity.getCount() < 0) {\n            return Result.ofFail(-1, \"count should be at lease zero\");\n        }\n        if (entity.getStrategy() == null) {\n            return Result.ofFail(-1, \"strategy can't be null\");\n        }\n        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {\n            return Result.ofFail(-1, \"refResource can't be null or empty when strategy!=0\");\n        }\n        if (entity.getControlBehavior() == null) {\n            return Result.ofFail(-1, \"controlBehavior can't be null\");\n        }\n        int controlBehavior = entity.getControlBehavior();\n        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {\n            return Result.ofFail(-1, \"warmUpPeriodSec can't be null when controlBehavior==1\");\n        }\n        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {\n            return Result.ofFail(-1, \"maxQueueingTimeMs can't be null when controlBehavior==2\");\n        }\n        if (entity.isClusterMode() && entity.getClusterConfig() == null) {\n            return Result.ofFail(-1, \"cluster config should be valid\");\n        }\n        return null;\n    }\n\n    @PostMapping(\"/rule\")\n    public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);\n\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n        entity.setId(null);\n        Date date = new Date();\n        entity.setGmtCreate(date);\n        entity.setGmtModified(date);\n        entity.setLimitApp(entity.getLimitApp().trim());\n        entity.setResource(entity.getResource().trim());\n        try {\n            entity = repository.save(entity);\n            publishRules(entity.getApp());\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to add flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @PutMapping(\"/rule/{id}\")\n    public Result<FlowRuleEntity> apiUpdateFlowRule(HttpServletRequest request,\n                                                    @PathVariable(\"id\") Long id,\n                                                    @RequestBody FlowRuleEntity entity) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofFail(-1, \"id \" + id + \" does not exist\");\n        }\n        if (entity == null) {\n            return Result.ofFail(-1, \"invalid body\");\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE);\n\n        entity.setApp(oldEntity.getApp());\n        entity.setIp(oldEntity.getIp());\n        entity.setPort(oldEntity.getPort());\n        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);\n        if (checkResult != null) {\n            return checkResult;\n        }\n\n        entity.setId(id);\n        Date date = new Date();\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(date);\n        try {\n            entity = repository.save(entity);\n            if (entity == null) {\n                return Result.ofFail(-1, \"save entity fail\");\n            }\n            publishRules(oldEntity.getApp());\n        } catch (Throwable throwable) {\n            logger.error(\"Failed to update flow rule\", throwable);\n            return Result.ofThrowable(-1, throwable);\n        }\n        return Result.ofSuccess(entity);\n    }\n\n    @DeleteMapping(\"/rule/{id}\")\n    public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable(\"id\") Long id) {\n        AuthUser authUser = authService.getAuthUser(request);\n        if (id == null || id <= 0) {\n            return Result.ofFail(-1, \"Invalid id\");\n        }\n        FlowRuleEntity oldEntity = repository.findById(id);\n        if (oldEntity == null) {\n            return Result.ofSuccess(null);\n        }\n        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);\n        try {\n            repository.delete(id);\n            publishRules(oldEntity.getApp());\n        } catch (Exception e) {\n            return Result.ofFail(-1, e.getMessage());\n        }\n        return Result.ofSuccess(id);\n    }\n\n    private void publishRules(/*@NonNull*/ String app) throws Exception {\n        List<FlowRuleEntity> rules = repository.findAllByApp(app);\n        rulePublisher.publish(app, rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\n\n/**\n * @author leyou\n */\npublic class ApplicationEntity {\n\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String activeConsole;\n    private Date lastFetch;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getActiveConsole() {\n        return activeConsole;\n    }\n\n    public Date getLastFetch() {\n        return lastFetch;\n    }\n\n    public void setLastFetch(Date lastFetch) {\n        this.lastFetch = lastFetch;\n    }\n\n    public void setActiveConsole(String activeConsole) {\n        this.activeConsole = activeConsole;\n    }\n\n    public AppInfo toAppInfo() {\n        return new AppInfo(app);\n    }\n\n    @Override\n    public String toString() {\n        return \"ApplicationEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", activeConsole='\" + activeConsole + '\\'' +\n            \", lastFetch=\" + lastFetch +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * @author leyou\n */\npublic class MachineEntity {\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String ip;\n    private String hostname;\n    private Date timestamp;\n    private Integer port;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public Date getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Date timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    public MachineInfo toMachineInfo() {\n        MachineInfo machineInfo = new MachineInfo();\n\n        machineInfo.setApp(app);\n        machineInfo.setHostname(hostname);\n        machineInfo.setIp(ip);\n        machineInfo.setPort(port);\n        machineInfo.setLastHeartbeat(timestamp.getTime());\n        machineInfo.setHeartbeatVersion(timestamp.getTime());\n\n        return machineInfo;\n    }\n\n    @Override\n    public String toString() {\n        return \"MachineEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", hostname='\" + hostname + '\\'' +\n            \", timestamp=\" + timestamp +\n            \", port=\" + port +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\n/**\n * @author leyou\n */\npublic class MetricEntity {\n    private Long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    /**\n     * 监控信息的时间戳\n     */\n    private Date timestamp;\n    private String resource;\n    private Long passQps;\n    private Long successQps;\n    private Long blockQps;\n    private Long exceptionQps;\n\n    /**\n     * summary rt of all success exit qps.\n     */\n    private double rt;\n\n    /**\n     * 本次聚合的总条数\n     */\n    private int count;\n\n    private int resourceCode;\n\n    public static MetricEntity copyOf(MetricEntity oldEntity) {\n        MetricEntity entity = new MetricEntity();\n        entity.setId(oldEntity.getId());\n        entity.setGmtCreate(oldEntity.getGmtCreate());\n        entity.setGmtModified(oldEntity.getGmtModified());\n        entity.setApp(oldEntity.getApp());\n        entity.setTimestamp(oldEntity.getTimestamp());\n        entity.setResource(oldEntity.getResource());\n        entity.setPassQps(oldEntity.getPassQps());\n        entity.setBlockQps(oldEntity.getBlockQps());\n        entity.setSuccessQps(oldEntity.getSuccessQps());\n        entity.setExceptionQps(oldEntity.getExceptionQps());\n        entity.setRt(oldEntity.getRt());\n        entity.setCount(oldEntity.getCount());\n        entity.setResource(oldEntity.getResource());\n        return entity;\n    }\n\n    public synchronized void addPassQps(Long passQps) {\n        this.passQps += passQps;\n    }\n\n    public synchronized void addBlockQps(Long blockQps) {\n        this.blockQps += blockQps;\n    }\n\n    public synchronized void addExceptionQps(Long exceptionQps) {\n        this.exceptionQps += exceptionQps;\n    }\n\n    public synchronized void addCount(int count) {\n        this.count += count;\n    }\n\n    public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) {\n        this.rt += avgRt * successQps;\n        this.successQps += successQps;\n    }\n\n    /**\n     * {@link #rt} = {@code avgRt * successQps}\n     *\n     * @param avgRt      average rt of {@code successQps}\n     * @param successQps\n     */\n    public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) {\n        this.rt = avgRt * successQps;\n        this.successQps = successQps;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Date getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Date timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n        this.resourceCode = resource.hashCode();\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public double getRt() {\n        return rt;\n    }\n\n    public void setRt(double rt) {\n        this.rt = rt;\n    }\n\n    public int getCount() {\n        return count;\n    }\n\n    public void setCount(int count) {\n        this.count = count;\n    }\n\n    public int getResourceCode() {\n        return resourceCode;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    @Override\n    public String toString() {\n        return \"MetricEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", timestamp=\" + timestamp +\n            \", resource='\" + resource + '\\'' +\n            \", passQps=\" + passQps +\n            \", blockQps=\" + blockQps +\n            \", successQps=\" + successQps +\n            \", exceptionQps=\" + exceptionQps +\n            \", rt=\" + rt +\n            \", count=\" + count +\n            \", resourceCode=\" + resourceCode +\n            '}';\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport java.util.Date;\n\n/**\n * @author leyou\n */\npublic class MetricPositionEntity {\n    private long id;\n    private Date gmtCreate;\n    private Date gmtModified;\n    private String app;\n    private String ip;\n    /**\n     * Sentinel在该应用上使用的端口\n     */\n    private int port;\n\n    /**\n     * 机器名，冗余字段\n     */\n    private String hostname;\n\n    /**\n     * 上一次拉取的最晚时间戳\n     */\n    private Date lastFetch;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public Date getLastFetch() {\n        return lastFetch;\n    }\n\n    public void setLastFetch(Date lastFetch) {\n        this.lastFetch = lastFetch;\n    }\n\n    @Override\n    public String toString() {\n        return \"MetricPositionEntity{\" +\n            \"id=\" + id +\n            \", gmtCreate=\" + gmtCreate +\n            \", gmtModified=\" + gmtModified +\n            \", app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", hostname='\" + hostname + '\\'' +\n            \", lastFetch=\" + lastFetch +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersion.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class SentinelVersion {\n    private int majorVersion;\n    private int minorVersion;\n    private int fixVersion;\n    private String postfix;\n    \n    public SentinelVersion() {\n        this(0, 0, 0);\n    }\n    \n    public SentinelVersion(int major, int minor, int fix) {\n        this(major, minor, fix, null);\n    }\n    \n    public SentinelVersion(int major, int minor, int fix, String postfix) {\n        this.majorVersion = major;\n        this.minorVersion = minor;\n        this.fixVersion = fix;\n        this.postfix = postfix;\n    }\n    \n    /**\n     * 000, 000, 000\n     */\n    public int getFullVersion() {\n        return majorVersion * 1000000 + minorVersion * 1000 + fixVersion;\n    }\n\n    public int getMajorVersion() {\n        return majorVersion;\n    }\n\n    public SentinelVersion setMajorVersion(int majorVersion) {\n        this.majorVersion = majorVersion;\n        return this;\n    }\n\n    public int getMinorVersion() {\n        return minorVersion;\n    }\n\n    public SentinelVersion setMinorVersion(int minorVersion) {\n        this.minorVersion = minorVersion;\n        return this;\n    }\n\n    public int getFixVersion() {\n        return fixVersion;\n    }\n\n    public SentinelVersion setFixVersion(int fixVersion) {\n        this.fixVersion = fixVersion;\n        return this;\n    }\n\n    public String getPostfix() {\n        return postfix;\n    }\n\n    public SentinelVersion setPostfix(String postfix) {\n        this.postfix = postfix;\n        return this;\n    }\n\n    public boolean greaterThan(SentinelVersion version) {\n        if (version == null) {\n            return true;\n        }\n        return getFullVersion() > version.getFullVersion();\n    }\n\n    public boolean greaterOrEqual(SentinelVersion version) {\n        if (version == null) {\n            return true;\n        }\n        return getFullVersion() >= version.getFullVersion();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) { return true; }\n        if (o == null || getClass() != o.getClass()) { return false; }\n\n        SentinelVersion that = (SentinelVersion)o;\n\n        if (getFullVersion() != that.getFullVersion()) { return false; }\n        return postfix != null ? postfix.equals(that.postfix) : that.postfix == null;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = majorVersion;\n        result = 31 * result + minorVersion;\n        result = 31 * result + fixVersion;\n        result = 31 * result + (postfix != null ? postfix.hashCode() : 0);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"SentinelVersion{\" +\n            \"majorVersion=\" + majorVersion +\n            \", minorVersion=\" + minorVersion +\n            \", fixVersion=\" + fixVersion +\n            \", postfix='\" + postfix + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AbstractRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.AbstractRule;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic abstract class AbstractRuleEntity<T extends AbstractRule> implements RuleEntity {\n\n    protected Long id;\n\n    protected String app;\n    protected String ip;\n    protected Integer port;\n\n    protected T rule;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public AbstractRuleEntity<T> setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public AbstractRuleEntity<T> setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public AbstractRuleEntity<T> setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public T getRule() {\n        return rule;\n    }\n\n    public AbstractRuleEntity<T> setRule(T rule) {\n        this.rule = rule;\n        return this;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public AbstractRuleEntity<T> setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n        return this;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public AbstractRuleEntity<T> setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AuthorityRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> {\n\n    public AuthorityRuleEntity() {}\n\n    public AuthorityRuleEntity(AuthorityRule authorityRule) {\n        AssertUtil.notNull(authorityRule, \"Authority rule should not be null\");\n        this.rule = authorityRule;\n    }\n\n    public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integer port, AuthorityRule rule) {\n        AuthorityRuleEntity entity = new AuthorityRuleEntity(rule);\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        return entity;\n    }\n\n    @JsonIgnore\n    public String getLimitApp() {\n        return rule.getLimitApp();\n    }\n\n    @JsonIgnore\n    public String getResource() {\n        return rule.getResource();\n    }\n\n    @JsonIgnore\n    public int getStrategy() {\n        return rule.getStrategy();\n    }\n    \n    @Override\n    public Rule toRule() {\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/DegradeRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;\n\n/**\n * @author leyou\n */\npublic class DegradeRuleEntity implements RuleEntity {\n    private Long id;\n    private String app;\n    private String ip;\n    private Integer port;\n    private String resource;\n    private String limitApp;\n    private Double count;\n    private Integer timeWindow;\n    /**\n     * 0 rt 限流; 1为异常;\n     */\n    private Integer grade;\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static DegradeRuleEntity fromDegradeRule(String app, String ip, Integer port, DegradeRule rule) {\n        DegradeRuleEntity entity = new DegradeRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setResource(rule.getResource());\n        entity.setLimitApp(rule.getLimitApp());\n        entity.setCount(rule.getCount());\n        entity.setTimeWindow(rule.getTimeWindow());\n        entity.setGrade(rule.getGrade());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public String getLimitApp() {\n        return limitApp;\n    }\n\n    public void setLimitApp(String limitApp) {\n        this.limitApp = limitApp;\n    }\n\n    public Double getCount() {\n        return count;\n    }\n\n    public void setCount(Double count) {\n        this.count = count;\n    }\n\n    public Integer getTimeWindow() {\n        return timeWindow;\n    }\n\n    public void setTimeWindow(Integer timeWindow) {\n        this.timeWindow = timeWindow;\n    }\n\n    public Integer getGrade() {\n        return grade;\n    }\n\n    public void setGrade(Integer grade) {\n        this.grade = grade;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public DegradeRule toRule() {\n        DegradeRule rule = new DegradeRule();\n        rule.setResource(resource);\n        rule.setLimitApp(limitApp);\n        rule.setCount(count);\n        rule.setTimeWindow(timeWindow);\n        rule.setGrade(grade);\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\n\n/**\n * @author leyou\n */\npublic class FlowRuleEntity implements RuleEntity {\n\n    private Long id;\n    private String app;\n    private String ip;\n    private Integer port;\n    private String limitApp;\n    private String resource;\n    /**\n     * 0为线程数;1为qps\n     */\n    private Integer grade;\n    private Double count;\n    /**\n     * 0为直接限流;1为关联限流;2为链路限流\n     ***/\n    private Integer strategy;\n    private String refResource;\n    /**\n     * 0. default, 1. warm up, 2. rate limiter\n     */\n    private Integer controlBehavior;\n    private Integer warmUpPeriodSec;\n    /**\n     * max queueing time in rate limiter behavior\n     */\n    private Integer maxQueueingTimeMs;\n\n    private boolean clusterMode;\n    /**\n     * Flow rule config for cluster mode.\n     */\n    private ClusterFlowConfig clusterConfig;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static FlowRuleEntity fromFlowRule(String app, String ip, Integer port, FlowRule rule) {\n        FlowRuleEntity entity = new FlowRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setLimitApp(rule.getLimitApp());\n        entity.setResource(rule.getResource());\n        entity.setGrade(rule.getGrade());\n        entity.setCount(rule.getCount());\n        entity.setStrategy(rule.getStrategy());\n        entity.setRefResource(rule.getRefResource());\n        entity.setControlBehavior(rule.getControlBehavior());\n        entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec());\n        entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs());\n        entity.setClusterMode(rule.isClusterMode());\n        entity.setClusterConfig(rule.getClusterConfig());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getLimitApp() {\n        return limitApp;\n    }\n\n    public void setLimitApp(String limitApp) {\n        this.limitApp = limitApp;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getGrade() {\n        return grade;\n    }\n\n    public void setGrade(Integer grade) {\n        this.grade = grade;\n    }\n\n    public Double getCount() {\n        return count;\n    }\n\n    public void setCount(Double count) {\n        this.count = count;\n    }\n\n    public Integer getStrategy() {\n        return strategy;\n    }\n\n    public void setStrategy(Integer strategy) {\n        this.strategy = strategy;\n    }\n\n    public String getRefResource() {\n        return refResource;\n    }\n\n    public void setRefResource(String refResource) {\n        this.refResource = refResource;\n    }\n\n    public Integer getControlBehavior() {\n        return controlBehavior;\n    }\n\n    public void setControlBehavior(Integer controlBehavior) {\n        this.controlBehavior = controlBehavior;\n    }\n\n    public Integer getWarmUpPeriodSec() {\n        return warmUpPeriodSec;\n    }\n\n    public void setWarmUpPeriodSec(Integer warmUpPeriodSec) {\n        this.warmUpPeriodSec = warmUpPeriodSec;\n    }\n\n    public Integer getMaxQueueingTimeMs() {\n        return maxQueueingTimeMs;\n    }\n\n    public void setMaxQueueingTimeMs(Integer maxQueueingTimeMs) {\n        this.maxQueueingTimeMs = maxQueueingTimeMs;\n    }\n\n    public boolean isClusterMode() {\n        return clusterMode;\n    }\n\n    public FlowRuleEntity setClusterMode(boolean clusterMode) {\n        this.clusterMode = clusterMode;\n        return this;\n    }\n\n    public ClusterFlowConfig getClusterConfig() {\n        return clusterConfig;\n    }\n\n    public FlowRuleEntity setClusterConfig(ClusterFlowConfig clusterConfig) {\n        this.clusterConfig = clusterConfig;\n        return this;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public FlowRule toRule() {\n        FlowRule flowRule = new FlowRule();\n        flowRule.setCount(this.count);\n        flowRule.setGrade(this.grade);\n        flowRule.setResource(this.resource);\n        flowRule.setLimitApp(this.limitApp);\n        flowRule.setRefResource(this.refResource);\n        flowRule.setStrategy(this.strategy);\n        if (this.controlBehavior != null) {\n            flowRule.setControlBehavior(controlBehavior);\n        }\n        if (this.warmUpPeriodSec != null) {\n            flowRule.setWarmUpPeriodSec(warmUpPeriodSec);\n        }\n        if (this.maxQueueingTimeMs != null) {\n            flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs);\n        }\n        flowRule.setClusterMode(clusterMode);\n        flowRule.setClusterConfig(clusterConfig);\n        return flowRule;\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {\n\n    public ParamFlowRuleEntity() {}\n\n    public ParamFlowRuleEntity(ParamFlowRule rule) {\n        AssertUtil.notNull(rule, \"Authority rule should not be null\");\n        this.rule = rule;\n    }\n\n    public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) {\n        ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule);\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        return entity;\n    }\n\n    @JsonIgnore\n    public String getLimitApp() {\n        return rule.getLimitApp();\n    }\n\n    @JsonIgnore\n    public String getResource() {\n        return rule.getResource();\n    }\n\n    @JsonIgnore\n    public int getGrade() {\n        return rule.getGrade();\n    }\n\n    @JsonIgnore\n    public Integer getParamIdx() {\n        return rule.getParamIdx();\n    }\n\n    @JsonIgnore\n    public double getCount() {\n        return rule.getCount();\n    }\n\n    @JsonIgnore\n    public List<ParamFlowItem> getParamFlowItemList() {\n        return rule.getParamFlowItemList();\n    }\n\n    @JsonIgnore\n    public boolean isClusterMode() {\n        return rule.isClusterMode();\n    }\n\n    @JsonIgnore\n    public ParamFlowClusterConfig getClusterConfig() {\n        return rule.getClusterConfig();\n    }\n    \n    @Override\n    public Rule toRule() {\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/RuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.block.Rule;\n\n/**\n * @author leyou\n */\npublic interface RuleEntity {\n\n    Long getId();\n\n    void setId(Long id);\n\n    String getApp();\n\n    String getIp();\n\n    Integer getPort();\n\n    Date getGmtCreate();\n    \n    Rule toRule();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;\n\nimport java.util.Date;\n\nimport com.alibaba.csp.sentinel.slots.system.SystemRule;\n\n/**\n * @author leyou\n */\npublic class SystemRuleEntity implements RuleEntity {\n\n    private Long id;\n\n    private String app;\n    private String ip;\n    private Integer port;\n    private Double avgLoad;\n    private Long avgRt;\n    private Long maxThread;\n    private Double qps;\n\n    private Date gmtCreate;\n    private Date gmtModified;\n\n    public static SystemRuleEntity fromSystemRule(String app, String ip, Integer port, SystemRule rule) {\n        SystemRuleEntity entity = new SystemRuleEntity();\n        entity.setApp(app);\n        entity.setIp(ip);\n        entity.setPort(port);\n        entity.setAvgLoad(rule.getHighestSystemLoad());\n        entity.setAvgRt(rule.getAvgRt());\n        entity.setMaxThread(rule.getMaxThread());\n        entity.setQps(rule.getQps());\n        return entity;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Double getAvgLoad() {\n        return avgLoad;\n    }\n\n    public void setAvgLoad(Double avgLoad) {\n        this.avgLoad = avgLoad;\n    }\n\n    public Long getAvgRt() {\n        return avgRt;\n    }\n\n    public void setAvgRt(Long avgRt) {\n        this.avgRt = avgRt;\n    }\n\n    public Long getMaxThread() {\n        return maxThread;\n    }\n\n    public void setMaxThread(Long maxThread) {\n        this.maxThread = maxThread;\n    }\n\n    public Double getQps() {\n        return qps;\n    }\n\n    public void setQps(Double qps) {\n        this.qps = qps;\n    }\n\n    @Override\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public SystemRule toRule() {\n        SystemRule rule = new SystemRule();\n        rule.setHighestSystemLoad(avgLoad);\n        rule.setAvgRt(avgRt);\n        rule.setMaxThread(maxThread);\n        rule.setQps(qps);\n        return rule;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\npublic class AppInfo {\n\n    private String app = \"\";\n\n    private Set<MachineInfo> machines = ConcurrentHashMap.newKeySet();\n\n    public AppInfo() {}\n\n    public AppInfo(String app) {\n        this.app = app;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    /**\n     * Get the current machines.\n     *\n     * @return a new copy of the current machines.\n     */\n    public Set<MachineInfo> getMachines() {\n        return new HashSet<>(machines);\n    }\n\n    @Override\n    public String toString() {\n        return \"AppInfo{\" + \"app='\" + app + \", machines=\" + machines + '}';\n    }\n\n    public boolean addMachine(MachineInfo machineInfo) {\n        machines.remove(machineInfo);\n        return machines.add(machineInfo);\n    }\n\n    public synchronized boolean removeMachine(String ip, int port) {\n        Iterator<MachineInfo> it = machines.iterator();\n        while (it.hasNext()) {\n            MachineInfo machine = it.next();\n            if (machine.getIp().equals(ip) && machine.getPort() == port) {\n                it.remove();\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public Optional<MachineInfo> getMachine(String ip, int port) {\n        return machines.stream()\n            .filter(e -> e.getIp().equals(ip) && e.getPort().equals(port))\n            .findFirst();\n    }\n\n    private boolean heartbeatJudge(final int threshold) {\n        if (machines.size() == 0) {\n            return false;\n        }\n        if (threshold > 0) {\n            long healthyCount = machines.stream()\n                .filter(MachineInfo::isHealthy)\n                .count();\n            if (healthyCount == 0) {\n                // No healthy machines.\n                return machines.stream()\n                    .max(Comparator.comparingLong(MachineInfo::getLastHeartbeat))\n                    .map(e -> System.currentTimeMillis() - e.getLastHeartbeat() < threshold)\n                    .orElse(false);\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Check whether current application has no healthy machines and should not be displayed.\n     *\n     * @return true if the application should be displayed in the sidebar, otherwise false\n     */\n    public boolean isShown() {\n        return heartbeatJudge(DashboardConfig.getHideAppNoMachineMillis());\n    }\n\n    /**\n     * Check whether current application has no healthy machines and should be removed.\n     *\n     * @return true if the application is dead and should be removed, otherwise false\n     */\n    public boolean isDead() {\n        return !heartbeatJudge(DashboardConfig.getRemoveAppNoMachineMillis());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.annotation.PostConstruct;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AppManagement implements MachineDiscovery {\n\n    @Autowired\n    private ApplicationContext context;\n\n    private MachineDiscovery machineDiscovery;\n\n    @PostConstruct\n    public void init() {\n        machineDiscovery = context.getBean(SimpleMachineDiscovery.class);\n    }\n\n    @Override\n    public Set<AppInfo> getBriefApps() {\n        return machineDiscovery.getBriefApps();\n    }\n\n    @Override\n    public long addMachine(MachineInfo machineInfo) {\n        return machineDiscovery.addMachine(machineInfo);\n    }\n    \n    @Override\n    public boolean removeMachine(String app, String ip, int port) {\n        return machineDiscovery.removeMachine(app, ip, port);\n    }\n\n    @Override\n    public List<String> getAppNames() {\n        return machineDiscovery.getAppNames();\n    }\n\n    @Override\n    public AppInfo getDetailApp(String app) {\n        return machineDiscovery.getDetailApp(app);\n    }\n    \n    @Override\n    public void removeApp(String app) {\n        machineDiscovery.removeApp(app);\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic interface MachineDiscovery {\n\n    String UNKNOWN_APP_NAME = \"CLUSTER_NOT_STARTED\";\n\n    List<String> getAppNames();\n\n    Set<AppInfo> getBriefApps();\n\n    AppInfo getDetailApp(String app);\n\n    /**\n     * Remove the given app from the application registry.\n     *\n     * @param app application name\n     * @since 1.5.0\n     */\n    void removeApp(String app);\n\n    long addMachine(MachineInfo machineInfo);\n\n    /**\n     * Remove the given machine instance from the application registry.\n     *\n     * @param app the application name of the machine\n     * @param ip machine IP\n     * @param port machine port\n     * @return true if removed, otherwise false\n     * @since 1.5.0\n     */\n    boolean removeMachine(String app, String ip, int port);\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.Objects;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\npublic class MachineInfo implements Comparable<MachineInfo> {\n\n    private String app = \"\";\n    private String hostname = \"\";\n    private String ip = \"\";\n    private Integer port = -1;\n    private long lastHeartbeat;\n    private long heartbeatVersion;\n\n    /**\n     * Indicates the version of Sentinel client (since 0.2.0).\n     */\n    private String version;\n\n    public static MachineInfo of(String app, String ip, Integer port) {\n        MachineInfo machineInfo = new MachineInfo();\n        machineInfo.setApp(app);\n        machineInfo.setIp(ip);\n        machineInfo.setPort(port);\n        return machineInfo;\n    }\n\n    public String toHostPort() {\n        return ip + \":\" + port;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public long getHeartbeatVersion() {\n        return heartbeatVersion;\n    }\n    \n    public void setHeartbeatVersion(long heartbeatVersion) {\n        this.heartbeatVersion = heartbeatVersion;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public MachineInfo setVersion(String version) {\n        this.version = version;\n        return this;\n    }\n    \n    public boolean isHealthy() {\n        long delta = System.currentTimeMillis() - lastHeartbeat;\n        return delta < DashboardConfig.getUnhealthyMachineMillis();\n    }\n    \n    /**\n     * whether dead should be removed\n     * \n     * @return\n     */\n    public boolean isDead() {\n        if (DashboardConfig.getAutoRemoveMachineMillis() > 0) {\n            long delta = System.currentTimeMillis() - lastHeartbeat;\n            return delta > DashboardConfig.getAutoRemoveMachineMillis();\n        }\n        return false;\n    }\n    \n    public long getLastHeartbeat() {\n        return lastHeartbeat;\n    }\n    \n    public void setLastHeartbeat(long lastHeartbeat) {\n        this.lastHeartbeat = lastHeartbeat;\n    }\n\n    @Override\n    public int compareTo(MachineInfo o) {\n        if (this == o) {\n            return 0;\n        }\n        if (!port.equals(o.getPort())) {\n            return port.compareTo(o.getPort());\n        }\n        if (!StringUtil.equals(app, o.getApp())) {\n            return app.compareToIgnoreCase(o.getApp());\n        }\n        return ip.compareToIgnoreCase(o.getIp());\n    }\n\n    @Override\n    public String toString() {\n        return new StringBuilder(\"MachineInfo {\")\n            .append(\"app='\").append(app).append('\\'')\n            .append(\", hostname='\").append(hostname).append('\\'')\n            .append(\", ip='\").append(ip).append('\\'')\n            .append(\", port=\").append(port)\n            .append(\", heartbeatVersion=\").append(heartbeatVersion)\n            .append(\", lastHeartbeat=\").append(lastHeartbeat)\n            .append(\", version='\").append(version).append('\\'')\n            .append(\", healthy=\").append(isHealthy())\n            .append('}').toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) { return true; }\n        if (!(o instanceof MachineInfo)) { return false; }\n        MachineInfo that = (MachineInfo)o;\n        return Objects.equals(app, that.app) &&\n            Objects.equals(ip, that.ip) &&\n            Objects.equals(port, that.port);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(app, ip, port);\n    }\n\n    /**\n     * Information for log\n     *\n     * @return\n     */\n    public String toLogString() {\n        return app + \"|\" + ip + \"|\" + port + \"|\" + version;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class SimpleMachineDiscovery implements MachineDiscovery {\n\n    private final ConcurrentMap<String, AppInfo> apps = new ConcurrentHashMap<>();\n\n    @Override\n    public long addMachine(MachineInfo machineInfo) {\n        AssertUtil.notNull(machineInfo, \"machineInfo cannot be null\");\n        AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), AppInfo::new);\n        appInfo.addMachine(machineInfo);\n        return 1;\n    }\n\n    @Override\n    public boolean removeMachine(String app, String ip, int port) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        AppInfo appInfo = apps.get(app);\n        if (appInfo != null) {\n            return appInfo.removeMachine(ip, port);\n        }\n        return false;\n    }\n\n    @Override\n    public List<String> getAppNames() {\n        return new ArrayList<>(apps.keySet());\n    }\n\n    @Override\n    public AppInfo getDetailApp(String app) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        return apps.get(app);\n    }\n\n    @Override\n    public Set<AppInfo> getBriefApps() {\n        return new HashSet<>(apps.values());\n    }\n\n    @Override\n    public void removeApp(String app) {\n        AssertUtil.assertNotBlank(app, \"app name cannot be blank\");\n        apps.remove(app);\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/ResourceTreeNode.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\n/**\n * @author leyou\n */\npublic class ResourceTreeNode {\n    private String id;\n    private String parentId;\n    private String resource;\n\n    private Integer threadNum;\n    private Long passQps;\n    private Long blockQps;\n    private Long totalQps;\n    private Long averageRt;\n    private Long successQps;\n    private Long exceptionQps;\n    private Long oneMinutePass;\n    private Long oneMinuteBlock;\n    private Long oneMinuteException;\n    private Long oneMinuteTotal;\n\n    private boolean visible = true;\n\n    private List<ResourceTreeNode> children = new ArrayList<>();\n\n    public static ResourceTreeNode fromNodeVoList(List<NodeVo> nodeVos) {\n        if (nodeVos == null || nodeVos.isEmpty()) {\n            return null;\n        }\n        ResourceTreeNode root = null;\n        Map<String, ResourceTreeNode> map = new HashMap<>();\n        for (NodeVo vo : nodeVos) {\n            ResourceTreeNode node = fromNodeVo(vo);\n            map.put(node.id, node);\n            // real root\n            if (node.parentId == null) {\n                root = node;\n            } else if (map.containsKey(node.parentId)) {\n                map.get(node.parentId).children.add(node);\n            } else {\n                // impossible\n            }\n        }\n        return root;\n    }\n\n    public static ResourceTreeNode fromNodeVo(NodeVo vo) {\n        ResourceTreeNode node = new ResourceTreeNode();\n        node.id = vo.getId();\n        node.parentId = vo.getParentId();\n        node.resource = vo.getResource();\n        node.threadNum = vo.getThreadNum();\n        node.passQps = vo.getPassQps();\n        node.blockQps = vo.getBlockQps();\n        node.totalQps = vo.getTotalQps();\n        node.averageRt = vo.getAverageRt();\n        node.successQps = vo.getSuccessQps();\n        node.exceptionQps = vo.getExceptionQps();\n        node.oneMinutePass = vo.getOneMinutePass();\n        node.oneMinuteBlock = vo.getOneMinuteBlock();\n        node.oneMinuteException = vo.getOneMinuteException();\n        node.oneMinuteTotal = vo.getOneMinuteTotal();\n        return node;\n    }\n\n    public void searchIgnoreCase(String searchKey) {\n        search(this, searchKey);\n    }\n\n    /**\n     * This node is visible only when searchKey matches this.resource or at least\n     * one of this's children is visible\n     */\n    private boolean search(ResourceTreeNode node, String searchKey) {\n        // empty matches all\n        if (searchKey == null || searchKey.isEmpty() ||\n            node.resource.toLowerCase().contains(searchKey.toLowerCase())) {\n            node.visible = true;\n        } else {\n            node.visible = false;\n        }\n\n        boolean found = false;\n        for (ResourceTreeNode c : node.children) {\n            found |= search(c, searchKey);\n        }\n        node.visible |= found;\n        return node.visible;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getParentId() {\n        return parentId;\n    }\n\n    public void setParentId(String parentId) {\n        this.parentId = parentId;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getThreadNum() {\n        return threadNum;\n    }\n\n    public void setThreadNum(Integer threadNum) {\n        this.threadNum = threadNum;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getTotalQps() {\n        return totalQps;\n    }\n\n    public void setTotalQps(Long totalQps) {\n        this.totalQps = totalQps;\n    }\n\n    public Long getAverageRt() {\n        return averageRt;\n    }\n\n    public void setAverageRt(Long averageRt) {\n        this.averageRt = averageRt;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Long getOneMinutePass() {\n        return oneMinutePass;\n    }\n\n    public void setOneMinutePass(Long oneMinutePass) {\n        this.oneMinutePass = oneMinutePass;\n    }\n\n    public Long getOneMinuteBlock() {\n        return oneMinuteBlock;\n    }\n\n    public void setOneMinuteBlock(Long oneMinuteBlock) {\n        this.oneMinuteBlock = oneMinuteBlock;\n    }\n\n    public Long getOneMinuteException() {\n        return oneMinuteException;\n    }\n\n    public void setOneMinuteException(Long oneMinuteException) {\n        this.oneMinuteException = oneMinuteException;\n    }\n\n    public Long getOneMinuteTotal() {\n        return oneMinuteTotal;\n    }\n\n    public void setOneMinuteTotal(Long oneMinuteTotal) {\n        this.oneMinuteTotal = oneMinuteTotal;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public void setVisible(boolean visible) {\n        this.visible = visible;\n    }\n\n    public List<ResourceTreeNode> getChildren() {\n        return children;\n    }\n\n    public void setChildren(List<ResourceTreeNode> children) {\n        this.children = children;\n    }\n}\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/Result.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain;\n\n/**\n * @author leyou\n * @author Eric Zhao\n */\npublic class Result<R> {\n\n    private boolean success;\n    private int code;\n    private String msg;\n    private R data;\n\n    public static <R> Result<R> ofSuccess(R data) {\n        return new Result<R>()\n            .setSuccess(true)\n            .setMsg(\"success\")\n            .setData(data);\n    }\n\n    public static <R> Result<R> ofSuccessMsg(String msg) {\n        return new Result<R>()\n            .setSuccess(true)\n            .setMsg(msg);\n    }\n\n    public static <R> Result<R> ofFail(int code, String msg) {\n        Result<R> result = new Result<>();\n        result.setSuccess(false);\n        result.setCode(code);\n        result.setMsg(msg);\n        return result;\n    }\n\n    public static <R> Result<R> ofThrowable(int code, Throwable throwable) {\n        Result<R> result = new Result<>();\n        result.setSuccess(false);\n        result.setCode(code);\n        result.setMsg(throwable.getClass().getName() + \", \" + throwable.getMessage());\n        return result;\n    }\n\n    public boolean isSuccess() {\n        return success;\n    }\n\n    public Result<R> setSuccess(boolean success) {\n        this.success = success;\n        return this;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public Result<R> setCode(int code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMsg() {\n        return msg;\n    }\n\n    public Result<R> setMsg(String msg) {\n        this.msg = msg;\n        return this;\n    }\n\n    public R getData() {\n        return data;\n    }\n\n    public Result<R> setData(R data) {\n        this.data = data;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"Result{\" +\n            \"success=\" + success +\n            \", code=\" + code +\n            \", msg='\" + msg + '\\'' +\n            \", data=\" + data +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppAssignResultVO {\n\n    private Set<String> failedServerSet;\n    private Set<String> failedClientSet;\n\n    private Integer totalCount;\n\n    public Set<String> getFailedServerSet() {\n        return failedServerSet;\n    }\n\n    public ClusterAppAssignResultVO setFailedServerSet(Set<String> failedServerSet) {\n        this.failedServerSet = failedServerSet;\n        return this;\n    }\n\n    public Set<String> getFailedClientSet() {\n        return failedClientSet;\n    }\n\n    public ClusterAppAssignResultVO setFailedClientSet(Set<String> failedClientSet) {\n        this.failedClientSet = failedClientSet;\n        return this;\n    }\n\n    public Integer getTotalCount() {\n        return totalCount;\n    }\n\n    public ClusterAppAssignResultVO setTotalCount(Integer totalCount) {\n        this.totalCount = totalCount;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppAssignResultVO{\" +\n            \"failedServerSet=\" + failedServerSet +\n            \", failedClientSet=\" + failedClientSet +\n            \", totalCount=\" + totalCount +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppFullAssignRequest {\n\n    private List<ClusterAppAssignMap> clusterMap;\n    private Set<String> remainingList;\n\n    public List<ClusterAppAssignMap> getClusterMap() {\n        return clusterMap;\n    }\n\n    public ClusterAppFullAssignRequest setClusterMap(\n        List<ClusterAppAssignMap> clusterMap) {\n        this.clusterMap = clusterMap;\n        return this;\n    }\n\n    public Set<String> getRemainingList() {\n        return remainingList;\n    }\n\n    public ClusterAppFullAssignRequest setRemainingList(Set<String> remainingList) {\n        this.remainingList = remainingList;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppFullAssignRequest{\" +\n            \"clusterMap=\" + clusterMap +\n            \", remainingList=\" + remainingList +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppSingleServerAssignRequest {\n\n    private ClusterAppAssignMap clusterMap;\n    private Set<String> remainingList;\n\n    public ClusterAppAssignMap getClusterMap() {\n        return clusterMap;\n    }\n\n    public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) {\n        this.clusterMap = clusterMap;\n        return this;\n    }\n\n    public Set<String> getRemainingList() {\n        return remainingList;\n    }\n\n    public ClusterAppSingleServerAssignRequest setRemainingList(Set<String> remainingList) {\n        this.remainingList = remainingList;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppSingleServerAssignRequest{\" +\n            \"clusterMap=\" + clusterMap +\n            \", remainingList=\" + remainingList +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterClientInfoVO {\n\n    private String serverHost;\n    private Integer serverPort;\n\n    private Integer clientState;\n\n    private Integer requestTimeout;\n\n    public String getServerHost() {\n        return serverHost;\n    }\n\n    public ClusterClientInfoVO setServerHost(String serverHost) {\n        this.serverHost = serverHost;\n        return this;\n    }\n\n    public Integer getServerPort() {\n        return serverPort;\n    }\n\n    public ClusterClientInfoVO setServerPort(Integer serverPort) {\n        this.serverPort = serverPort;\n        return this;\n    }\n\n    public Integer getClientState() {\n        return clientState;\n    }\n\n    public ClusterClientInfoVO setClientState(Integer clientState) {\n        this.clientState = clientState;\n        return this;\n    }\n\n    public Integer getRequestTimeout() {\n        return requestTimeout;\n    }\n\n    public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) {\n        this.requestTimeout = requestTimeout;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientInfoVO{\" +\n            \"serverHost='\" + serverHost + '\\'' +\n            \", serverPort=\" + serverPort +\n            \", clientState=\" + clientState +\n            \", requestTimeout=\" + requestTimeout +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterGroupEntity {\n\n    private String machineId;\n\n    private String ip;\n    private Integer port;\n\n    private Set<String> clientSet = new HashSet<>();\n\n    private Boolean belongToApp;\n\n    public String getMachineId() {\n        return machineId;\n    }\n\n    public ClusterGroupEntity setMachineId(String machineId) {\n        this.machineId = machineId;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterGroupEntity setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterGroupEntity setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Set<String> getClientSet() {\n        return clientSet;\n    }\n\n    public ClusterGroupEntity setClientSet(Set<String> clientSet) {\n        this.clientSet = clientSet;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public ClusterGroupEntity setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterGroupEntity{\" +\n            \"machineId='\" + machineId + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", clientSet=\" + clientSet +\n            \", belongToApp=\" + belongToApp +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterStateSingleVO {\n\n    private String address;\n    private Integer mode;\n    private String target;\n\n    public String getAddress() {\n        return address;\n    }\n\n    public ClusterStateSingleVO setAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterStateSingleVO setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public String getTarget() {\n        return target;\n    }\n\n    public ClusterStateSingleVO setTarget(String target) {\n        this.target = target;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterStateSingleVO{\" +\n            \"address='\" + address + '\\'' +\n            \", mode=\" + mode +\n            \", target='\" + target + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ConnectionDescriptorVO {\n\n    private String address;\n    private String host;\n\n    public String getAddress() {\n        return address;\n    }\n\n    public ConnectionDescriptorVO setAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public String getHost() {\n        return host;\n    }\n\n    public ConnectionDescriptorVO setHost(String host) {\n        this.host = host;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConnectionDescriptorVO{\" +\n            \"address='\" + address + '\\'' +\n            \", host='\" + host + '\\'' +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster;\n\nimport java.util.List;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ConnectionGroupVO {\n\n    private String namespace;\n    private List<ConnectionDescriptorVO> connectionSet;\n    private Integer connectedCount;\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public ConnectionGroupVO setNamespace(String namespace) {\n        this.namespace = namespace;\n        return this;\n    }\n\n    public List<ConnectionDescriptorVO> getConnectionSet() {\n        return connectionSet;\n    }\n\n    public ConnectionGroupVO setConnectionSet(\n        List<ConnectionDescriptorVO> connectionSet) {\n        this.connectionSet = connectionSet;\n        return this;\n    }\n\n    public Integer getConnectedCount() {\n        return connectedCount;\n    }\n\n    public ConnectionGroupVO setConnectedCount(Integer connectedCount) {\n        this.connectedCount = connectedCount;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConnectionGroupVO{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", connectionSet=\" + connectionSet +\n            \", connectedCount=\" + connectedCount +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientConfig {\n\n    private String serverHost;\n    private Integer serverPort;\n\n    private Integer requestTimeout;\n    private Integer connectTimeout;\n\n    public String getServerHost() {\n        return serverHost;\n    }\n\n    public ClusterClientConfig setServerHost(String serverHost) {\n        this.serverHost = serverHost;\n        return this;\n    }\n\n    public Integer getServerPort() {\n        return serverPort;\n    }\n\n    public ClusterClientConfig setServerPort(Integer serverPort) {\n        this.serverPort = serverPort;\n        return this;\n    }\n\n    public Integer getRequestTimeout() {\n        return requestTimeout;\n    }\n\n    public ClusterClientConfig setRequestTimeout(Integer requestTimeout) {\n        this.requestTimeout = requestTimeout;\n        return this;\n    }\n\n    public Integer getConnectTimeout() {\n        return connectTimeout;\n    }\n\n    public ClusterClientConfig setConnectTimeout(Integer connectTimeout) {\n        this.connectTimeout = connectTimeout;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientConfig{\" +\n            \"serverHost='\" + serverHost + '\\'' +\n            \", serverPort=\" + serverPort +\n            \", requestTimeout=\" + requestTimeout +\n            \", connectTimeout=\" + connectTimeout +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ServerFlowConfig {\n\n    public static final double DEFAULT_EXCEED_COUNT = 1.0d;\n    public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d;\n\n    public static final int DEFAULT_INTERVAL_MS = 1000;\n    public static final int DEFAULT_SAMPLE_COUNT= 10;\n    public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;\n\n    private final String namespace;\n\n    private Double exceedCount = DEFAULT_EXCEED_COUNT;\n    private Double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO;\n    private Integer intervalMs = DEFAULT_INTERVAL_MS;\n    private Integer sampleCount = DEFAULT_SAMPLE_COUNT;\n\n    private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;\n\n    public ServerFlowConfig() {\n        this(\"default\");\n    }\n\n    public ServerFlowConfig(String namespace) {\n        this.namespace = namespace;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public Double getExceedCount() {\n        return exceedCount;\n    }\n\n    public ServerFlowConfig setExceedCount(Double exceedCount) {\n        this.exceedCount = exceedCount;\n        return this;\n    }\n\n    public Double getMaxOccupyRatio() {\n        return maxOccupyRatio;\n    }\n\n    public ServerFlowConfig setMaxOccupyRatio(Double maxOccupyRatio) {\n        this.maxOccupyRatio = maxOccupyRatio;\n        return this;\n    }\n\n    public Integer getIntervalMs() {\n        return intervalMs;\n    }\n\n    public ServerFlowConfig setIntervalMs(Integer intervalMs) {\n        this.intervalMs = intervalMs;\n        return this;\n    }\n\n    public Integer getSampleCount() {\n        return sampleCount;\n    }\n\n    public ServerFlowConfig setSampleCount(Integer sampleCount) {\n        this.sampleCount = sampleCount;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ServerFlowConfig{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", exceedCount=\" + exceedCount +\n            \", maxOccupyRatio=\" + maxOccupyRatio +\n            \", intervalMs=\" + intervalMs +\n            \", sampleCount=\" + sampleCount +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.config;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ServerTransportConfig {\n\n    public static final int DEFAULT_PORT = 18730;\n    public static final int DEFAULT_IDLE_SECONDS = 600;\n\n    private Integer port;\n    private Integer idleSeconds;\n\n    public ServerTransportConfig() {\n        this(DEFAULT_PORT, DEFAULT_IDLE_SECONDS);\n    }\n\n    public ServerTransportConfig(Integer port, Integer idleSeconds) {\n        this.port = port;\n        this.idleSeconds = idleSeconds;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ServerTransportConfig setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Integer getIdleSeconds() {\n        return idleSeconds;\n    }\n\n    public ServerTransportConfig setIdleSeconds(Integer idleSeconds) {\n        this.idleSeconds = idleSeconds;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ServerTransportConfig{\" +\n            \"port=\" + port +\n            \", idleSeconds=\" + idleSeconds +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport java.util.Set;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterAppAssignMap {\n\n    private String machineId;\n    private String ip;\n    private Integer port;\n\n    private Boolean belongToApp;\n\n    private Set<String> clientSet;\n\n    private Set<String> namespaceSet;\n    private Double maxAllowedQps;\n\n    public String getMachineId() {\n        return machineId;\n    }\n\n    public ClusterAppAssignMap setMachineId(String machineId) {\n        this.machineId = machineId;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterAppAssignMap setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterAppAssignMap setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Set<String> getClientSet() {\n        return clientSet;\n    }\n\n    public ClusterAppAssignMap setClientSet(Set<String> clientSet) {\n        this.clientSet = clientSet;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterAppAssignMap setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterAppAssignMap{\" +\n            \"machineId='\" + machineId + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", belongToApp=\" + belongToApp +\n            \", clientSet=\" + clientSet +\n            \", namespaceSet=\" + namespaceSet +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientModifyRequest implements ClusterModifyRequest {\n\n    private String app;\n    private String ip;\n    private Integer port;\n\n    private Integer mode;\n    private ClusterClientConfig clientConfig;\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public ClusterClientModifyRequest setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterClientModifyRequest setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterClientModifyRequest setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    @Override\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterClientModifyRequest setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public ClusterClientConfig getClientConfig() {\n        return clientConfig;\n    }\n\n    public ClusterClientModifyRequest setClientConfig(\n        ClusterClientConfig clientConfig) {\n        this.clientConfig = clientConfig;\n        return this;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface ClusterModifyRequest {\n\n    String getApp();\n\n    String getIp();\n\n    Integer getPort();\n\n    Integer getMode();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.request;\n\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterServerModifyRequest implements ClusterModifyRequest {\n\n    private String app;\n    private String ip;\n    private Integer port;\n\n    private Integer mode;\n    private ServerFlowConfig flowConfig;\n    private ServerTransportConfig transportConfig;\n    private Set<String> namespaceSet;\n\n    @Override\n    public String getApp() {\n        return app;\n    }\n\n    public ClusterServerModifyRequest setApp(String app) {\n        this.app = app;\n        return this;\n    }\n\n    @Override\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterServerModifyRequest setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    @Override\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterServerModifyRequest setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    @Override\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterServerModifyRequest setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public ServerFlowConfig getFlowConfig() {\n        return flowConfig;\n    }\n\n    public ClusterServerModifyRequest setFlowConfig(\n        ServerFlowConfig flowConfig) {\n        this.flowConfig = flowConfig;\n        return this;\n    }\n\n    public ServerTransportConfig getTransportConfig() {\n        return transportConfig;\n    }\n\n    public ClusterServerModifyRequest setTransportConfig(\n        ServerTransportConfig transportConfig) {\n        this.transportConfig = transportConfig;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterServerModifyRequest setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterServerModifyRequest{\" +\n            \"app='\" + app + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port=\" + port +\n            \", mode=\" + mode +\n            \", flowConfig=\" + flowConfig +\n            \", transportConfig=\" + transportConfig +\n            \", namespaceSet=\" + namespaceSet +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class AppClusterClientStateWrapVO {\n\n    /**\n     * {ip}@{transport_command_port}.\n     */\n    private String id;\n\n    private Integer commandPort;\n    private String ip;\n\n    private ClusterClientStateVO state;\n\n    public String getId() {\n        return id;\n    }\n\n    public AppClusterClientStateWrapVO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public AppClusterClientStateWrapVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public ClusterClientStateVO getState() {\n        return state;\n    }\n\n    public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    public Integer getCommandPort() {\n        return commandPort;\n    }\n\n    public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) {\n        this.commandPort = commandPort;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AppClusterClientStateWrapVO{\" +\n            \"id='\" + id + '\\'' +\n            \", commandPort=\" + commandPort +\n            \", ip='\" + ip + '\\'' +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class AppClusterServerStateWrapVO {\n\n    /**\n     * {ip}@{transport_command_port}.\n     */\n    private String id;\n\n    private String ip;\n    private Integer port;\n\n    private Integer connectedCount;\n\n    private Boolean belongToApp;\n\n    private ClusterServerStateVO state;\n\n    public String getId() {\n        return id;\n    }\n\n    public AppClusterServerStateWrapVO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public AppClusterServerStateWrapVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public AppClusterServerStateWrapVO setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public Boolean getBelongToApp() {\n        return belongToApp;\n    }\n\n    public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) {\n        this.belongToApp = belongToApp;\n        return this;\n    }\n\n    public Integer getConnectedCount() {\n        return connectedCount;\n    }\n\n    public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) {\n        this.connectedCount = connectedCount;\n        return this;\n    }\n\n    public ClusterServerStateVO getState() {\n        return state;\n    }\n\n    public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AppClusterServerStateWrapVO{\" +\n            \"id='\" + id + '\\'' +\n            \", ip='\" + ip + '\\'' +\n            \", port='\" + port + '\\'' +\n            \", belongToApp=\" + belongToApp +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterClientStateVO {\n\n    /**\n     * Cluster token client state.\n     */\n    private ClusterClientInfoVO clientConfig;\n\n    public ClusterClientInfoVO getClientConfig() {\n        return clientConfig;\n    }\n\n    public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) {\n        this.clientConfig = clientConfig;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterClientStateVO{\" +\n            \"clientConfig=\" + clientConfig +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterRequestLimitVO {\n\n    private String namespace;\n    private Double currentQps;\n    private Double maxAllowedQps;\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public ClusterRequestLimitVO setNamespace(String namespace) {\n        this.namespace = namespace;\n        return this;\n    }\n\n    public Double getCurrentQps() {\n        return currentQps;\n    }\n\n    public ClusterRequestLimitVO setCurrentQps(Double currentQps) {\n        this.currentQps = currentQps;\n        return this;\n    }\n\n    public Double getMaxAllowedQps() {\n        return maxAllowedQps;\n    }\n\n    public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) {\n        this.maxAllowedQps = maxAllowedQps;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterRequestLimitVO{\" +\n            \"namespace='\" + namespace + '\\'' +\n            \", currentQps=\" + currentQps +\n            \", maxAllowedQps=\" + maxAllowedQps +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterServerStateVO {\n\n    private String appName;\n\n    private ServerTransportConfig transport;\n    private ServerFlowConfig flow;\n    private Set<String> namespaceSet;\n\n    private Integer port;\n\n    private List<ConnectionGroupVO> connection;\n    private List<ClusterRequestLimitVO> requestLimitData;\n\n    private Boolean embedded;\n\n    public String getAppName() {\n        return appName;\n    }\n\n    public ClusterServerStateVO setAppName(String appName) {\n        this.appName = appName;\n        return this;\n    }\n\n    public ServerTransportConfig getTransport() {\n        return transport;\n    }\n\n    public ClusterServerStateVO setTransport(ServerTransportConfig transport) {\n        this.transport = transport;\n        return this;\n    }\n\n    public ServerFlowConfig getFlow() {\n        return flow;\n    }\n\n    public ClusterServerStateVO setFlow(ServerFlowConfig flow) {\n        this.flow = flow;\n        return this;\n    }\n\n    public Set<String> getNamespaceSet() {\n        return namespaceSet;\n    }\n\n    public ClusterServerStateVO setNamespaceSet(Set<String> namespaceSet) {\n        this.namespaceSet = namespaceSet;\n        return this;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public ClusterServerStateVO setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n    public List<ConnectionGroupVO> getConnection() {\n        return connection;\n    }\n\n    public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) {\n        this.connection = connection;\n        return this;\n    }\n\n    public List<ClusterRequestLimitVO> getRequestLimitData() {\n        return requestLimitData;\n    }\n\n    public ClusterServerStateVO setRequestLimitData(List<ClusterRequestLimitVO> requestLimitData) {\n        this.requestLimitData = requestLimitData;\n        return this;\n    }\n\n    public Boolean getEmbedded() {\n        return embedded;\n    }\n\n    public ClusterServerStateVO setEmbedded(Boolean embedded) {\n        this.embedded = embedded;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterServerStateVO{\" +\n            \"appName='\" + appName + '\\'' +\n            \", transport=\" + transport +\n            \", flow=\" + flow +\n            \", namespaceSet=\" + namespaceSet +\n            \", port=\" + port +\n            \", connection=\" + connection +\n            \", requestLimitData=\" + requestLimitData +\n            \", embedded=\" + embedded +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterStateSimpleEntity {\n\n    private Integer mode;\n    private Long lastModified;\n    private Boolean clientAvailable;\n    private Boolean serverAvailable;\n\n    public Integer getMode() {\n        return mode;\n    }\n\n    public ClusterStateSimpleEntity setMode(Integer mode) {\n        this.mode = mode;\n        return this;\n    }\n\n    public Long getLastModified() {\n        return lastModified;\n    }\n\n    public ClusterStateSimpleEntity setLastModified(Long lastModified) {\n        this.lastModified = lastModified;\n        return this;\n    }\n\n    public Boolean getClientAvailable() {\n        return clientAvailable;\n    }\n\n    public ClusterStateSimpleEntity setClientAvailable(Boolean clientAvailable) {\n        this.clientAvailable = clientAvailable;\n        return this;\n    }\n\n    public Boolean getServerAvailable() {\n        return serverAvailable;\n    }\n\n    public ClusterStateSimpleEntity setServerAvailable(Boolean serverAvailable) {\n        this.serverAvailable = serverAvailable;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterStateSimpleEntity{\" +\n            \"mode=\" + mode +\n            \", lastModified=\" + lastModified +\n            \", clientAvailable=\" + clientAvailable +\n            \", serverAvailable=\" + serverAvailable +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic class ClusterUniversalStatePairVO {\n\n    private String ip;\n    private Integer commandPort;\n\n    private ClusterUniversalStateVO state;\n\n    public ClusterUniversalStatePairVO() {}\n\n    public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) {\n        this.ip = ip;\n        this.commandPort = commandPort;\n        this.state = state;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public ClusterUniversalStatePairVO setIp(String ip) {\n        this.ip = ip;\n        return this;\n    }\n\n    public Integer getCommandPort() {\n        return commandPort;\n    }\n\n    public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) {\n        this.commandPort = commandPort;\n        return this;\n    }\n\n    public ClusterUniversalStateVO getState() {\n        return state;\n    }\n\n    public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) {\n        this.state = state;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterUniversalStatePairVO{\" +\n            \"ip='\" + ip + '\\'' +\n            \", commandPort=\" + commandPort +\n            \", state=\" + state +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.cluster.state;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic class ClusterUniversalStateVO {\n\n    private ClusterStateSimpleEntity stateInfo;\n    private ClusterClientStateVO client;\n    private ClusterServerStateVO server;\n\n    public ClusterClientStateVO getClient() {\n        return client;\n    }\n\n    public ClusterUniversalStateVO setClient(ClusterClientStateVO client) {\n        this.client = client;\n        return this;\n    }\n\n    public ClusterServerStateVO getServer() {\n        return server;\n    }\n\n    public ClusterUniversalStateVO setServer(ClusterServerStateVO server) {\n        this.server = server;\n        return this;\n    }\n\n    public ClusterStateSimpleEntity getStateInfo() {\n        return stateInfo;\n    }\n\n    public ClusterUniversalStateVO setStateInfo(\n        ClusterStateSimpleEntity stateInfo) {\n        this.stateInfo = stateInfo;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterUniversalStateVO{\" +\n            \"stateInfo=\" + stateInfo +\n            \", client=\" + client +\n            \", server=\" + server +\n            '}';\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * @author leyou\n */\npublic class MachineInfoVo {\n\n    private String app;\n    private String hostname;\n    private String ip;\n    private int port;\n    private long heartbeatVersion;\n    private long lastHeartbeat;\n    private boolean healthy;\n\n    private String version;\n\n    public static List<MachineInfoVo> fromMachineInfoList(List<MachineInfo> machines) {\n        List<MachineInfoVo> list = new ArrayList<>();\n        for (MachineInfo machine : machines) {\n            list.add(fromMachineInfo(machine));\n        }\n        return list;\n    }\n\n    public static MachineInfoVo fromMachineInfo(MachineInfo machine) {\n        MachineInfoVo vo = new MachineInfoVo();\n        vo.setApp(machine.getApp());\n        vo.setHostname(machine.getHostname());\n        vo.setIp(machine.getIp());\n        vo.setPort(machine.getPort());\n        vo.setLastHeartbeat(machine.getLastHeartbeat());\n        vo.setHeartbeatVersion(machine.getHeartbeatVersion());\n        vo.setVersion(machine.getVersion());\n        vo.setHealthy(machine.isHealthy());\n        return vo;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public long getLastHeartbeat() {\n        return lastHeartbeat;\n    }\n    \n    public void setLastHeartbeat(long lastHeartbeat) {\n        this.lastHeartbeat = lastHeartbeat;\n    }\n    \n    public void setHeartbeatVersion(long heartbeatVersion) {\n        this.heartbeatVersion = heartbeatVersion;\n    }\n    \n    public long getHeartbeatVersion() {\n        return heartbeatVersion;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public MachineInfoVo setVersion(String version) {\n        this.version = version;\n        return this;\n    }\n\n    public boolean isHealthy() {\n        return healthy;\n    }\n\n    public void setHealthy(boolean healthy) {\n        this.healthy = healthy;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MetricVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\n\n/**\n * @author leyou\n */\npublic class MetricVo implements Comparable<MetricVo> {\n    private Long id;\n    private String app;\n    private Long timestamp;\n    private Long gmtCreate = System.currentTimeMillis();\n    private String resource;\n    private Long passQps;\n    private Long blockQps;\n    private Long successQps;\n    private Long exceptionQps;\n    /**\n     * average rt\n     */\n    private Double rt;\n    private Integer count;\n\n    public MetricVo() {\n    }\n\n    public static List<MetricVo> fromMetricEntities(Collection<MetricEntity> entities) {\n        List<MetricVo> list = new ArrayList<>();\n        if (entities != null) {\n            for (MetricEntity entity : entities) {\n                list.add(fromMetricEntity(entity));\n            }\n        }\n        return list;\n    }\n\n    /**\n     * 保留资源名为identity的结果。\n     *\n     * @param entities 通过hashCode查找到的MetricEntities\n     * @param identity 真正需要查找的资源名\n     * @return\n     */\n    public static List<MetricVo> fromMetricEntities(Collection<MetricEntity> entities, String identity) {\n        List<MetricVo> list = new ArrayList<>();\n        if (entities != null) {\n            for (MetricEntity entity : entities) {\n                if (entity.getResource().equals(identity)) {\n                    list.add(fromMetricEntity(entity));\n                }\n            }\n        }\n        return list;\n    }\n\n    public static MetricVo fromMetricEntity(MetricEntity entity) {\n        MetricVo vo = new MetricVo();\n        vo.id = entity.getId();\n        vo.app = entity.getApp();\n        vo.timestamp = entity.getTimestamp().getTime();\n        vo.gmtCreate = entity.getGmtCreate().getTime();\n        vo.resource = entity.getResource();\n        vo.passQps = entity.getPassQps();\n        vo.blockQps = entity.getBlockQps();\n        vo.successQps = entity.getSuccessQps();\n        vo.exceptionQps = entity.getExceptionQps();\n        if (entity.getSuccessQps() != 0) {\n            vo.rt = entity.getRt() / entity.getSuccessQps();\n        } else {\n            vo.rt = 0D;\n        }\n        vo.count = entity.getCount();\n        return vo;\n    }\n\n    public static MetricVo parse(String line) {\n        String[] strs = line.split(\"\\\\|\");\n        long timestamp = Long.parseLong(strs[0]);\n        String identity = strs[1];\n        long passQps = Long.parseLong(strs[2]);\n        long blockQps = Long.parseLong(strs[3]);\n        long exception = Long.parseLong(strs[4]);\n        double rt = Double.parseDouble(strs[5]);\n        long successQps = Long.parseLong(strs[6]);\n        MetricVo vo = new MetricVo();\n        vo.timestamp = timestamp;\n        vo.resource = identity;\n        vo.passQps = passQps;\n        vo.blockQps = blockQps;\n        vo.successQps = successQps;\n        vo.exceptionQps = exception;\n        vo.rt = rt;\n        vo.count = 1;\n        return vo;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getApp() {\n        return app;\n    }\n\n    public void setApp(String app) {\n        this.app = app;\n    }\n\n    public Long getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Long timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public Long getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Long gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getSuccessQps() {\n        return successQps;\n    }\n\n    public void setSuccessQps(Long successQps) {\n        this.successQps = successQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Double getRt() {\n        return rt;\n    }\n\n    public void setRt(Double rt) {\n        this.rt = rt;\n    }\n\n    public Integer getCount() {\n        return count;\n    }\n\n    public void setCount(Integer count) {\n        this.count = count;\n    }\n\n    @Override\n    public int compareTo(MetricVo o) {\n        return Long.compare(this.timestamp, o.timestamp);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/ResourceVo.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.domain.vo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.command.vo.NodeVo;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode;\n\n/**\n * @author leyou\n */\npublic class ResourceVo {\n    private String parentTtId;\n    private String ttId;\n    private String resource;\n\n    private Integer threadNum;\n    private Long passQps;\n    private Long blockQps;\n    private Long totalQps;\n    private Long averageRt;\n    private Long passRequestQps;\n    private Long exceptionQps;\n    private Long oneMinutePass;\n    private Long oneMinuteBlock;\n    private Long oneMinuteException;\n    private Long oneMinuteTotal;\n\n    private boolean visible = true;\n\n    public ResourceVo() {\n    }\n\n    public static List<ResourceVo> fromNodeVoList(List<NodeVo> nodeVos) {\n        if (nodeVos == null) {\n            return null;\n        }\n        List<ResourceVo> list = new ArrayList<>();\n        for (NodeVo nodeVo : nodeVos) {\n            ResourceVo vo = new ResourceVo();\n            vo.parentTtId = nodeVo.getParentId();\n            vo.ttId = nodeVo.getId();\n            vo.resource = nodeVo.getResource();\n            vo.threadNum = nodeVo.getThreadNum();\n            vo.passQps = nodeVo.getPassQps();\n            vo.blockQps = nodeVo.getBlockQps();\n            vo.totalQps = nodeVo.getTotalQps();\n            vo.averageRt = nodeVo.getAverageRt();\n            vo.exceptionQps = nodeVo.getExceptionQps();\n            vo.oneMinutePass = nodeVo.getOneMinutePass();\n            vo.oneMinuteBlock = nodeVo.getOneMinuteBlock();\n            vo.oneMinuteException = nodeVo.getOneMinuteException();\n            vo.oneMinuteTotal = nodeVo.getOneMinuteTotal();\n            list.add(vo);\n        }\n        return list;\n    }\n\n    public static List<ResourceVo> fromResourceTreeNode(ResourceTreeNode root) {\n        if (root == null) {\n            return null;\n        }\n        List<ResourceVo> list = new ArrayList<>();\n        visit(root, list, false, true);\n        //if(!list.isEmpty()){\n        //    list.remove(0);\n        //}\n        return list;\n    }\n\n    /**\n     * This node is visible when this.visible==true or one of this's parents is visible,\n     * root node is always invisible.\n     */\n    private static void visit(ResourceTreeNode node, List<ResourceVo> list, boolean parentVisible, boolean isRoot) {\n        boolean visible = !isRoot && (node.isVisible() || parentVisible);\n        //boolean visible = node.isVisible();\n        if (visible) {\n            ResourceVo vo = new ResourceVo();\n            vo.parentTtId = node.getParentId();\n            vo.ttId = node.getId();\n            vo.resource = node.getResource();\n            vo.threadNum = node.getThreadNum();\n            vo.passQps = node.getPassQps();\n            vo.blockQps = node.getBlockQps();\n            vo.totalQps = node.getTotalQps();\n            vo.averageRt = node.getAverageRt();\n            vo.exceptionQps = node.getExceptionQps();\n            vo.oneMinutePass = node.getOneMinutePass();\n            vo.oneMinuteBlock = node.getOneMinuteBlock();\n            vo.oneMinuteException = node.getOneMinuteException();\n            vo.oneMinuteTotal = node.getOneMinuteTotal();\n            vo.visible = node.isVisible();\n            list.add(vo);\n        }\n        for (ResourceTreeNode c : node.getChildren()) {\n            visit(c, list, visible, false);\n        }\n    }\n\n    public String getParentTtId() {\n        return parentTtId;\n    }\n\n    public void setParentTtId(String parentTtId) {\n        this.parentTtId = parentTtId;\n    }\n\n    public String getTtId() {\n        return ttId;\n    }\n\n    public void setTtId(String ttId) {\n        this.ttId = ttId;\n    }\n\n    public String getResource() {\n        return resource;\n    }\n\n    public void setResource(String resource) {\n        this.resource = resource;\n    }\n\n    public Integer getThreadNum() {\n        return threadNum;\n    }\n\n    public void setThreadNum(Integer threadNum) {\n        this.threadNum = threadNum;\n    }\n\n    public Long getPassQps() {\n        return passQps;\n    }\n\n    public void setPassQps(Long passQps) {\n        this.passQps = passQps;\n    }\n\n    public Long getBlockQps() {\n        return blockQps;\n    }\n\n    public void setBlockQps(Long blockQps) {\n        this.blockQps = blockQps;\n    }\n\n    public Long getTotalQps() {\n        return totalQps;\n    }\n\n    public void setTotalQps(Long totalQps) {\n        this.totalQps = totalQps;\n    }\n\n    public Long getAverageRt() {\n        return averageRt;\n    }\n\n    public void setAverageRt(Long averageRt) {\n        this.averageRt = averageRt;\n    }\n\n    public Long getPassRequestQps() {\n        return passRequestQps;\n    }\n\n    public void setPassRequestQps(Long passRequestQps) {\n        this.passRequestQps = passRequestQps;\n    }\n\n    public Long getExceptionQps() {\n        return exceptionQps;\n    }\n\n    public void setExceptionQps(Long exceptionQps) {\n        this.exceptionQps = exceptionQps;\n    }\n\n    public Long getOneMinuteException() {\n        return oneMinuteException;\n    }\n\n    public void setOneMinuteException(Long oneMinuteException) {\n        this.oneMinuteException = oneMinuteException;\n    }\n\n    public Long getOneMinutePass() {\n        return oneMinutePass;\n    }\n\n    public void setOneMinutePass(Long oneMinutePass) {\n        this.oneMinutePass = oneMinutePass;\n    }\n\n    public Long getOneMinuteBlock() {\n        return oneMinuteBlock;\n    }\n\n    public void setOneMinuteBlock(Long oneMinuteBlock) {\n        this.oneMinuteBlock = oneMinuteBlock;\n    }\n\n    public Long getOneMinuteTotal() {\n        return oneMinuteTotal;\n    }\n\n    public void setOneMinuteTotal(Long oneMinuteTotal) {\n        this.oneMinuteTotal = oneMinuteTotal;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public void setVisible(boolean visible) {\n        this.visible = visible;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.filter;\n\nimport com.alibaba.csp.sentinel.dashboard.auth.AuthService;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * Servlet Filter that authenticate requests.\n *\n * Note:\n * Some urls are excluded as they needn't auth, such as:\n *\n * Index url: /\n * Authentication request url: /login,logout\n * Used for client: /registry/machine\n * Static resources: htm,html,js and so on.\n *\n * The excluded urls and urlSuffixes are configured in application.properties\n *\n * @author cdfive\n * @since 1.6.0\n */\n@Component\npublic class AuthFilter implements Filter {\n\n    private static final String URL_SUFFIX_DOT = \".\";\n\n    /**Some urls which needn't auth, such as /auth/login,/registry/machine and so on*/\n    @Value(\"#{'${auth.filter.exclude-urls}'.split(',')}\")\n    private List<String> authFilterExcludeUrls;\n\n    /**Some urls with suffixes which needn't auth, such as htm,html,js and so on*/\n    @Value(\"#{'${auth.filter.exclude-url-suffixes}'.split(',')}\")\n    private List<String> authFilterExcludeUrlSuffixes;\n\n    /**Authentication using AuthService interface*/\n    @Autowired\n    private AuthService<HttpServletRequest> authService;\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n\n    }\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n        HttpServletRequest httpRequest = (HttpServletRequest) request;\n\n        String requestURI = httpRequest.getRequestURI();\n\n        // Exclude the urls which needn't auth\n        if (authFilterExcludeUrls.contains(requestURI)) {\n            chain.doFilter(request, response);\n            return;\n        }\n\n        // Exclude the urls with suffixes which needn't auth\n        for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {\n            if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {\n                continue;\n            }\n\n            // Add . for url suffix so that we needn't add . in property file\n            if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {\n                authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;\n            }\n\n            if (requestURI.endsWith(authFilterExcludeUrlSuffix)) {\n                chain.doFilter(request, response);\n                return;\n            }\n        }\n\n        AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);\n\n        HttpServletResponse httpResponse = (HttpServletResponse) response;\n        if (authUser == null) {\n            // If auth fail, set response status code to 401\n            httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n        } else {\n            chain.doFilter(request, response);\n        }\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.metric;\n\nimport java.net.ConnectException;\nimport java.net.SocketTimeoutException;\nimport java.nio.charset.Charset;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.node.metric.MetricNode;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.concurrent.FutureCallback;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.client.DefaultRedirectStrategy;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.apache.http.impl.nio.reactor.IOReactorConfig;\nimport org.apache.http.protocol.HTTP;\nimport org.apache.http.util.EntityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * Fetch metric of machines.\n *\n * @author leyou\n */\n@Component\npublic class MetricFetcher {\n\n    public static final String NO_METRICS = \"No metrics\";\n    private static final int HTTP_OK = 200;\n    private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;\n    private static final long FETCH_INTERVAL_SECOND = 6;\n    private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());\n    private final static String METRIC_URL_PATH = \"metric\";\n    private static Logger logger = LoggerFactory.getLogger(MetricFetcher.class);\n    private final long intervalSecond = 1;\n\n    private Map<String, AtomicLong> appLastFetchTime = new ConcurrentHashMap<>();\n\n    @Autowired\n    private MetricsRepository<MetricEntity> metricStore;\n    @Autowired\n    private AppManagement appManagement;\n\n    private CloseableHttpAsyncClient httpclient;\n\n    @SuppressWarnings(\"PMD.ThreadPoolCreationRule\")\n    private ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool(1,\n        new NamedThreadFactory(\"sentinel-dashboard-metrics-fetch-task\"));\n    private ExecutorService fetchService;\n    private ExecutorService fetchWorker;\n\n    public MetricFetcher() {\n        int cores = Runtime.getRuntime().availableProcessors() * 2;\n        long keepAliveTime = 0;\n        int queueSize = 2048;\n        RejectedExecutionHandler handler = new DiscardPolicy();\n        fetchService = new ThreadPoolExecutor(cores, cores,\n            keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),\n            new NamedThreadFactory(\"sentinel-dashboard-metrics-fetchService\"), handler);\n        fetchWorker = new ThreadPoolExecutor(cores, cores,\n            keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),\n            new NamedThreadFactory(\"sentinel-dashboard-metrics-fetchWorker\"), handler);\n        IOReactorConfig ioConfig = IOReactorConfig.custom()\n            .setConnectTimeout(3000)\n            .setSoTimeout(3000)\n            .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2)\n            .build();\n\n        httpclient = HttpAsyncClients.custom()\n            .setRedirectStrategy(new DefaultRedirectStrategy() {\n                @Override\n                protected boolean isRedirectable(final String method) {\n                    return false;\n                }\n            }).setMaxConnTotal(4000)\n            .setMaxConnPerRoute(1000)\n            .setDefaultIOReactorConfig(ioConfig)\n            .build();\n        httpclient.start();\n        start();\n    }\n\n    private void start() {\n        fetchScheduleService.scheduleAtFixedRate(() -> {\n            try {\n                fetchAllApp();\n            } catch (Exception e) {\n                logger.info(\"fetchAllApp error:\", e);\n            }\n        }, 10, intervalSecond, TimeUnit.SECONDS);\n    }\n\n    private void writeMetric(Map<String, MetricEntity> map) {\n        if (map.isEmpty()) {\n            return;\n        }\n        Date date = new Date();\n        for (MetricEntity entity : map.values()) {\n            entity.setGmtCreate(date);\n            entity.setGmtModified(date);\n        }\n        metricStore.saveAll(map.values());\n    }\n\n    /**\n     * Traverse each APP, and then pull the metric of all machines for that APP.\n     */\n    private void fetchAllApp() {\n        List<String> apps = appManagement.getAppNames();\n        if (apps == null) {\n            return;\n        }\n        for (final String app : apps) {\n            fetchService.submit(() -> {\n                try {\n                    doFetchAppMetric(app);\n                } catch (Exception e) {\n                    logger.error(\"fetchAppMetric error\", e);\n                }\n            });\n        }\n    }\n\n    /**\n     * fetch metric between [startTime, endTime], both side inclusive\n     */\n    private void fetchOnce(String app, long startTime, long endTime, int maxWaitSeconds) {\n        if (maxWaitSeconds <= 0) {\n            throw new IllegalArgumentException(\"maxWaitSeconds must > 0, but \" + maxWaitSeconds);\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        // auto remove for app\n        if (appInfo.isDead()) {\n            logger.info(\"Dead app removed: {}\", app);\n            appManagement.removeApp(app);\n            return;\n        }\n        Set<MachineInfo> machines = appInfo.getMachines();\n        logger.debug(\"enter fetchOnce(\" + app + \"), machines.size()=\" + machines.size()\n            + \", time intervalMs [\" + startTime + \", \" + endTime + \"]\");\n        if (machines.isEmpty()) {\n            return;\n        }\n        final String msg = \"fetch\";\n        AtomicLong unhealthy = new AtomicLong();\n        final AtomicLong success = new AtomicLong();\n        final AtomicLong fail = new AtomicLong();\n\n        long start = System.currentTimeMillis();\n        /** app_resource_timeSecond -> metric */\n        final Map<String, MetricEntity> metricMap = new ConcurrentHashMap<>(16);\n        final CountDownLatch latch = new CountDownLatch(machines.size());\n        for (final MachineInfo machine : machines) {\n            // auto remove\n            if (machine.isDead()) {\n                latch.countDown();\n                appManagement.getDetailApp(app).removeMachine(machine.getIp(), machine.getPort());\n                logger.info(\"Dead machine removed: {}:{} of {}\", machine.getIp(), machine.getPort(), app);\n                continue;\n            }\n            if (!machine.isHealthy()) {\n                latch.countDown();\n                unhealthy.incrementAndGet();\n                continue;\n            }\n            final String url = \"http://\" + machine.getIp() + \":\" + machine.getPort() + \"/\" + METRIC_URL_PATH\n                + \"?startTime=\" + startTime + \"&endTime=\" + endTime + \"&refetch=\" + false;\n            final HttpGet httpGet = new HttpGet(url);\n            httpGet.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);\n            httpclient.execute(httpGet, new FutureCallback<HttpResponse>() {\n                @Override\n                public void completed(final HttpResponse response) {\n                    try {\n                        handleResponse(response, machine, metricMap);\n                        success.incrementAndGet();\n                    } catch (Exception e) {\n                        logger.error(msg + \" metric \" + url + \" error:\", e);\n                    } finally {\n                        latch.countDown();\n                    }\n                }\n\n                @Override\n                public void failed(final Exception ex) {\n                    latch.countDown();\n                    fail.incrementAndGet();\n                    httpGet.abort();\n                    if (ex instanceof SocketTimeoutException) {\n                        logger.error(\"Failed to fetch metric from <{}>: socket timeout\", url);\n                    } else if (ex instanceof ConnectException) {\n                        logger.error(\"Failed to fetch metric from <{}> (ConnectionException: {})\", url, ex.getMessage());\n                    } else {\n                        logger.error(msg + \" metric \" + url + \" error\", ex);\n                    }\n                }\n\n                @Override\n                public void cancelled() {\n                    latch.countDown();\n                    fail.incrementAndGet();\n                    httpGet.abort();\n                }\n            });\n        }\n        try {\n            latch.await(maxWaitSeconds, TimeUnit.SECONDS);\n        } catch (Exception e) {\n            logger.info(msg + \" metric, wait http client error:\", e);\n        }\n        long cost = System.currentTimeMillis() - start;\n        //logger.info(\"finished \" + msg + \" metric for \" + app + \", time intervalMs [\" + startTime + \", \" + endTime\n        //    + \"], total machines=\" + machines.size() + \", dead=\" + dead + \", fetch success=\"\n        //    + success + \", fetch fail=\" + fail + \", time cost=\" + cost + \" ms\");\n        writeMetric(metricMap);\n    }\n\n    private void doFetchAppMetric(final String app) {\n        long now = System.currentTimeMillis();\n        long lastFetchMs = now - MAX_LAST_FETCH_INTERVAL_MS;\n        if (appLastFetchTime.containsKey(app)) {\n            lastFetchMs = Math.max(lastFetchMs, appLastFetchTime.get(app).get() + 1000);\n        }\n        // trim milliseconds\n        lastFetchMs = lastFetchMs / 1000 * 1000;\n        long endTime = lastFetchMs + FETCH_INTERVAL_SECOND * 1000;\n        if (endTime > now - 1000 * 2) {\n            // to near\n            return;\n        }\n        // update last_fetch in advance.\n        appLastFetchTime.computeIfAbsent(app, a -> new AtomicLong()).set(endTime);\n        final long finalLastFetchMs = lastFetchMs;\n        final long finalEndTime = endTime;\n        try {\n            // do real fetch async\n            fetchWorker.submit(() -> {\n                try {\n                    fetchOnce(app, finalLastFetchMs, finalEndTime, 5);\n                } catch (Exception e) {\n                    logger.info(\"fetchOnce(\" + app + \") error\", e);\n                }\n            });\n        } catch (Exception e) {\n            logger.info(\"submit fetchOnce(\" + app + \") fail, intervalMs [\" + lastFetchMs + \", \" + endTime + \"]\", e);\n        }\n    }\n\n    private void handleResponse(final HttpResponse response, MachineInfo machine,\n                                Map<String, MetricEntity> metricMap) throws Exception {\n        int code = response.getStatusLine().getStatusCode();\n        if (code != HTTP_OK) {\n            return;\n        }\n        Charset charset = null;\n        try {\n            String contentTypeStr = response.getFirstHeader(\"Content-type\").getValue();\n            if (StringUtil.isNotEmpty(contentTypeStr)) {\n                ContentType contentType = ContentType.parse(contentTypeStr);\n                charset = contentType.getCharset();\n            }\n        } catch (Exception ignore) {\n        }\n        String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);\n        if (StringUtil.isEmpty(body) || body.startsWith(NO_METRICS)) {\n            //logger.info(machine.getApp() + \":\" + machine.getIp() + \":\" + machine.getPort() + \", bodyStr is empty\");\n            return;\n        }\n        String[] lines = body.split(\"\\n\");\n        //logger.info(machine.getApp() + \":\" + machine.getIp() + \":\" + machine.getPort() +\n        //    \", bodyStr.length()=\" + body.length() + \", lines=\" + lines.length);\n        handleBody(lines, machine, metricMap);\n    }\n\n    private void handleBody(String[] lines, MachineInfo machine, Map<String, MetricEntity> map) {\n        //logger.info(\"handleBody() lines=\" + lines.length + \", machine=\" + machine);\n        if (lines.length < 1) {\n            return;\n        }\n\n        for (String line : lines) {\n            try {\n                MetricNode node = MetricNode.fromThinString(line);\n                /**\n                 * aggregation metrics by app_resource_timeSecond, ignore ip and port.\n                 */\n                String key = buildMetricKey(machine.getApp(), node.getResource(), node.getTimestamp());\n                MetricEntity entity = map.get(key);\n                if (entity != null) {\n                    entity.addPassQps(node.getPassQps());\n                    entity.addBlockQps(node.getBlockQps());\n                    entity.addRtAndSuccessQps(node.getRt(), node.getSuccessQps());\n                    entity.addExceptionQps(node.getExceptionQps());\n                    entity.addCount(1);\n                } else {\n                    entity = new MetricEntity();\n                    entity.setApp(machine.getApp());\n                    entity.setTimestamp(new Date(node.getTimestamp()));\n                    entity.setPassQps(node.getPassQps());\n                    entity.setBlockQps(node.getBlockQps());\n                    entity.setRtAndSuccessQps(node.getRt(), node.getSuccessQps());\n                    entity.setExceptionQps(node.getExceptionQps());\n                    entity.setCount(1);\n                    entity.setResource(node.getResource());\n                    map.put(key, entity);\n                }\n            } catch (Exception e) {\n                logger.warn(\"handleBody line exception, machine: {}, line: {}\", machine.toLogString(), line);\n            }\n        }\n    }\n\n    private String buildMetricKey(String app, String resource, long timestamp) {\n        return app + \"__\" + resource + \"__\" + (timestamp / 1000);\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;\nimport org.springframework.stereotype.Component;\n\n/**\n * Caches metrics data in a period of time in memory.\n *\n * @author Carpenter Lee\n * @author Eric Zhao\n */\n@Component\npublic class InMemoryMetricsRepository implements MetricsRepository<MetricEntity> {\n\n    private static final long MAX_METRIC_LIVE_TIME_MS = 1000 * 60 * 5;\n\n    /**\n     * {@code app -> resource -> timestamp -> metric}\n     */\n    private Map<String, Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>>> allMetrics = new ConcurrentHashMap<>();\n\n\n\n    @Override\n    public synchronized void save(MetricEntity entity) {\n        if (entity == null || StringUtil.isBlank(entity.getApp())) {\n            return;\n        }\n        allMetrics.computeIfAbsent(entity.getApp(), e -> new ConcurrentHashMap<>(16))\n            .computeIfAbsent(entity.getResource(), e -> new ConcurrentLinkedHashMap.Builder<Long, MetricEntity>()\n                .maximumWeightedCapacity(MAX_METRIC_LIVE_TIME_MS).weigher((key, value) -> {\n                    // Metric older than {@link #MAX_METRIC_LIVE_TIME_MS} will be removed.\n                    int weight = (int)(System.currentTimeMillis() - key);\n                    // weight must be a number greater than or equal to one\n                    return Math.max(weight, 1);\n                }).build()).put(entity.getTimestamp().getTime(), entity);\n    }\n\n    @Override\n    public synchronized void saveAll(Iterable<MetricEntity> metrics) {\n        if (metrics == null) {\n            return;\n        }\n        metrics.forEach(this::save);\n    }\n\n    @Override\n    public synchronized List<MetricEntity> queryByAppAndResourceBetween(String app, String resource,\n                                                                        long startTime, long endTime) {\n        List<MetricEntity> results = new ArrayList<>();\n        if (StringUtil.isBlank(app)) {\n            return results;\n        }\n        Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMap = allMetrics.get(app);\n        if (resourceMap == null) {\n            return results;\n        }\n        ConcurrentLinkedHashMap<Long, MetricEntity> metricsMap = resourceMap.get(resource);\n        if (metricsMap == null) {\n            return results;\n        }\n        for (Entry<Long, MetricEntity> entry : metricsMap.entrySet()) {\n            if (entry.getKey() >= startTime && entry.getKey() <= endTime) {\n                results.add(entry.getValue());\n            }\n        }\n        return results;\n    }\n\n    @Override\n    public List<String> listResourcesOfApp(String app) {\n        List<String> results = new ArrayList<>();\n        if (StringUtil.isBlank(app)) {\n            return results;\n        }\n        // resource -> timestamp -> metric\n        Map<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMap = allMetrics.get(app);\n        if (resourceMap == null) {\n            return results;\n        }\n        final long minTimeMs = System.currentTimeMillis() - 1000 * 60;\n        Map<String, MetricEntity> resourceCount = new ConcurrentHashMap<>(32);\n\n        for (Entry<String, ConcurrentLinkedHashMap<Long, MetricEntity>> resourceMetrics : resourceMap.entrySet()) {\n            for (Entry<Long, MetricEntity> metrics : resourceMetrics.getValue().entrySet()) {\n                if (metrics.getKey() < minTimeMs) {\n                    continue;\n                }\n                MetricEntity newEntity = metrics.getValue();\n                if (resourceCount.containsKey(resourceMetrics.getKey())) {\n                    MetricEntity oldEntity = resourceCount.get(resourceMetrics.getKey());\n                    oldEntity.addPassQps(newEntity.getPassQps());\n                    oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps());\n                    oldEntity.addBlockQps(newEntity.getBlockQps());\n                    oldEntity.addExceptionQps(newEntity.getExceptionQps());\n                    oldEntity.addCount(1);\n                } else {\n                    resourceCount.put(resourceMetrics.getKey(), MetricEntity.copyOf(newEntity));\n                }\n            }\n        }\n        // Order by last minute b_qps DESC.\n        return resourceCount.entrySet()\n            .stream()\n            .sorted((o1, o2) -> {\n                MetricEntity e1 = o1.getValue();\n                MetricEntity e2 = o2.getValue();\n                int t = e2.getBlockQps().compareTo(e1.getBlockQps());\n                if (t != 0) {\n                    return t;\n                }\n                return e2.getPassQps().compareTo(e1.getPassQps());\n            })\n            .map(Entry::getKey)\n            .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/MetricsRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport java.util.List;\n\n/**\n * Repository interface for aggregated metrics data.\n *\n * @param <T> type of metrics\n * @author Eric Zhao\n */\npublic interface MetricsRepository<T> {\n\n    /**\n     * Save the metric to the storage repository.\n     *\n     * @param metric metric data to save\n     */\n    void save(T metric);\n\n    /**\n     * Save all metrics to the storage repository.\n     *\n     * @param metrics metrics to save\n     */\n    void saveAll(Iterable<T> metrics);\n\n    /**\n     * Get all metrics by {@code appName} and {@code resourceName} between a period of time.\n     *\n     * @param app       application name for Sentinel\n     * @param resource  resource name\n     * @param startTime start timestamp\n     * @param endTime   end timestamp\n     * @return all metrics in query conditions\n     */\n    List<T> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime);\n\n    /**\n     * List resource name of provided application name.\n     *\n     * @param app application name\n     * @return list of resources\n     */\n    List<String> listResourcesOfApp(String app);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemAuthorityRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * In-memory storage for authority rules.\n *\n * @author Eric Zhao\n * @since 0.2.1\n */\n@Component\npublic class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter<AuthorityRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemDegradeRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class InMemDegradeRuleStore extends InMemoryRuleRepositoryAdapter<DegradeRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * Store {@link FlowRuleEntity} in memory.\n *\n * @author leyou\n */\n@Component\npublic class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter<FlowRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n\n    @Override\n    protected FlowRuleEntity preProcess(FlowRuleEntity entity) {\n        if (entity != null && entity.isClusterMode()) {\n            ClusterFlowConfig config = entity.getClusterConfig();\n            if (config == null) {\n                config = new ClusterFlowConfig();\n                entity.setClusterConfig(config);\n            }\n            // Set cluster rule id.\n            config.setFlowId(entity.getId());\n        }\n        return entity;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemParamFlowRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;\nimport com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 0.2.1\n */\n@Component\npublic class InMemParamFlowRuleStore extends InMemoryRuleRepositoryAdapter<ParamFlowRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n\n    @Override\n    protected ParamFlowRuleEntity preProcess(ParamFlowRuleEntity entity) {\n        if (entity != null && entity.isClusterMode()) {\n            ParamFlowClusterConfig config = entity.getClusterConfig();\n            if (config == null) {\n                config = new ParamFlowClusterConfig();\n            }\n            // Set cluster rule id.\n            config.setFlowId(entity.getId());\n        }\n        return entity;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemSystemRuleStore.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author leyou\n */\n@Component\npublic class InMemSystemRuleStore extends InMemoryRuleRepositoryAdapter<SystemRuleEntity> {\n\n    private static AtomicLong ids = new AtomicLong(0);\n\n    @Override\n    protected long nextId() {\n        return ids.incrementAndGet();\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemoryRuleRepositoryAdapter.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\n/**\n * @author leyou\n */\npublic abstract class InMemoryRuleRepositoryAdapter<T extends RuleEntity> implements RuleRepository<T, Long> {\n\n    /**\n     * {@code <machine, <id, rule>>}\n     */\n    private Map<MachineInfo, Map<Long, T>> machineRules = new ConcurrentHashMap<>(16);\n    private Map<Long, T> allRules = new ConcurrentHashMap<>(16);\n\n    private Map<String, Map<Long, T>> appRules = new ConcurrentHashMap<>(16);\n\n    private static final int MAX_RULES_SIZE = 10000;\n\n    @Override\n    public T save(T entity) {\n        if (entity.getId() == null) {\n            entity.setId(nextId());\n        }\n        T processedEntity = preProcess(entity);\n        if (processedEntity != null) {\n            allRules.put(processedEntity.getId(), processedEntity);\n            machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(),\n                processedEntity.getPort()), e -> new ConcurrentHashMap<>(32))\n                .put(processedEntity.getId(), processedEntity);\n            appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32))\n                .put(processedEntity.getId(), processedEntity);\n        }\n\n        return processedEntity;\n    }\n\n    @Override\n    public List<T> saveAll(List<T> rules) {\n        // TODO: check here.\n        allRules.clear();\n        machineRules.clear();\n        appRules.clear();\n\n        if (rules == null) {\n            return null;\n        }\n        List<T> savedRules = new ArrayList<>(rules.size());\n        for (T rule : rules) {\n            savedRules.add(save(rule));\n        }\n        return savedRules;\n    }\n\n    @Override\n    public T delete(Long id) {\n        T entity = allRules.remove(id);\n        if (entity != null) {\n            if (appRules.get(entity.getApp()) != null) {\n                appRules.get(entity.getApp()).remove(id);\n            }\n            machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id);\n        }\n        return entity;\n    }\n\n    @Override\n    public T findById(Long id) {\n        return allRules.get(id);\n    }\n\n    @Override\n    public List<T> findAllByMachine(MachineInfo machineInfo) {\n        Map<Long, T> entities = machineRules.get(machineInfo);\n        if (entities == null) {\n            return new ArrayList<>();\n        }\n        return new ArrayList<>(entities.values());\n    }\n\n    @Override\n    public List<T> findAllByApp(String appName) {\n        AssertUtil.notEmpty(appName, \"appName cannot be empty\");\n        Map<Long, T> entities = appRules.get(appName);\n        if (entities == null) {\n            return new ArrayList<>();\n        }\n        return new ArrayList<>(entities.values());\n    }\n\n    protected T preProcess(T entity) {\n        return entity;\n    }\n\n    /**\n     * Get next unused id.\n     *\n     * @return next unused id\n     */\n    abstract protected long nextId();\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/RuleRepository.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.rule;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\n\n/**\n * Interface to store and find rules.\n *\n * @author leyou\n */\npublic interface RuleRepository<T, ID> {\n\n    /**\n     * Save one.\n     *\n     * @param entity\n     * @return\n     */\n    T save(T entity);\n\n    /**\n     * Save all.\n     *\n     * @param rules\n     * @return rules saved.\n     */\n    List<T> saveAll(List<T> rules);\n\n    /**\n     * Delete by id\n     *\n     * @param id\n     * @return entity deleted\n     */\n    T delete(ID id);\n\n    /**\n     * Find by id.\n     *\n     * @param id\n     * @return\n     */\n    T findById(ID id);\n\n    /**\n     * Find all by machine.\n     *\n     * @param machineInfo\n     * @return\n     */\n    List<T> findAllByMachine(MachineInfo machineInfo);\n\n    /**\n     * Find all by application.\n     *\n     * @param appName valid app name\n     * @return all rules of the application\n     * @since 1.4.0\n     */\n    List<T> findAllByApp(String appName);\n\n    ///**\n    // * Find all by app and enable switch.\n    // * @param app\n    // * @param enable\n    // * @return\n    // */\n    //List<T> findAllByAppAndEnable(String app, boolean enable);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRuleProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface DynamicRuleProvider<T> {\n\n    T getRules(String appName) throws Exception;\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRulePublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic interface DynamicRulePublisher<T> {\n\n    /**\n     * Publish rules to remote rule configuration center for given application name.\n     *\n     * @param app app name\n     * @param rules list of rules to push\n     * @throws Exception if some error occurs\n     */\n    void publish(String app, T rules) throws Exception;\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n */\n@Component(\"flowRuleDefaultProvider\")\npublic class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        if (StringUtil.isBlank(appName)) {\n            return new ArrayList<>();\n        }\n        List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()\n            .stream()\n            .filter(MachineInfo::isHealthy)\n            .sorted((e1, e2) -> Long.compare(e2.getLastHeartbeat(), e1.getLastHeartbeat())).collect(Collectors.toList());\n        if (list.isEmpty()) {\n            return new ArrayList<>();\n        } else {\n            MachineInfo machine = list.get(0);\n            return sentinelApiClient.fetchFlowRuleOfMachine(machine.getApp(), machine.getIp(), machine.getPort());\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleDefaultPublisher\")\npublic class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        if (StringUtil.isBlank(app)) {\n            return;\n        }\n        if (rules == null) {\n            return;\n        }\n        Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();\n\n        for (MachineInfo machine : set) {\n            if (!machine.isHealthy()) {\n                continue;\n            }\n            // TODO: parse the results\n            sentinelApiClient.setFlowRuleOfMachine(app, machine.getIp(), machine.getPort(), rules);\n        }\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosProvider\")\npublic class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    public static final String FLOW_DATA_ID_POSTFIX = \"-sentinel\";\n    public static final String GROUP_ID = \"DEFAULT_GROUP\";\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String rules = configService.getConfig(appName + FLOW_DATA_ID_POSTFIX, GROUP_ID, 3000);\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosPublisher\")\npublic class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    public static final String FLOW_DATA_ID_POSTFIX = \"-sentinel\";\n    public static final String GROUP_ID = \"DEFAULT_GROUP\";\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n        configService.publishConfig(app + FLOW_DATA_ID_POSTFIX, GROUP_ID, converter.convert(rules));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.nacos.api.PropertyKeyConst;\nimport com.alibaba.nacos.api.config.ConfigFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Configuration\npublic class NacosConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ConfigService nacosConfigService() throws Exception {\n        Properties properties = new Properties();\n        properties.put(PropertyKeyConst.SERVER_ADDR, \"localhost\");\n//        properties.put(PropertyKeyConst.NAMESPACE, \"130e71fa-97fe-467d-ad77-967456f2c16d\");\n        return ConfigFactory.createConfigService(properties);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic interface ClusterAssignService {\n\n    /**\n     * Unbind a specific cluster server and its clients.\n     *\n     * @param app app name\n     * @param machineId valid machine ID ({@code host@commandPort})\n     * @return assign result\n     */\n    ClusterAppAssignResultVO unbindClusterServer(String app, String machineId);\n\n    /**\n     * Unbind a set of cluster servers and its clients.\n     *\n     * @param app app name\n     * @param machineIdSet set of valid machine ID ({@code host@commandPort})\n     * @return assign result\n     */\n    ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet);\n\n    /**\n     * Apply cluster server and client assignment for provided app.\n     *\n     * @param app app name\n     * @param clusterMap cluster assign map (server -> clients)\n     * @param remainingSet unassigned set of machine ID\n     * @return assign result\n     */\n    ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,\n                                              Set<String> remainingSet);\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.csp.sentinel.util.function.Tuple2;\n\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;\nimport com.alibaba.csp.sentinel.dashboard.util.MachineUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\n@Service\npublic class ClusterAssignServiceImpl implements ClusterAssignService {\n\n    private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class);\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private ClusterConfigService clusterConfigService;\n\n    private boolean isMachineInApp(/*@NonEmpty*/ String machineId) {\n        return machineId.contains(\":\");\n    }\n\n    private ClusterAppAssignResultVO handleUnbindClusterServerNotInApp(String app, String machineId) {\n        Set<String> failedSet = new HashSet<>();\n        try {\n            List<ClusterUniversalStatePairVO> list = clusterConfigService.getClusterUniversalState(app)\n                .get(10, TimeUnit.SECONDS);\n            Set<String> toModifySet = list.stream()\n                .filter(e -> e.getState().getStateInfo().getMode() == ClusterStateManager.CLUSTER_CLIENT)\n                .filter(e -> machineId.equals(e.getState().getClient().getClientConfig().getServerHost() + ':' +\n                    e.getState().getClient().getClientConfig().getServerPort()))\n                .map(e -> e.getIp() + '@' + e.getCommandPort())\n                .collect(Collectors.toSet());\n            // Modify mode to NOT-STARTED for all associated token clients.\n            modifyToNonStarted(toModifySet, failedSet);\n        } catch (Exception ex) {\n            Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;\n            LOGGER.error(\"Failed to unbind machine <{}>\", machineId, e);\n            failedSet.add(machineId);\n        }\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedSet)\n            .setFailedServerSet(new HashSet<>());\n    }\n\n    private void modifyToNonStarted(Set<String> toModifySet, Set<String> failedSet) {\n        toModifySet.parallelStream()\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(e -> {\n                CompletableFuture<Void> f = modifyMode(e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED);\n                return Tuple2.of(e.r1 + '@' + e.r2, f);\n            })\n            .forEach(f -> handleFutureSync(f, failedSet));\n    }\n\n    @Override\n    public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.assertNotBlank(machineId, \"machineId cannot be blank\");\n\n        if (isMachineInApp(machineId)) {\n            return handleUnbindClusterServerNotInApp(app, machineId);\n        }\n        Set<String> failedSet = new HashSet<>();\n        try {\n            ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId)\n                .get(10, TimeUnit.SECONDS);\n            Set<String> toModifySet = new HashSet<>();\n            toModifySet.add(machineId);\n            if (entity.getClientSet() != null) {\n                toModifySet.addAll(entity.getClientSet());\n            }\n            // Modify mode to NOT-STARTED for all chosen token servers and associated token clients.\n            modifyToNonStarted(toModifySet, failedSet);\n        } catch (Exception ex) {\n            Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;\n            LOGGER.error(\"Failed to unbind machine <{}>\", machineId, e);\n            failedSet.add(machineId);\n        }\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedSet)\n            .setFailedServerSet(new HashSet<>());\n    }\n\n    @Override\n    public ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), \"machineIdSet cannot be empty\");\n        ClusterAppAssignResultVO result = new ClusterAppAssignResultVO()\n            .setFailedClientSet(new HashSet<>())\n            .setFailedServerSet(new HashSet<>());\n        for (String machineId : machineIdSet) {\n            ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId);\n            result.getFailedClientSet().addAll(resultVO.getFailedClientSet());\n            result.getFailedServerSet().addAll(resultVO.getFailedServerSet());\n        }\n        return result;\n    }\n\n    @Override\n    public ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,\n                                                     Set<String> remainingSet) {\n        AssertUtil.assertNotBlank(app, \"app cannot be blank\");\n        AssertUtil.notNull(clusterMap, \"clusterMap cannot be null\");\n        Set<String> failedServerSet = new HashSet<>();\n        Set<String> failedClientSet = new HashSet<>();\n\n        // Assign server and apply config.\n        clusterMap.stream()\n            .filter(Objects::nonNull)\n            .filter(ClusterAppAssignMap::getBelongToApp)\n            .map(e -> {\n                String ip = e.getIp();\n                int commandPort = parsePort(e);\n                CompletableFuture<Void> f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_SERVER)\n                    .thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e));\n                return Tuple2.of(e.getMachineId(), f);\n            })\n            .forEach(t -> handleFutureSync(t, failedServerSet));\n\n        // Assign client of servers and apply config.\n        clusterMap.parallelStream()\n            .filter(Objects::nonNull)\n            .forEach(e -> applyAllClientConfigChange(app, e, failedClientSet));\n\n        // Unbind remaining (unassigned) machines.\n        applyAllRemainingMachineSet(app, remainingSet, failedClientSet);\n\n        return new ClusterAppAssignResultVO()\n            .setFailedClientSet(failedClientSet)\n            .setFailedServerSet(failedServerSet);\n    }\n\n    private void applyAllRemainingMachineSet(String app, Set<String> remainingSet, Set<String> failedSet) {\n        if (remainingSet == null || remainingSet.isEmpty()) {\n            return;\n        }\n        remainingSet.parallelStream()\n            .filter(Objects::nonNull)\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(ipPort -> {\n                String ip = ipPort.r1;\n                int commandPort = ipPort.r2;\n                CompletableFuture<Void> f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED);\n                return Tuple2.of(ip + '@' + commandPort, f);\n            })\n            .forEach(t -> handleFutureSync(t, failedSet));\n    }\n\n    private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap,\n                                            Set<String> failedSet) {\n        Set<String> clientSet = assignMap.getClientSet();\n        if (clientSet == null || clientSet.isEmpty()) {\n            return;\n        }\n        final String serverIp = assignMap.getIp();\n        final int serverPort = assignMap.getPort();\n        clientSet.stream()\n            .map(MachineUtils::parseCommandIpAndPort)\n            .filter(Optional::isPresent)\n            .map(Optional::get)\n            .map(ipPort -> {\n                CompletableFuture<Void> f = sentinelApiClient\n                    .modifyClusterMode(ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT)\n                    .thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2,\n                        new ClusterClientConfig().setRequestTimeout(20)\n                            .setServerHost(serverIp)\n                            .setServerPort(serverPort)\n                    ));\n                return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f);\n            })\n            .forEach(t -> handleFutureSync(t, failedSet));\n    }\n\n    private void handleFutureSync(Tuple2<String, CompletableFuture<Void>> t, Set<String> failedSet) {\n        try {\n            t.r2.get(10, TimeUnit.SECONDS);\n        } catch (Exception ex) {\n            if (ex instanceof ExecutionException) {\n                LOGGER.error(\"Request for <{}> failed\", t.r1, ex.getCause());\n            } else {\n                LOGGER.error(\"Request for <{}> failed\", t.r1, ex);\n            }\n            failedSet.add(t.r1);\n        }\n    }\n\n    private CompletableFuture<Void> applyServerConfigChange(String app, String ip, int commandPort,\n                                                            ClusterAppAssignMap assignMap) {\n        ServerTransportConfig transportConfig = new ServerTransportConfig()\n            .setPort(assignMap.getPort())\n            .setIdleSeconds(600);\n        return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig)\n            .thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap))\n            .thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap));\n    }\n\n    private CompletableFuture<Void> applyServerFlowConfigChange(String app, String ip, int commandPort,\n                                                                ClusterAppAssignMap assignMap) {\n        Double maxAllowedQps = assignMap.getMaxAllowedQps();\n        if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) {\n            return CompletableFuture.completedFuture(null);\n        }\n        return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort,\n            new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps));\n    }\n\n    private CompletableFuture<Void> applyServerNamespaceSetConfig(String app, String ip, int commandPort,\n                                                                  ClusterAppAssignMap assignMap) {\n        Set<String> namespaceSet = assignMap.getNamespaceSet();\n        if (namespaceSet == null || namespaceSet.isEmpty()) {\n            return CompletableFuture.completedFuture(null);\n        }\n        return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet);\n    }\n\n    private CompletableFuture<Void> modifyMode(String ip, int port, int mode) {\n        return sentinelApiClient.modifyClusterMode(ip, port, mode);\n    }\n\n    private int parsePort(ClusterAppAssignMap assignMap) {\n        return MachineUtils.parseCommandPort(assignMap.getMachineId())\n            .orElse(ServerTransportConfig.DEFAULT_PORT);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.stream.Collectors;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;\nimport com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.util.AsyncUtils;\nimport com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Service\npublic class ClusterConfigService {\n\n    @Autowired\n    private SentinelApiClient sentinelApiClient;\n    @Autowired\n    private AppManagement appManagement;\n\n    public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {\n        if (notClientRequestValid(request)) {\n            throw new IllegalArgumentException(\"Invalid request\");\n        }\n        String app = request.getApp();\n        String ip = request.getIp();\n        int port = request.getPort();\n        return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())\n            .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT));\n    }\n\n    private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {\n        ClusterClientConfig config = request.getClientConfig();\n        return config == null || StringUtil.isEmpty(config.getServerHost())\n            || config.getServerPort() == null || config.getServerPort() <= 0\n            || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0;\n    }\n\n    public CompletableFuture<Void> modifyClusterServerConfig(ClusterServerModifyRequest request) {\n        ServerTransportConfig transportConfig = request.getTransportConfig();\n        ServerFlowConfig flowConfig = request.getFlowConfig();\n        Set<String> namespaceSet = request.getNamespaceSet();\n        if (invalidTransportConfig(transportConfig)) {\n            throw new IllegalArgumentException(\"Invalid transport config in request\");\n        }\n        if (invalidFlowConfig(flowConfig)) {\n            throw new IllegalArgumentException(\"Invalid flow config in request\");\n        }\n        if (namespaceSet == null) {\n            throw new IllegalArgumentException(\"namespace set cannot be null\");\n        }\n        String app = request.getApp();\n        String ip = request.getIp();\n        int port = request.getPort();\n        return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet)\n            .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig))\n            .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig))\n            .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_SERVER));\n    }\n\n    /**\n     * Get cluster state list of all available machines of provided application.\n     *\n     * @param app application name\n     * @return cluster state list of all available machines of the application\n     * @since 1.4.1\n     */\n    public CompletableFuture<List<ClusterUniversalStatePairVO>> getClusterUniversalState(String app) {\n        if (StringUtil.isBlank(app)) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app cannot be empty\"));\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null || appInfo.getMachines() == null) {\n            return CompletableFuture.completedFuture(new ArrayList<>());\n        }\n\n        List<CompletableFuture<ClusterUniversalStatePairVO>> futures = appInfo.getMachines().stream()\n            .filter(e -> e.isHealthy())\n            .map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())\n                .thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))\n            .collect(Collectors.toList());\n\n        return AsyncUtils.sequenceSuccessFuture(futures);\n    }\n\n    public CompletableFuture<ClusterGroupEntity> getClusterUniversalStateForAppMachine(String app, String machineId) {\n        if (StringUtil.isBlank(app)) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app cannot be empty\"));\n        }\n        AppInfo appInfo = appManagement.getDetailApp(app);\n        if (appInfo == null || appInfo.getMachines() == null) {\n            return AsyncUtils.newFailedFuture(new IllegalArgumentException(\"app does not have machines\"));\n        }\n\n        boolean machineOk = appInfo.getMachines().stream()\n            .filter(e -> e.isHealthy())\n            .map(e -> e.getIp() + '@' + e.getPort())\n            .anyMatch(e -> e.equals(machineId));\n        if (!machineOk) {\n            return AsyncUtils.newFailedFuture(new IllegalStateException(\"machine does not exist or disconnected\"));\n        }\n\n        return getClusterUniversalState(app)\n            .thenApply(ClusterEntityUtils::wrapToClusterGroup)\n            .thenCompose(e -> e.stream()\n                .filter(e1 -> e1.getMachineId().equals(machineId))\n                .findAny()\n                .map(CompletableFuture::completedFuture)\n                .orElse(AsyncUtils.newFailedFuture(new IllegalStateException(\"not a server: \" + machineId)))\n            );\n    }\n\n    public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {\n        return sentinelApiClient.fetchClusterMode(ip, port)\n            .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))\n            .thenCompose(vo -> {\n                if (vo.getStateInfo().getClientAvailable()) {\n                    return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port)\n                        .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));\n                } else {\n                    return CompletableFuture.completedFuture(vo);\n                }\n            }).thenCompose(vo -> {\n                if (vo.getStateInfo().getServerAvailable()) {\n                    return sentinelApiClient.fetchClusterServerBasicInfo(ip, port)\n                        .thenApply(vo::setServer);\n                } else {\n                    return CompletableFuture.completedFuture(vo);\n                }\n            });\n    }\n\n    private boolean invalidTransportConfig(ServerTransportConfig transportConfig) {\n        return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0\n            || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0;\n    }\n\n    private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {\n        return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0\n            || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0\n            || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0\n            || flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/AsyncUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic final class AsyncUtils {\n\n    private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class);\n\n    public static <R> CompletableFuture<R> newFailedFuture(Throwable ex) {\n        CompletableFuture<R> future = new CompletableFuture<>();\n        future.completeExceptionally(ex);\n        return future;\n    }\n\n    public static <R> CompletableFuture<List<R>> sequenceFuture(List<CompletableFuture<R>> futures) {\n        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))\n            .thenApply(v -> futures.stream()\n                .map(AsyncUtils::getValue)\n                .filter(Objects::nonNull)\n                .collect(Collectors.toList())\n            );\n    }\n\n    public static <R> CompletableFuture<List<R>> sequenceSuccessFuture(List<CompletableFuture<R>> futures) {\n        return CompletableFuture.supplyAsync(() -> futures.parallelStream()\n            .map(AsyncUtils::getValue)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList())\n        );\n    }\n\n    public static <T> T getValue(CompletableFuture<T> future) {\n        try {\n            return future.get(10, TimeUnit.SECONDS);\n        } catch (Exception ex) {\n            LOG.error(\"getValue for async result failed\", ex);\n        }\n        return null;\n    }\n\n    public static boolean isSuccessFuture(CompletableFuture future) {\n        return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled();\n    }\n\n    private AsyncUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/ClusterEntityUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.alibaba.csp.sentinel.cluster.ClusterStateManager;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;\nimport com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;\n\n/**\n * @author Eric Zhao\n * @since 1.4.1\n */\npublic final class ClusterEntityUtils {\n\n    public static List<AppClusterServerStateWrapVO> wrapToAppClusterServerState(\n        List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, AppClusterServerStateWrapVO> map = new HashMap<>();\n        Set<String> tokenServerSet = new HashSet<>();\n        // Handle token servers that belong to current app.\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_SERVER) {\n                String ip = stateVO.getIp();\n                String serverId = ip + '@' + stateVO.getCommandPort();\n                ClusterServerStateVO serverStateVO = stateVO.getState().getServer();\n                map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()\n                    .setId(serverId)\n                    .setIp(ip)\n                    .setPort(serverStateVO.getPort())\n                    .setState(serverStateVO)\n                    .setBelongToApp(true)\n                    .setConnectedCount(serverStateVO.getConnection().stream()\n                        .mapToInt(ConnectionGroupVO::getConnectedCount)\n                        .sum()\n                    )\n                );\n                tokenServerSet.add(ip + \":\" + serverStateVO.getPort());\n            }\n        }\n        // Handle token servers from other app.\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                ClusterClientStateVO clientState = stateVO.getState().getClient();\n                if (clientState == null) {\n                    continue;\n                }\n                String serverIp = clientState.getClientConfig().getServerHost();\n                int serverPort = clientState.getClientConfig().getServerPort();\n                if (tokenServerSet.contains(serverIp + \":\" + serverPort)) {\n                    continue;\n                }\n                // We are not able to get the commandPort of foreign token server directly.\n                String serverId = String.format(\"%s:%d\", serverIp, serverPort);\n                map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()\n                    .setId(serverId)\n                    .setIp(serverIp)\n                    .setPort(serverPort)\n                    .setBelongToApp(false)\n                );\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    public static List<AppClusterClientStateWrapVO> wrapToAppClusterClientState(\n        List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, AppClusterClientStateWrapVO> map = new HashMap<>();\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                String ip = stateVO.getIp();\n                String clientId = ip + '@' + stateVO.getCommandPort();\n                ClusterClientStateVO clientStateVO = stateVO.getState().getClient();\n                map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO()\n                    .setId(clientId)\n                    .setIp(ip)\n                    .setState(clientStateVO)\n                    .setCommandPort(stateVO.getCommandPort())\n                );\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    public static List<ClusterGroupEntity> wrapToClusterGroup(List<ClusterUniversalStatePairVO> list) {\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<>();\n        }\n        Map<String, ClusterGroupEntity> map = new HashMap<>();\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n            String ip = stateVO.getIp();\n            if (mode == ClusterStateManager.CLUSTER_SERVER) {\n                String serverAddress = getIp(ip);\n                int port = stateVO.getState().getServer().getPort();\n                map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity()\n                    .setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort())\n                    .setIp(ip).setPort(port)\n                );\n            }\n        }\n        for (ClusterUniversalStatePairVO stateVO : list) {\n            int mode = stateVO.getState().getStateInfo().getMode();\n            String ip = stateVO.getIp();\n            if (mode == ClusterStateManager.CLUSTER_CLIENT) {\n                String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost();\n                Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort();\n                if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) {\n                    continue;\n                }\n\n                ClusterGroupEntity group = map.computeIfAbsent(targetServer,\n                    v -> new ClusterGroupEntity()\n                        .setBelongToApp(true).setMachineId(targetServer)\n                        .setIp(targetServer).setPort(targetPort)\n                );\n                group.getClientSet().add(ip + '@' + stateVO.getCommandPort());\n            }\n        }\n        return new ArrayList<>(map.values());\n    }\n\n    private static String getIp(String str) {\n        if (str.contains(\":\")) {\n            return str.split(\":\")[0];\n        }\n        return str;\n    }\n\n    private ClusterEntityUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.Optional;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.csp.sentinel.util.function.Tuple2;\n\n/**\n * @author Eric Zhao\n */\npublic final class MachineUtils {\n\n    public static Optional<Integer> parseCommandPort(String machineIp) {\n        try {\n            if (!machineIp.contains(\"@\")) {\n                return Optional.empty();\n            }\n            String[] str = machineIp.split(\"@\");\n            if (str.length <= 1) {\n                return Optional.empty();\n            }\n            return Optional.of(Integer.parseInt(str[1]));\n        } catch (Exception ex) {\n            return Optional.empty();\n        }\n    }\n\n    public static Optional<Tuple2<String, Integer>> parseCommandIpAndPort(String machineIp) {\n        try {\n            if (StringUtil.isEmpty(machineIp) || !machineIp.contains(\"@\")) {\n                return Optional.empty();\n            }\n            String[] str = machineIp.split(\"@\");\n            if (str.length <= 1) {\n                return Optional.empty();\n            }\n            return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1])));\n        } catch (Exception ex) {\n            return Optional.empty();\n        }\n    }\n\n    private MachineUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtils.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.util;\n\nimport java.util.Optional;\n\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\n\n/**\n * Util class for parsing version.\n *\n * @author Eric Zhao\n * @since 0.2.1\n */\npublic final class VersionUtils {\n\n    /**\n     * Parse version of Sentinel from raw string.\n     *\n     * @param versionFull version string\n     * @return parsed {@link SentinelVersion} if the version is valid; empty if\n     * there is something wrong with the format\n     */\n    public static Optional<SentinelVersion> parseVersion(String s) {\n        if (StringUtil.isBlank(s)) {\n            return Optional.empty();\n        }\n        try {\n            String versionFull = s;\n            SentinelVersion version = new SentinelVersion();\n            \n            // postfix\n            int index = versionFull.indexOf(\"-\");\n            if (index == 0) {\n                // Start with \"-\"\n                return Optional.empty();\n            }\n            if (index == versionFull.length() - 1) {\n                // End with \"-\"\n            } else if (index > 0) {\n                version.setPostfix(versionFull.substring(index + 1));\n            }\n            \n            if (index >= 0) {\n                versionFull = versionFull.substring(0, index);\n            }\n            \n            // x.x.x\n            int segment = 0;\n            int[] ver = new int[3];\n            while (segment < ver.length) {\n                index = versionFull.indexOf('.');\n                if (index < 0) {\n                    if (versionFull.length() > 0) {\n                        ver[segment] = Integer.valueOf(versionFull);\n                    }\n                    break;\n                }\n                ver[segment] = Integer.valueOf(versionFull.substring(0, index));\n                versionFull = versionFull.substring(index + 1);\n                segment ++;\n            }\n            \n            if (ver[0] < 1) {\n                // Wrong format, return empty.\n                return Optional.empty();\n            } else {\n                return Optional.of(version\n                        .setMajorVersion(ver[0])\n                        .setMinorVersion(ver[1])\n                        .setFixVersion(ver[2]));\n            }\n        } catch (Exception ex) {\n            // Parse fail, return empty.\n            return Optional.empty();\n        }\n    }\n\n    private VersionUtils() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/resources/application.properties",
    "content": "#spring settings\nspring.http.encoding.force=true\nspring.http.encoding.charset=UTF-8\nspring.http.encoding.enabled=true\n\n#logging settings\nlogging.level.org.springframework.web=INFO\nlogging.file=${user.home}/logs/csp/sentinel-dashboard.log\nlogging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\n#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\n\n#auth settings\nauth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine\nauth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png\nauth.username=sentinel\nauth.password=sentinel\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/.gitignore",
    "content": "node_modules/\ntmp/"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/.jshintrc",
    "content": "{\n  /*\n   * ENVIRONMENTS\n   * =================\n   */\n\n  // Define globals exposed by modern browsers.\n  \"browser\": true,\n\n  // Define globals exposed by jQuery.\n  \"jquery\": true,\n\n  // Define globals exposed by Node.js.\n  \"node\": true,\n\n  // Allow ES6.\n  \"esversion\": 6,\n\n  /*\n   * ENFORCING OPTIONS\n   * =================\n   */\n\n  // Force all variable names to use either camelCase style or UPPER_CASE\n  // with underscores.\n  \"camelcase\": true,\n\n  // Prohibit use of == and != in favor of === and !==.\n  \"eqeqeq\": true,\n\n  // Enforce tab width of 2 spaces.\n  \"indent\": 2,\n\n  // Prohibit use of a variable before it is defined.\n  \"latedef\": true,\n\n  // Enforce line length to 100 characters\n  \"maxlen\": 100,\n\n  // Require capitalized names for constructor functions.\n  \"newcap\": true,\n\n  // Enforce use of single quotation marks for strings.\n  \"quotmark\": \"single\",\n\n  // Enforce placing 'use strict' at the top function scope\n  // 前端项目中外层使用 strict 即可，覆盖此条规则\n  \"strict\": false,\n\n  // Prohibit use of explicitly undeclared variables.\n  \"undef\": true,\n\n  // Warn when variables are defined but never used.\n  \"unused\": true,\n\n  /*\n   * RELAXING OPTIONS\n   * =================\n   */\n\n  // Suppress warnings about == null comparisons.\n  \"eqnull\": true,\n  \"globals\": {\n    \"$\": false,\n    \"angular\": false\n  }\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/README.md",
    "content": "# Sentinel Dashboard Frontend\n\n## Env Requirement\n\n- Node.js > 6.x\n\n## Code Guide\n\n- [Code Style Guide for HTML/CSS](https://codeguide.bootcss.com/)\n- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5)\n\n## Install Packages\n\n```\nnpm install\n```\n\n## Start Development\n\n```\nnpm start\n```\n\n## Build for production\n\n```\nnpm run build\n```\n\n## Credit\n\n- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/README_zh.md",
    "content": "# Sentinel Dashboard Frontend\n\n## 环境要求\n\n- Node.js > 6.x\n\n## 编码规范\n\n- HTML/CSS 遵循 [Bootstrap 编码规范](https://codeguide.bootcss.com/)\n- JavaScript 遵循 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) 以及最新的 ES 6 标准\n\n## 安装依赖\n\n```\nnpm i\n```\n\n## 开始本地开发\n\n```\nnpm start\n```\n\n## 构建前端资源\n\n```\nnpm run build\n```\n\n## Credit\n\n- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/app.js",
    "content": "'use strict';\n\n/**\n * @ngdoc overview\n * @name sentinelDashboardApp\n * @description\n * # sentinelDashboardApp\n *\n * Main module of the application.\n */\n\nangular\n  .module('sentinelDashboardApp', [\n    'oc.lazyLoad',\n    'ui.router',\n    'ui.bootstrap',\n    'angular-loading-bar',\n    'ngDialog',\n    'ui.bootstrap.datetimepicker',\n    'ui-notification',\n    'rzTable',\n    'angular-clipboard',\n    'selectize',\n    'angularUtils.directives.dirPagination'\n  ])\n  .factory('AuthInterceptor', ['$window', '$state', function ($window, $state) {\n    var authInterceptor = {\n      'responseError' : function(response) {\n        if (response.status == 401) {\n          // If not auth, clear session in localStorage and jump to the login page\n          $window.localStorage.removeItem(\"session_sentinel_admin\");\n          $state.go('login');\n        }\n\n        return response;\n      },\n      'response' : function(response) {\n        return response;\n      },\n      'request' : function(config) {\n        return config;\n      },\n      'requestError' : function(config){\n        return config;\n      }\n    };\n    return authInterceptor;\n  }])\n  .config(['$stateProvider', '$urlRouterProvider', '$ocLazyLoadProvider', '$httpProvider',\n    function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, $httpProvider) {\n      $httpProvider.interceptors.push('AuthInterceptor');\n\n      $ocLazyLoadProvider.config({\n        debug: false,\n        events: true,\n      });\n\n      $urlRouterProvider.otherwise('/dashboard/home');\n\n      $stateProvider\n        .state('login', {\n            url: '/login',\n            templateUrl: 'app/views/login.html',\n            controller: 'LoginCtl',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/login.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard', {\n        url: '/dashboard',\n        templateUrl: 'app/views/dashboard/main.html',\n        resolve: {\n          loadMyDirectives: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load(\n              {\n                name: 'sentinelDashboardApp',\n                files: [\n                  'app/scripts/directives/header/header.js',\n                  'app/scripts/directives/sidebar/sidebar.js',\n                  'app/scripts/directives/sidebar/sidebar-search/sidebar-search.js',\n                ]\n              });\n          }]\n        }\n      })\n\n      .state('dashboard.home', {\n        url: '/home',\n        templateUrl: 'app/views/dashboard/home.html',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/main.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.flowV1', {\n        templateUrl: 'app/views/flow_v1.html',\n        url: '/flow/:app',\n        controller: 'FlowControllerV1',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/flow_v1.js',\n              ]\n            });\n          }]\n        }\n      })\n\n        .state('dashboard.flow', {\n            templateUrl: 'app/views/flow_v2.html',\n            url: '/v2/flow/:app',\n            controller: 'FlowControllerV2',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/flow_v2.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard.paramFlow', {\n        templateUrl: 'app/views/param_flow.html',\n        url: '/paramFlow/:app',\n        controller: 'ParamFlowController',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/param_flow.js',\n              ]\n            });\n          }]\n        }\n      })\n\n        .state('dashboard.clusterAppAssignManage', {\n            templateUrl: 'app/views/cluster_app_assign_manage.html',\n            url: '/cluster/assign_manage/:app',\n            controller: 'SentinelClusterAppAssignManageController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_assign_manage.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterAppServerList', {\n            templateUrl: 'app/views/cluster_app_server_list.html',\n            url: '/cluster/server/:app',\n            controller: 'SentinelClusterAppServerListController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_server_list.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterAppClientList', {\n            templateUrl: 'app/views/cluster_app_client_list.html',\n            url: '/cluster/client/:app',\n            controller: 'SentinelClusterAppTokenClientListController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_app_token_client_list.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n        .state('dashboard.clusterSingle', {\n            templateUrl: 'app/views/cluster_single_config.html',\n            url: '/cluster/single/:app',\n            controller: 'SentinelClusterSingleController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/cluster_single.js',\n                        ]\n                    });\n                }]\n            }\n        })\n\n      .state('dashboard.authority', {\n            templateUrl: 'app/views/authority.html',\n            url: '/authority/:app',\n            controller: 'AuthorityRuleController',\n            resolve: {\n                loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n                    return $ocLazyLoad.load({\n                        name: 'sentinelDashboardApp',\n                        files: [\n                            'app/scripts/controllers/authority.js',\n                        ]\n                    });\n                }]\n            }\n       })\n\n      .state('dashboard.degrade', {\n        templateUrl: 'app/views/degrade.html',\n        url: '/degrade/:app',\n        controller: 'DegradeCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/degrade.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.system', {\n        templateUrl: 'app/views/system.html',\n        url: '/system/:app',\n        controller: 'SystemCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/system.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.machine', {\n        templateUrl: 'app/views/machine.html',\n        url: '/app/:app',\n        controller: 'MachineCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/machine.js',\n              ]\n            });\n          }]\n        }\n      })\n\n      .state('dashboard.identity', {\n        templateUrl: 'app/views/identity.html',\n        url: '/identity/:app',\n        controller: 'IdentityCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/identity.js',\n              ]\n            });\n          }]\n        }\n      })\n      .state('dashboard.metric', {\n        templateUrl: 'app/views/metric.html',\n        url: '/metric/:app',\n        controller: 'MetricCtl',\n        resolve: {\n          loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {\n            return $ocLazyLoad.load({\n              name: 'sentinelDashboardApp',\n              files: [\n                'app/scripts/controllers/metric.js',\n              ]\n            });\n          }]\n        }\n      });\n  }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/authority.js",
    "content": "/**\n * Authority rule controller.\n */\nangular.module('sentinelDashboardApp').controller('AuthorityRuleController', ['$scope', '$stateParams', 'AuthorityRuleService', 'ngDialog',\n    'MachineService',\n    function ($scope, $stateParams, AuthorityRuleService, ngDialog,\n              MachineService) {\n        $scope.app = $stateParams.app;\n\n        $scope.rulesPageConfig = {\n            pageSize: 10,\n            currentPageIndex: 1,\n            totalPage: 1,\n            totalCount: 0,\n        };\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n\n        function getMachineRules() {\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            AuthorityRuleService.queryMachineRules($scope.app, mac[0], mac[1])\n                .success(function (data) {\n                    if (data.code === 0 && data.data) {\n                        $scope.loadError = undefined;\n                        $scope.rules = data.data;\n                        $scope.rulesPageConfig.totalCount = $scope.rules.length;\n                    } else {\n                        $scope.rules = [];\n                        $scope.rulesPageConfig.totalCount = 0;\n                        $scope.loadError = {message: data.msg};\n                    }\n                })\n                .error((data, header, config, status) => {\n                    $scope.loadError = {message: \"未知错误\"};\n                });\n        };\n        $scope.getMachineRules = getMachineRules;\n        getMachineRules();\n\n        var authorityRuleDialog;\n\n        $scope.editRule = function (rule) {\n            $scope.currentRule = angular.copy(rule);\n            $scope.authorityRuleDialog = {\n                title: '编辑授权规则',\n                type: 'edit',\n                confirmBtnText: '保存',\n            };\n            authorityRuleDialog = ngDialog.open({\n                template: '/app/views/dialog/authority-rule-dialog.html',\n                width: 680,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.addNewRule = function () {\n            var mac = $scope.macInputModel.split(':');\n            $scope.currentRule = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n                rule: {\n                    strategy: 0,\n                    limitApp: '',\n                }\n            };\n            $scope.authorityRuleDialog = {\n                title: '新增授权规则',\n                type: 'add',\n                confirmBtnText: '新增',\n                showAdvanceButton: true,\n            };\n            authorityRuleDialog = ngDialog.open({\n                template: '/app/views/dialog/authority-rule-dialog.html',\n                width: 680,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.saveRule = function () {\n            if (!AuthorityRuleService.checkRuleValid($scope.currentRule.rule)) {\n                return;\n            }\n            if ($scope.authorityRuleDialog.type === 'add') {\n                addNewRuleAndPush($scope.currentRule);\n            } else if ($scope.authorityRuleDialog.type === 'edit') {\n                saveRuleAndPush($scope.currentRule, true);\n            }\n        };\n\n        function addNewRuleAndPush(rule) {\n            AuthorityRuleService.addNewRule(rule).success((data) => {\n                if (data.success) {\n                    getMachineRules();\n                    authorityRuleDialog.close();\n                } else {\n                    alert('添加规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('添加规则失败：' + data.msg);\n                } else {\n                    alert(\"添加规则失败：未知错误\");\n                }\n            });\n        }\n\n        function saveRuleAndPush(rule, edit) {\n            AuthorityRuleService.saveRule(rule).success(function (data) {\n                if (data.success) {\n                    alert(\"修改规则成功\");\n                    getMachineRules();\n                    if (edit) {\n                        authorityRuleDialog.close();\n                    } else {\n                        confirmDialog.close();\n                    }\n                } else {\n                    alert('修改规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('修改规则失败：' + data.msg);\n                } else {\n                    alert(\"修改规则失败：未知错误\");\n                }\n            });\n        }\n\n        function deleteRuleAndPush(entity) {\n            if (entity.id === undefined || isNaN(entity.id)) {\n                alert('规则 ID 不合法！');\n                return;\n            }\n            AuthorityRuleService.deleteRule(entity).success((data) => {\n                if (data.code == 0) {\n                    getMachineRules();\n                    confirmDialog.close();\n                } else {\n                    alert('删除规则失败：' + data.msg);\n                }\n            }).error((data) => {\n                if (data) {\n                    alert('删除规则失败：' + data.msg);\n                } else {\n                    alert(\"删除规则失败：未知错误\");\n                }\n            });\n        };\n\n        var confirmDialog;\n        $scope.deleteRule = function (ruleEntity) {\n            $scope.currentRule = ruleEntity;\n            $scope.confirmDialog = {\n                title: '删除授权规则',\n                type: 'delete_rule',\n                attentionTitle: '请确认是否删除如下授权限流规则',\n                attention: '资源名: ' + ruleEntity.rule.resource + ', 流控应用: ' + ruleEntity.rule.limitApp +\n                    ', 类型: ' + (ruleEntity.rule.strategy === 0 ? '白名单' : '黑名单'),\n                confirmBtnText: '删除',\n            };\n            confirmDialog = ngDialog.open({\n                template: '/app/views/dialog/confirm-dialog.html',\n                scope: $scope,\n                overlay: true\n            });\n        };\n\n        $scope.confirm = function () {\n            if ($scope.confirmDialog.type === 'delete_rule') {\n                deleteRuleAndPush($scope.currentRule);\n            } else {\n                console.error('error');\n            }\n        };\n\n        queryAppMachines();\n\n        function queryAppMachines() {\n            MachineService.getAppMachines($scope.app).success(\n                function (data) {\n                    if (data.code == 0) {\n                        // $scope.machines = data.data;\n                        if (data.data) {\n                            $scope.machines = [];\n                            $scope.macsInputOptions = [];\n                            data.data.forEach(function (item) {\n                                if (item.healthy) {\n                                    $scope.macsInputOptions.push({\n                                        text: item.ip + ':' + item.port,\n                                        value: item.ip + ':' + item.port\n                                    });\n                                }\n                            });\n                        }\n                        if ($scope.macsInputOptions.length > 0) {\n                            $scope.macInputModel = $scope.macsInputOptions[0].value;\n                        }\n                    } else {\n                        $scope.macsInputOptions = [];\n                    }\n                }\n            );\n        };\n        $scope.$watch('macInputModel', function () {\n            if ($scope.macInputModel) {\n                getMachineRules();\n            }\n        });\n    }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_assign_manage.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingClientAddressList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(ip)) {\n                    tmpMap.set(ip, group);\n                }\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                    return;\n                }\n\n                if (!tmpMap.has(targetServer)) {\n                    let group = {\n                        ip: targetServer,\n                        machineId: targetServer,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(targetServer, group);\n                } else {\n                    let g = tmpMap.get(targetServer);\n                    g.clientSet.push(machineId);\n                }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.onCurrentServerChange = () => {\n            resetChosen();\n        };\n\n        $scope.remainingClientAddressList = [];\n\n        $scope.moveToServerGroup = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                chosenServer.clientSet.push(e);\n                removeFromArr($scope.remainingClientAddressList, e);\n            });\n            resetChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingClientAddressList.push(e);\n                removeFromArr($scope.tmp.curChosenServer.clientSet, e);\n            });\n            resetChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        $scope.addToServerList = () => {\n            let group;\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                group = {\n                    machineId: e,\n                    ip: parseIpFromMachineId(e),\n                    port: DEFAULT_CLUSTER_SERVER_PORT,\n                    clientSet: [],\n                    namespaceSetStr: 'default,' + $scope.app,\n                    belongToApp: true,\n                };\n                $scope.clusterMap.push(group);\n                removeFromArr($scope.remainingClientAddressList, e);\n                $scope.tmp.curChosenServer = group;\n            });\n            resetChosen();\n        };\n\n        $scope.removeFromServerList = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            chosenServer.clientSet.forEach((e) => {\n                if (e !== undefined) {\n                    $scope.remainingClientAddressList.push(e);\n                }\n            });\n\n            if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {\n                $scope.remainingClientAddressList.push(chosenServer.machineId);\n            } else {\n                alert('提示：非本应用内机器将不会置回空闲列表中');\n            }\n\n            removeFromArr($scope.clusterMap, chosenServer);\n\n            resetChosen();\n\n            if ($scope.clusterMap.length > 0) {\n                $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                $scope.onCurrentServerChange();\n            } else {\n                $scope.tmp.curChosenServer = {};\n            }\n        };\n\n        function retrieveClusterAppInfo() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    if ($scope.clusterMap.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                        $scope.onCurrentServerChange();\n                    }\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterAppInfo();\n\n        $scope.saveAndApplyAssign = () => {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let cm = $scope.clusterMap;\n            if (!cm) {\n                cm = [];\n            }\n            cm.forEach((e) => {\n                e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);\n            });\n            cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);\n            let request = {\n                clusterMap: cm,\n                remainingList: $scope.remainingClientAddressList,\n            };\n            ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        alert('推送完毕。token server 失败列表：' + JSON.stringify(failedServerSet) +\n                            '; token client 失败列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    retrieveClusterAppInfo();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_list.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n        const DEFAULT_NAMESPACE = 'default';\n        const DEFAULT_MAX_ALLOWED_QPS = 20000;\n\n        // tmp for dialog temporary data.\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        $scope.remainingMachineList = [];\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) {\n                return DEFAULT_NAMESPACE;\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                let ns = set[i];\n                if (ns !== DEFAULT_NAMESPACE) {\n                    s = s + ns;\n                    if (i < set.length - 1) {\n                        s = s + ',';\n                    }\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function removeFromArrIf(arr, f) {\n            for (let i = 0; i < arr.length; i++) {\n                if (f(arr[i]) === true) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetAssignDialogChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            let serverCommandPortMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingMachineList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingMachineList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    maxAllowedQps: e.state.server.flow.maxAllowedQps,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(machineId)) {\n                    tmpMap.set(machineId, group);\n                }\n                serverCommandPortMap.set(ip + ':' + e.state.server.port, e.commandPort);\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingMachineList.push(generateMachineId(e));\n                    return;\n                }\n\n                let serverHostPort = targetServer + ':' + targetPort;\n\n                if (serverCommandPortMap.has(serverHostPort)) {\n                    let serverCommandPort = serverCommandPortMap.get(serverHostPort);\n                    let g;\n                    if (serverCommandPort < 0) {\n                        // Not belong to this app.\n                        g = tmpMap.get(serverHostPort);\n                    } else {\n                        // Belong to this app.\n                        g = tmpMap.get(targetServer + '@' + serverCommandPort);\n                    }\n                    g.clientSet.push(machineId);\n                } else {\n                    let group = {\n                        ip: targetServer,\n                        machineId: serverHostPort,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(serverHostPort, group);\n                    // Indicates that it's not belonging to current app.\n                    serverCommandPortMap.set(serverHostPort, -1);\n                }\n\n                // if (!tmpMap.has(serverHostPort)) {\n                //     let group = {\n                //         ip: targetServer,\n                //         machineId: targetServer,\n                //         port: targetPort,\n                //         clientSet: [machineId],\n                //         belongToApp: false,\n                //     };\n                //     tmpMap.set(targetServer, group);\n                // } else {\n                //     let g = tmpMap.get(targetServer);\n                //     g.clientSet.push(machineId);\n                // }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.notChosenServer = (id) => {\n            return id !== $scope.serverAssignDialogData.serverData.currentServer;\n        };\n\n        $scope.onCurrentServerChange = () => {\n            resetAssignDialogChosen();\n        };\n\n        $scope.moveToServerGroup = () => {\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                $scope.serverAssignDialogData.serverData.clientSet.push(e);\n                removeFromArr($scope.remainingMachineList, e);\n            });\n            resetAssignDialogChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingMachineList.push(e);\n                removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e);\n            });\n            resetAssignDialogChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf(':') !== -1) {\n                return machineId.split(':')[0];\n            }\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        function retrieveClusterAssignInfoOfApp() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n\n        $scope.newServerDialog = () => {\n            retrieveClusterAssignInfoOfApp();\n            $scope.serverAssignDialogData = {\n                title: '新增 Token Server',\n                type: 'add',\n                confirmBtnText: '保存',\n                serverData: {\n                    serverType: 0,\n                    clientSet: [],\n                    serverPort: DEFAULT_CLUSTER_SERVER_PORT,\n                    maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS,\n                }\n            };\n            $scope.serverAssignDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',\n                width: 1000,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        $scope.modifyServerAssignConfig = (serverVO) => {\n            let id = serverVO.id;\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    let clusterMap = $scope.clusterMap;\n                    let d;\n                    for (let i = 0; i < clusterMap.length; i++) {\n                        if (clusterMap[i].machineId === id) {\n                            d = clusterMap[i];\n                        }\n                    }\n                    if (!d) {\n                        alert('状态错误');\n                        return;\n                    }\n                    $scope.serverAssignDialogData = {\n                        title: 'Token Server 分配编辑',\n                        type: 'edit',\n                        confirmBtnText: '保存',\n                        serverData: {\n                            currentServer: d.machineId,\n                            belongToApp: serverVO.belongToApp,\n                            serverPort: d.port,\n                            clientSet: d.clientSet,\n                        }\n                    };\n                    if (d.maxAllowedQps !== undefined) {\n                        $scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps;\n                    }\n                    $scope.serverAssignDialog = ngDialog.open({\n                        template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',\n                        width: 1000,\n                        overlay: true,\n                        scope: $scope\n                    });\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        };\n\n        function getRemainingMachineList() {\n            return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e));\n        }\n\n        function doApplyNewSingleServerAssign() {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let serverData = $scope.serverAssignDialogData.serverData;\n            let belongToApp = serverData.serverType == 0; // don't modify here!\n            let machineId = serverData.currentServer;\n            let request = {\n                clusterMap: {\n                    machineId: machineId,\n                    ip: parseIpFromMachineId(machineId),\n                    port: serverData.serverPort,\n                    clientSet: serverData.clientSet,\n                    belongToApp: belongToApp,\n                    maxAllowedQps: serverData.maxAllowedQps,\n                },\n                remainingList: getRemainingMachineList(),\n            };\n            ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        let failedSet = [];\n                        if (failedServerSet) {\n                            failedServerSet.forEach((e) => {\n                                failedSet.push(e);\n                            });\n                        }\n                        if (failedClientSet) {\n                            failedClientSet.forEach((e) => {\n                                failedSet.push(e);\n                            });\n                        }\n\n                        alert('推送完毕。失败机器列表：' + JSON.stringify(failedSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        }\n\n        function doApplySingleServerAssignEdit() {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let serverData = $scope.serverAssignDialogData.serverData;\n            let machineId = serverData.currentServer;\n            let request = {\n                clusterMap: {\n                    machineId: machineId,\n                    ip: parseIpFromMachineId(machineId),\n                    port: serverData.serverPort,\n                    clientSet: serverData.clientSet,\n                    belongToApp: serverData.belongToApp,\n                },\n                remainingList: $scope.remainingMachineList,\n            };\n            if (serverData.maxAllowedQps !== undefined) {\n                request.clusterMap.maxAllowedQps = serverData.maxAllowedQps;\n            }\n            ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        let failedSet = [];\n                        failedServerSet.forEach(failedSet.push);\n                        failedClientSet.forEach(failedSet.push);\n                        alert('推送完毕。失败机器列表：' + JSON.stringify(failedSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        }\n\n        $scope.saveAssignForDialog = () => {\n            if (!checkAssignDialogValid()) {\n                return;\n            }\n            if ($scope.serverAssignDialogData.type === 'add') {\n                doApplyNewSingleServerAssign();\n            } else if ($scope.serverAssignDialogData.type === 'edit') {\n                doApplySingleServerAssignEdit();\n            } else {\n                alert('未知的操作');\n            }\n        };\n\n        function checkAssignDialogValid() {\n            let serverData = $scope.serverAssignDialogData.serverData;\n            if (serverData.currentServer === undefined || serverData.currentServer === '') {\n                alert('请指定有效的 Token Server');\n                return false;\n            }\n            if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) {\n                alert('请输入合法的端口值');\n                return false;\n            }\n            if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) {\n                alert('请输入合法的最大允许 QPS');\n                return false;\n            }\n            return true;\n        }\n\n        $scope.viewConnectionDetail = (serverVO) => {\n            $scope.connectionDetailDialogData = {\n                serverData: serverVO\n            };\n            $scope.connectionDetailDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html',\n                width: 700,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        function generateRequestLimitDataStr(limitData) {\n            if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) {\n                 return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps;\n            }\n            for (let i = 0; i < limitData.length; i++) {\n                let crl = limitData[i];\n                if (crl.namespace === $scope.app) {\n                    return '' + crl.currentQps + ' / ' + crl.maxAllowedQps;\n                }\n            }\n            return '0';\n        }\n\n        function processServerListData(serverVO) {\n            if (serverVO.state && serverVO.state.namespaceSet) {\n                serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);\n            }\n            if (serverVO.state && serverVO.state.requestLimitData) {\n                serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData);\n            }\n        }\n\n        $scope.generateConnectionSet = (data) => {\n            let connectionSet = data;\n            let s = '';\n            if (connectionSet) {\n                s = s + '[';\n                for (let i = 0; i < connectionSet.length; i++) {\n                    s = s + connectionSet[i].address;\n                    if (i < connectionSet.length - 1) {\n                        s = s + ', ';\n                    }\n                }\n                s = s + ']';\n            } else {\n                s = '[]';\n            }\n            return s;\n        };\n\n        function retrieveClusterServerInfo() {\n            ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.serverVOList = data.data;\n                    $scope.serverVOList.forEach(processServerListData);\n                } else {\n                    $scope.serverVOList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterServerInfo();\n\n        let confirmUnbindServerDialog;\n        $scope.unbindServer = (id) => {\n            $scope.pendingUnbindIds = [id];\n            $scope.confirmDialog = {\n                title: '移除 Token Server',\n                type: 'unbind_token_server',\n                attentionTitle: '请确认是否移除以下 Token Server（该 server 下的 client 也会解除分配）',\n                attention: id + '',\n                confirmBtnText: '移除',\n            };\n            confirmUnbindServerDialog = ngDialog.open({\n                template: '/app/views/dialog/confirm-dialog.html',\n                scope: $scope,\n                overlay: true\n            });\n        };\n\n        function apiUnbindServerAssign(ids) {\n            ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('成功');\n                    } else {\n                        alert('操作推送完毕，部分失败机器列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n            // confirmUnbindServerDialog.close();\n        }\n\n        // Confirm function for confirm dialog.\n        $scope.confirm = () => {\n            if ($scope.confirmDialog.type === 'unbind_token_server') {\n                apiUnbindServerAssign($scope.pendingUnbindIds);\n            } else {\n                console.error('Error dialog when unbinding token server');\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_manage.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n        const DEFAULT_CLUSTER_SERVER_PORT = 18730;\n\n        $scope.tmp = {\n            curClientChosen: [],\n            curRemainingClientChosen: [],\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function processAppSingleData(data) {\n            if (data.state.server && data.state.server.namespaceSet) {\n                data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);\n                data.mode = data.state.stateInfo.mode;\n            }\n        }\n\n        function removeFromArr(arr, v) {\n            for (let i = 0; i < arr.length; i++) {\n                if (arr[i] === v) {\n                    arr.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function resetChosen() {\n            $scope.tmp.curClientChosen = [];\n            $scope.tmp.curRemainingClientChosen = [];\n        }\n\n        function generateMachineId(e) {\n            return e.ip + '@' + e.commandPort;\n        }\n\n        function applyClusterMap(appClusterMachineList) {\n            if (!appClusterMachineList) {\n                return;\n            }\n            let tmpMap = new Map();\n            $scope.clusterMap = [];\n            $scope.remainingClientAddressList = [];\n            let tmpServerList = [];\n            let tmpClientList = [];\n            appClusterMachineList.forEach((e) => {\n                if (e.mode === CLUSTER_MODE_CLIENT) {\n                    tmpClientList.push(e);\n                } else if (e.mode === CLUSTER_MODE_SERVER) {\n                    tmpServerList.push(e);\n                } else {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                }\n            });\n            tmpServerList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n                let group = {\n                    ip: ip,\n                    machineId: machineId,\n                    port: e.state.server.port,\n                    clientSet: [],\n                    namespaceSetStr: e.state.server.namespaceSetStr,\n                    belongToApp: true,\n                };\n                if (!tmpMap.has(ip)) {\n                    tmpMap.set(ip, group);\n                }\n            });\n            tmpClientList.forEach((e) => {\n                let ip = e.ip;\n                let machineId = ip + '@' + e.commandPort;\n\n                let targetServer = e.state.client.clientConfig.serverHost;\n                let targetPort = e.state.client.clientConfig.serverPort;\n                if (targetServer === undefined || targetServer === '' ||\n                    targetPort === undefined || targetPort <= 0) {\n                    $scope.remainingClientAddressList.push(generateMachineId(e));\n                    return;\n                }\n\n                if (!tmpMap.has(targetServer)) {\n                    let group = {\n                        ip: targetServer,\n                        machineId: targetServer,\n                        port: targetPort,\n                        clientSet: [machineId],\n                        belongToApp: false,\n                    };\n                    tmpMap.set(targetServer, group);\n                } else {\n                    let g = tmpMap.get(targetServer);\n                    g.clientSet.push(machineId);\n                }\n            });\n            tmpMap.forEach((v) => {\n                if (v !== undefined) {\n                    $scope.clusterMap.push(v);\n                }\n            });\n        }\n\n        $scope.onCurrentServerChange = () => {\n            resetChosen();\n        };\n\n        $scope.remainingClientAddressList = [];\n\n        $scope.moveToServerGroup = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                chosenServer.clientSet.push(e);\n                removeFromArr($scope.remainingClientAddressList, e);\n            });\n            resetChosen();\n        };\n\n        $scope.moveToRemainingSharePool = () => {\n            $scope.tmp.curClientChosen.forEach(e => {\n                $scope.remainingClientAddressList.push(e);\n                removeFromArr($scope.tmp.curChosenServer.clientSet, e);\n            });\n            resetChosen();\n        };\n\n        function parseIpFromMachineId(machineId) {\n            if (machineId.indexOf('@') === -1) {\n                return machineId;\n            }\n            let arr = machineId.split('@');\n            return arr[0];\n        }\n\n        $scope.addToServerList = () => {\n            let group;\n            $scope.tmp.curRemainingClientChosen.forEach(e => {\n                group = {\n                    machineId: e,\n                    ip: parseIpFromMachineId(e),\n                    port: DEFAULT_CLUSTER_SERVER_PORT,\n                    clientSet: [],\n                    namespaceSetStr: 'default,' + $scope.app,\n                    belongToApp: true,\n                };\n                $scope.clusterMap.push(group);\n                removeFromArr($scope.remainingClientAddressList, e);\n                $scope.tmp.curChosenServer = group;\n            });\n            resetChosen();\n        };\n\n        $scope.removeFromServerList = () => {\n            let chosenServer = $scope.tmp.curChosenServer;\n            if (!chosenServer || !chosenServer.machineId) {\n                return;\n            }\n            chosenServer.clientSet.forEach((e) => {\n                if (e !== undefined) {\n                    $scope.remainingClientAddressList.push(e);\n                }\n            });\n\n            if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {\n                $scope.remainingClientAddressList.push(chosenServer.machineId);\n            } else {\n                alert('提示：非本应用内机器将不会置回空闲列表中');\n            }\n\n            removeFromArr($scope.clusterMap, chosenServer);\n\n            resetChosen();\n\n            if ($scope.clusterMap.length > 0) {\n                $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                $scope.onCurrentServerChange();\n            } else {\n                $scope.tmp.curChosenServer = {};\n            }\n        };\n\n        function retrieveClusterAppInfo() {\n            ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.appClusterMachineList = data.data;\n                    $scope.appClusterMachineList.forEach(processAppSingleData);\n                    applyClusterMap($scope.appClusterMachineList);\n                    if ($scope.clusterMap.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.clusterMap[0];\n                        $scope.onCurrentServerChange();\n                    }\n                } else {\n                    $scope.appClusterMachineList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterAppInfo();\n\n        $scope.saveAndApplyAssign = () => {\n            let ok = confirm('是否确认执行变更？');\n            if (!ok) {\n                return;\n            }\n            let cm = $scope.clusterMap;\n            if (!cm) {\n                cm = [];\n            }\n            cm.forEach((e) => {\n                e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);\n            });\n            cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);\n            let request = {\n                clusterMap: cm,\n                remainingList: $scope.remainingClientAddressList,\n            };\n            ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    let failedServerSet = data.data.failedServerSet;\n                    let failedClientSet = data.data.failedClientSet;\n                    if (failedClientSet.length === 0 && failedServerSet.length === 0) {\n                        alert('全部推送成功');\n                    } else {\n                        alert('推送完毕。token server 失败列表：' + JSON.stringify(failedServerSet) +\n                            '; token client 失败列表：' + JSON.stringify(failedClientSet));\n                    }\n\n                    retrieveClusterAppInfo();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('推送失败：' + data.msg);\n                    }\n                }\n            }).error(() => {\n                alert('未知错误');\n            });\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_monitor.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_SERVER = 1;\n\n        $scope.tmp = {\n            curChosenServer: {},\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function processServerData(serverVO) {\n            if (serverVO.state && serverVO.state.namespaceSet) {\n                serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);\n            }\n        }\n\n        $scope.generateConnectionSet = (data) => {\n            let connectionSet = data;\n            let s = '';\n            if (connectionSet) {\n                s = s + '[';\n                for (let i = 0; i < connectionSet.length; i++) {\n                    s = s + connectionSet[i].address;\n                    if (i < connectionSet.length - 1) {\n                        s = s + ', ';\n                    }\n                }\n                s = s + ']';\n            } else {\n                s = '[]';\n            }\n            return s;\n        };\n\n        $scope.onChosenServerChange = () => {\n\n        };\n\n        function retrieveClusterServerInfo() {\n            ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {\n                if (data.code === 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.serverVOList = data.data;\n                    $scope.serverVOList.forEach(processServerData);\n\n                    if ($scope.serverVOList.length > 0) {\n                        $scope.tmp.curChosenServer = $scope.serverVOList[0];\n                        $scope.onChosenServerChange();\n                    }\n                } else {\n                    $scope.serverVOList = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error(() => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        retrieveClusterServerInfo();\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_token_client_list.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n\n        const UNSUPPORTED_CODE = 4041;\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n\n        function processClientData(clientVO) {\n\n        }\n\n        $scope.modifyClientConfigDialog = (clientVO) => {\n            if (!clientVO) {\n                return;\n            }\n            $scope.ccDialogData = {\n                ip: clientVO.ip,\n                commandPort: clientVO.commandPort,\n                clientId: clientVO.id,\n                serverHost: clientVO.state.clientConfig.serverHost,\n                serverPort: clientVO.state.clientConfig.serverPort,\n                requestTimeout: clientVO.state.clientConfig.requestTimeout,\n            };\n            $scope.ccDialog = ngDialog.open({\n                template: '/app/views/dialog/cluster/cluster-client-config-dialog.html',\n                width: 700,\n                overlay: true,\n                scope: $scope\n            });\n        };\n\n        function checkValidClientConfig(config) {\n            if (!config.serverHost || config.serverHost.trim() == '') {\n                alert('请输入有效的 Token Server IP');\n                return false;\n            }\n            if (config.serverPort === undefined || config.serverPort <= 0 ||  config.serverPort > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            if (config.requestTimeout === undefined || config.requestTimeout <= 0) {\n                alert('请输入有效的请求超时时长');\n                return false;\n            }\n            return true;\n        }\n\n        $scope.doModifyClientConfig = () => {\n            if (!checkValidClientConfig($scope.ccDialogData)) {\n                return;\n            }\n            let id = $scope.ccDialogData.id;\n            let request = {\n                app: $scope.app,\n                ip: $scope.ccDialogData.ip,\n                port: $scope.ccDialogData.commandPort,\n                mode: CLUSTER_MODE_CLIENT,\n                clientConfig: {\n                    serverHost: $scope.ccDialogData.serverHost,\n                    serverPort: $scope.ccDialogData.serverPort,\n                    requestTimeout: $scope.ccDialogData.requestTimeout,\n                }\n            };\n            ClusterStateService.modifyClusterConfig(request).success((data) => {\n                if (data.code === 0 && data.data) {\n                    alert('修改 Token Client 配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        };\n\n        function retrieveClusterTokenClientInfo() {\n            ClusterStateService.fetchClusterClientStateOfApp($scope.app)\n                .success((data) => {\n                    if (data.code === 0 && data.data) {\n                        $scope.loadError = undefined;\n                        $scope.clientVOList = data.data;\n                        $scope.clientVOList.forEach(processClientData);\n                    } else {\n                        $scope.clientVOList = [];\n                        if (data.code === UNSUPPORTED_CODE) {\n                            $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                        } else {\n                            $scope.loadError = {message: data.msg};\n                        }\n                    }\n                })\n                .error(() => {\n                    $scope.loadError = {message: '未知错误'};\n                });\n        }\n\n        retrieveClusterTokenClientInfo();\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_single.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog',\n    'MachineService', 'ClusterStateService',\n    function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {\n        $scope.app = $stateParams.app;\n        const UNSUPPORTED_CODE = 4041;\n\n        const CLUSTER_MODE_CLIENT = 0;\n        const CLUSTER_MODE_SERVER = 1;\n\n        $scope.macsInputConfig = {\n            searchField: ['text', 'value'],\n            persist: true,\n            create: false,\n            maxItems: 1,\n            render: {\n                item: function (data, escape) {\n                    return '<div>' + escape(data.text) + '</div>';\n                }\n            },\n            onChange: function (value, oldValue) {\n                $scope.macInputModel = value;\n            }\n        };\n\n        function convertSetToString(set) {\n            if (set === undefined) {\n                return '';\n            }\n            let s = '';\n            for (let i = 0; i < set.length; i++) {\n                s = s + set[i];\n                if (i < set.length - 1) {\n                    s = s + ',';\n                }\n            }\n            return s;\n        }\n\n        function convertStrToNamespaceSet(str) {\n            if (str === undefined || str === '') {\n                return [];\n            }\n            let arr = [];\n            let spliced = str.split(',');\n            spliced.forEach((v) => {\n                arr.push(v.trim());\n            });\n            return arr;\n        }\n\n        function fetchMachineClusterState() {\n            if (!$scope.macInputModel || $scope.macInputModel === '') {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    $scope.loadError = undefined;\n                    $scope.stateVO = data.data;\n                    $scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode;\n                    if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) {\n                        $scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet);\n                    }\n                } else {\n                    $scope.stateVO = {};\n                    if (data.code === UNSUPPORTED_CODE) {\n                        $scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流，请升级至 1.4.0 以上版本并引入相关依赖。'}\n                    } else {\n                        $scope.loadError = {message: data.msg};\n                    }\n                }\n            }).error((data, header, config, status) => {\n                $scope.loadError = {message: '未知错误'};\n            });\n        }\n\n        fetchMachineClusterState();\n\n        function checkValidClientConfig(stateVO) {\n            if (!stateVO.client || !stateVO.client.clientConfig) {\n                alert('不合法的配置');\n                return false;\n            }\n            let config = stateVO.client.clientConfig;\n            if (!config.serverHost || config.serverHost.trim() == '') {\n                alert('请输入有效的 Token Server IP');\n                return false;\n            }\n            if (config.serverPort === undefined || config.serverPort <= 0 ||  config.serverPort > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            if (config.requestTimeout === undefined || config.requestTimeout <= 0) {\n                alert('请输入有效的请求超时时长');\n                return false;\n            }\n            return true;\n        }\n\n        function sendClusterClientRequest(stateVO) {\n            if (!checkValidClientConfig(stateVO)) {\n                return;\n            }\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            let request = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n            };\n            request.mode = CLUSTER_MODE_CLIENT;\n            request.clientConfig = stateVO.client.clientConfig;\n            ClusterStateService.modifyClusterConfig(request).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    alert('修改集群限流客户端配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        }\n\n        function checkValidServerConfig(stateVO) {\n            if (!stateVO.server || !stateVO.server.transport) {\n                alert('不合法的配置');\n                return false;\n            }\n            if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') {\n                alert('请输入有效的命名空间集合（多个 namespace 以 , 分隔）');\n                return false;\n            }\n            let transportConfig = stateVO.server.transport;\n            if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) {\n                alert('请输入有效的 Token Server 端口');\n                return false;\n            }\n            let flowConfig = stateVO.server.flow;\n            if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) {\n                alert('请输入有效的最大允许 QPS');\n                return false;\n            }\n            // if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) {\n            //     alert('请输入有效的连接清理时长 (idleSeconds)');\n            //     return false;\n            // }\n            return true;\n        }\n\n        function sendClusterServerRequest(stateVO) {\n            if (!checkValidServerConfig(stateVO)) {\n                return;\n            }\n            if (!$scope.macInputModel) {\n                return;\n            }\n            let mac = $scope.macInputModel.split(':');\n            let request = {\n                app: $scope.app,\n                ip: mac[0],\n                port: mac[1],\n            };\n            request.mode = CLUSTER_MODE_SERVER;\n            request.flowConfig = stateVO.server.flow;\n            request.transportConfig = stateVO.server.transport;\n            request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr);\n            ClusterStateService.modifyClusterConfig(request).success(function (data) {\n                if (data.code == 0 && data.data) {\n                    alert('修改集群限流服务端配置成功');\n                    window.location.reload();\n                } else {\n                    if (data.code === UNSUPPORTED_CODE) {\n                        alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端，请升级至 1.4.0 以上版本并引入相关依赖。');\n                    } else {\n                        alert('修改失败：' + data.msg);\n                    }\n                }\n            }).error((data, header, config, status) => {\n                alert('未知错误');\n            });\n        }\n\n\n        $scope.saveConfig = () => {\n            let ok = confirm('是否确定修改集群限流配置？');\n            if (!ok) {\n                return;\n            }\n            let mode = $scope.stateVO.stateInfo.mode;\n            if (mode != 1 && mode != 0) {\n                alert('未知的集群限流模式');\n                return;\n            }\n            if (mode == 0) {\n                sendClusterClientRequest($scope.stateVO);\n            } else {\n                sendClusterServerRequest($scope.stateVO);\n            }\n        };\n\n        function queryAppMachines() {\n            MachineService.getAppMachines($scope.app).success(\n                function (data) {\n                    if (data.code === 0) {\n                        // $scope.machines = data.data;\n                        if (data.data) {\n                            $scope.machines = [];\n                            $scope.macsInputOptionsOrigin = [];\n                            $scope.macsInputOptions = [];\n                            data.data.forEach(function (item) {\n                                if (item.healthy) {\n                                    $scope.macsInputOptionsOrigin.push({\n                                        text: item.ip + ':' + item.port,\n                                        value: item.ip + ':' + item.port\n                                    });\n                                }\n                            });\n                            $scope.macsInputOptions = $scope.macsInputOptionsOrigin;\n                        }\n                        if ($scope.macsInputOptions.length > 0) {\n                            $scope.macInputModel = $scope.macsInputOptions[0].value;\n                        }\n                    } else {\n                        $scope.macsInputOptions = [];\n                    }\n                }\n            );\n        }\n        queryAppMachines();\n\n        $scope.$watch('searchKey', function () {\n            if (!$scope.macsInputOptions) {\n                return;\n            }\n            if ($scope.searchKey) {\n                $scope.macsInputOptions = $scope.macsInputOptionsOrigin\n                    .filter((e) => e.value.indexOf($scope.searchKey) !== -1);\n            } else {\n                $scope.macsInputOptions = $scope.macsInputOptionsOrigin;\n            }\n            if ($scope.macsInputOptions.length > 0) {\n                $scope.macInputModel = $scope.macsInputOptions[0].value;\n            } else {\n                $scope.macInputModel = '';\n            }\n        });\n\n        $scope.$watch('macInputModel', function () {\n            if ($scope.macInputModel) {\n                fetchMachineClusterState();\n            }\n        });\n    }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/degrade.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService',\n  function ($scope, $stateParams, DegradeService, ngDialog, MachineService) {\n    //初始化\n    $scope.app = $stateParams.app;\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var degradeRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.degradeRuleDialog = {\n        title: '编辑降级规则',\n        type: 'edit',\n        confirmBtnText: '保存'\n      };\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default'\n      };\n      $scope.degradeRuleDialog = {\n        title: '新增降级规则',\n        type: 'add',\n        confirmBtnText: '新增'\n      };\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!DegradeService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.degradeRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.degradeRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    function parseDegradeMode(grade) {\n        switch (grade) {\n            case 0:\n              return 'RT';\n            case 1:\n              return '异常比例';\n            case 2:\n              return '异常数';\n            default:\n              return '未知';\n        }\n    }\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除降级规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下降级规则',\n        attention: '资源名: ' + rule.resource +\n            ', 降级模式: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type == 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      DegradeService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      DegradeService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          degradeRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function saveRule(rule, edit) {\n      DegradeService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            degradeRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/flow_v1.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, FlowService, ngDialog,\n    MachineService) {\n    $scope.app = $stateParams.app;\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    $scope.generateThresholdTypeShow = (rule) => {\n      if (!rule.clusterMode) {\n        return '单机';\n      }\n      if (rule.clusterConfig.thresholdType === 0) {\n        return '集群均摊';\n      } else if (rule.clusterConfig.thresholdType === 1) {\n        return '集群总体';\n      } else {\n        return '集群';\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var flowRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.flowRuleDialog = {\n        title: '编辑流控规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 1,\n        strategy: 0,\n        controlBehavior: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n          thresholdType: 0\n        }\n      };\n      $scope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!FlowService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.flowRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.flowRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除流控规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下流控规则',\n        attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp\n          + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      FlowService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      FlowService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    $scope.onOpenAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = false;\n    };\n    $scope.onCloseAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = true;\n    };\n\n    function saveRule(rule, edit) {\n      FlowService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            flowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/flow_v2.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, FlowService, ngDialog,\n    MachineService) {\n    $scope.app = $stateParams.app;\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    $scope.generateThresholdTypeShow = (rule) => {\n      if (!rule.clusterMode) {\n        return '单机';\n      }\n      if (rule.clusterConfig.thresholdType === 0) {\n        return '集群均摊';\n      } else if (rule.clusterConfig.thresholdType === 1) {\n        return '集群总体';\n      } else {\n        return '集群';\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n\n    var flowRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.flowRuleDialog = {\n        title: '编辑流控规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 1,\n        strategy: 0,\n        controlBehavior: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n          thresholdType: 0,\n          fallbackToLocalWhenFail: true\n        }\n      };\n      $scope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if (!FlowService.checkRuleValid($scope.currentRule)) {\n        return;\n      }\n      if ($scope.flowRuleDialog.type === 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.flowRuleDialog.type === 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      $scope.confirmDialog = {\n        title: '删除流控规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下流控规则',\n        attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp\n          + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRule($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      FlowService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      FlowService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    $scope.onOpenAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = false;\n    };\n    $scope.onCloseAdvanceClick = function () {\n      $scope.flowRuleDialog.showAdvanceButton = true;\n    };\n\n    function saveRule(rule, edit) {\n      FlowService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          if (edit) {\n            flowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/home.js",
    "content": "/**\n * @ngdoc function\n * @name sentinelDashboardApp.controller:MainCtrl\n * @description\n * # MainCtrl\n * Controller of the sentinelDashboardApp\n */\nangular.module('sentinelDashboardApp')\n  .controller('HomeCtrl', ['$scope', '$position', function ($scope, $position) {\n    // do noting\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/identity.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',\n  'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService',\n  '$interval', '$location', '$timeout',\n  function ($scope, $stateParams, IdentityService, ngDialog,\n    FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) {\n\n    $scope.app = $stateParams.app;\n\n    $scope.currentPage = 1;\n    $scope.pageSize = 16;\n    $scope.totalPage = 1;\n    $scope.totalCount = 0;\n    $scope.identities = [];\n    // 数据自动刷新频率, 默认10s\n    var DATA_REFRESH_INTERVAL = 30;\n\n    $scope.isExpand = true;\n    $scope.searchKey = '';\n    $scope.firstExpandAll = false;\n    $scope.isTreeView = true;\n\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n    $scope.table = null;\n\n    var flowRuleDialog;\n    var flowRuleDialogScope;\n    $scope.addNewFlowRule = function (resource) {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      flowRuleDialogScope = $scope.$new(true);\n      flowRuleDialogScope.currentRule = {\n        enable: false,\n        strategy: 0,\n        grade: 1,\n        controlBehavior: 0,\n        resource: resource,\n        limitApp: 'default',\n        clusterMode: false,\n        clusterConfig: {\n            thresholdType: 0\n        },\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1]\n      };\n\n      flowRuleDialogScope.flowRuleDialog = {\n        title: '新增流控规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        saveAndContinueBtnText: '新增并继续添加',\n        showAdvanceButton: true\n      };\n      // $scope.flowRuleDialog = {\n      //     showAdvanceButton : true\n      // };\n      flowRuleDialogScope.saveRule = saveFlowRule;\n      flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue;\n      flowRuleDialogScope.onOpenAdvanceClick = function () {\n        flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false;\n      };\n      flowRuleDialogScope.onCloseAdvanceClick = function () {\n        flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true;\n      };\n\n      flowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: flowRuleDialogScope\n      });\n    };\n\n    function saveFlowRule() {\n      if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {\n        return;\n      }\n      FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {\n        if (data.code === 0) {\n          flowRuleDialog.close();\n          let url = '/dashboard/flow/' + $scope.app;\n          $location.path(url);\n        } else {\n          alert('失败!');\n        }\n      }).error((data, header, config, status) => {\n          alert('未知错误');\n      });\n    }\n\n    function saveFlowRuleAndContinue() {\n        if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {\n            return;\n        }\n      FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          flowRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n    var degradeRuleDialog;\n    var degradeRuleDialogScope;\n    $scope.addNewDegradeRule = function (resource) {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      degradeRuleDialogScope = $scope.$new(true);\n      degradeRuleDialogScope.currentRule = {\n        enable: false,\n        grade: 0,\n        strategy: 0,\n        resource: resource,\n        limitApp: 'default',\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1]\n      };\n\n      degradeRuleDialogScope.degradeRuleDialog = {\n        title: '新增降级规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        saveAndContinueBtnText: '新增并继续添加'\n      };\n      degradeRuleDialogScope.saveRule = saveDegradeRule;\n      degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue;\n\n      degradeRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/degrade-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: degradeRuleDialogScope\n      });\n    };\n\n    function saveDegradeRule() {\n        if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {\n            return;\n        }\n      DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          degradeRuleDialog.close();\n          var url = '/dashboard/degrade/' + $scope.app;\n          $location.path(url);\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n    function saveDegradeRuleAndContinue() {\n        if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {\n            return;\n        }\n      DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {\n        if (data.code == 0) {\n          degradeRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n\n      let authorityRuleDialog;\n      let authorityRuleDialogScope;\n\n      function saveAuthorityRule() {\n          let ruleEntity = authorityRuleDialogScope.currentRule;\n          if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          AuthorityRuleService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  authorityRuleDialog.close();\n                  let url = '/dashboard/authority/' + $scope.app;\n                  $location.path(url);\n              } else {\n                  alert('添加规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加规则失败：' + data.msg);\n              } else {\n                  alert(\"添加规则失败：未知错误\");\n              }\n          });\n      }\n\n      function saveAuthorityRuleAndContinue() {\n          let ruleEntity = authorityRuleDialogScope.currentRule;\n          if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          AuthorityRuleService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  authorityRuleDialog.close();\n              } else {\n                  alert('添加规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加规则失败：' + data.msg);\n              } else {\n                  alert(\"添加规则失败：未知错误\");\n              }\n          });\n      }\n\n      $scope.addNewAuthorityRule = function (resource) {\n          if (!$scope.macInputModel) {\n              return;\n          }\n          let mac = $scope.macInputModel.split(':');\n          authorityRuleDialogScope = $scope.$new(true);\n          authorityRuleDialogScope.currentRule = {\n              app: $scope.app,\n              ip: mac[0],\n              port: mac[1],\n              rule: {\n                  resource: resource,\n                  strategy: 0,\n                  limitApp: '',\n              }\n          };\n\n          authorityRuleDialogScope.authorityRuleDialog = {\n              title: '新增授权规则',\n              type: 'add',\n              confirmBtnText: '新增',\n              saveAndContinueBtnText: '新增并继续添加'\n          };\n          authorityRuleDialogScope.saveRule = saveAuthorityRule;\n          authorityRuleDialogScope.saveRuleAndContinue = saveAuthorityRuleAndContinue;\n\n          authorityRuleDialog = ngDialog.open({\n              template: '/app/views/dialog/authority-rule-dialog.html',\n              width: 680,\n              overlay: true,\n              scope: authorityRuleDialogScope\n          });\n      };\n\n      let paramFlowRuleDialog;\n      let paramFlowRuleDialogScope;\n\n      function saveParamFlowRule() {\n          let ruleEntity = paramFlowRuleDialogScope.currentRule;\n          if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          ParamFlowService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  paramFlowRuleDialog.close();\n                  let url = '/dashboard/paramFlow/' + $scope.app;\n                  $location.path(url);\n              } else {\n                  alert('添加热点规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加热点规则失败：' + data.msg);\n              } else {\n                  alert(\"添加热点规则失败：未知错误\");\n              }\n          });\n      }\n\n      function saveParamFlowRuleAndContinue() {\n          let ruleEntity = paramFlowRuleDialogScope.currentRule;\n          if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {\n              return;\n          }\n          ParamFlowService.addNewRule(ruleEntity).success((data) => {\n              if (data.success) {\n                  paramFlowRuleDialog.close();\n              } else {\n                  alert('添加热点规则失败：' + data.msg);\n              }\n          }).error((data) => {\n              if (data) {\n                  alert('添加热点规则失败：' + data.msg);\n              } else {\n                  alert(\"添加热点规则失败：未知错误\");\n              }\n          });\n      }\n\n      $scope.addNewParamFlowRule = function (resource) {\n          if (!$scope.macInputModel) {\n              return;\n          }\n          let mac = $scope.macInputModel.split(':');\n          paramFlowRuleDialogScope = $scope.$new(true);\n          paramFlowRuleDialogScope.currentRule = {\n              app: $scope.app,\n              ip: mac[0],\n              port: mac[1],\n              rule: {\n                  resource: resource,\n                  grade: 1,\n                  paramFlowItemList: [],\n                  count: 0,\n                  limitApp: 'default',\n              }\n          };\n\n          paramFlowRuleDialogScope.paramFlowRuleDialog = {\n              title: '新增热点规则',\n              type: 'add',\n              confirmBtnText: '新增',\n              saveAndContinueBtnText: '新增并继续添加',\n              supportAdvanced: false,\n              showAdvanceButton: true\n          };\n          paramFlowRuleDialogScope.saveRule = saveParamFlowRule;\n          paramFlowRuleDialogScope.saveRuleAndContinue = saveParamFlowRuleAndContinue;\n          // paramFlowRuleDialogScope.onOpenAdvanceClick = function () {\n          //     paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = false;\n          // };\n          // paramFlowRuleDialogScope.onCloseAdvanceClick = function () {\n          //     paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = true;\n          // };\n\n          paramFlowRuleDialog = ngDialog.open({\n              template: '/app/views/dialog/param-flow-rule-dialog.html',\n              width: 680,\n              overlay: true,\n              scope: paramFlowRuleDialogScope\n          });\n      };\n\n    var searchHandler;\n    $scope.searchChange = function (searchKey) {\n      $timeout.cancel(searchHandler);\n      searchHandler = $timeout(function () {\n        $scope.searchKey = searchKey;\n        $scope.isExpand = true;\n        $scope.firstExpandAll = true;\n        reInitIdentityDatas();\n        $scope.firstExpandAll = false;\n      }, 600);\n    };\n\n    $scope.initTreeTable = function () {\n      // if (!$scope.table) {\n        com_github_culmat_jsTreeTable.register(window);\n        $scope.table = window.treeTable($('#identities'));\n      // }\n    };\n\n    $scope.expandAll = function () {\n      $scope.isExpand = true;\n    };\n    $scope.collapseAll = function () {\n      $scope.isExpand = false;\n    };\n    $scope.treeView = function () {\n      $scope.isTreeView = true;\n      queryIdentities();\n    };\n    $scope.listView = function () {\n      $scope.isTreeView = false;\n      queryIdentities();\n    };\n\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code === 0) {\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    }\n\n    // Fetch all machines by current app name.\n    queryAppMachines();\n\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        reInitIdentityDatas();\n      }\n    });\n\n    $scope.$on('$destroy', function () {\n      $interval.cancel(intervalId);\n    });\n\n    var intervalId;\n    function reInitIdentityDatas() {\n      // $interval.cancel(intervalId);\n      queryIdentities();\n      // intervalId = $interval(function () {\n      //    queryIdentities();\n      // }, DATA_REFRESH_INTERVAL * 1000);\n    };\n\n    function queryIdentities() {\n      var mac = $scope.macInputModel.split(':');\n      if (mac == null || mac.length < 2) {\n        return;\n      }\n      if ($scope.isTreeView) {\n        IdentityService.fetchIdentityOfMachine(mac[0], mac[1], $scope.searchKey).success(\n          function (data) {\n            if (data.code == 0 && data.data) {\n              $scope.identities = data.data;\n              $scope.totalCount = $scope.identities.length;\n            } else {\n              $scope.identities = [];\n              $scope.totalCount = 0;\n            }\n          }\n        );\n      } else {\n        IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success(\n          function (data) {\n            if (data.code == 0 && data.data) {\n              $scope.identities = data.data;\n              $scope.totalCount = $scope.identities.length;\n            } else {\n              $scope.identities = [];\n              $scope.totalCount = 0;\n            }\n          }\n        );\n      }\n    };\n    $scope.queryIdentities = queryIdentities;\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/login.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService',\n  function ($scope, $state, $window, LoginService) {\n    // If auth, jump to the index page directly\n    if ($window.localStorage.getItem('session_sentinel_admin')) {\n      $state.go('dashboard');\n    }\n\n    $scope.login = function () {\n      if (!$scope.username) {\n        alert('请输入用户名');\n        return;\n      }\n\n      if (!$scope.password) {\n        alert('请输入密码');\n        return;\n      }\n\n      var param = {\"username\": $scope.username, \"password\": $scope.password};\n\n      LoginService.login(param).success(function (data) {\n        if (data.code == 0) {\n          $window.localStorage.setItem('session_sentinel_admin', {\n            username: data.data\n          });\n\n          $state.go('dashboard');\n        } else {\n          alert(data.msg);\n        }\n      });\n    };\n  }]\n);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/machine.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService',\n  function ($scope, $stateParams, MachineService) {\n    $scope.app = $stateParams.app;\n    $scope.propertyName = '';\n    $scope.reverse = false;\n    $scope.currentPage = 1;\n    $scope.machines = [];\n    $scope.machinesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n\n    $scope.sortBy = function (propertyName) {\n      // console.log('machine sortBy ' + propertyName);\n      $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;\n      $scope.propertyName = propertyName;\n    };\n    \n    $scope.reloadMachines = function() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          // console.log('get machines: ' + data.data[0].hostname)\n          if (data.code == 0 && data.data) {\n            $scope.machines = data.data;\n            var healthy = 0;\n            $scope.machines.forEach(function (item) {\n              if (item.healthy) {\n                  healthy++;\n              }\n              if (!item.hostname) {\n                item.hostname = '未知'\n              }\n            })\n            $scope.healthyCount = healthy;\n            $scope.machinesPageConfig.totalCount = $scope.machines.length;\n          } else {\n            $scope.machines = [];\n            $scope.healthyCount = 0;\n          }\n        }\n      );\n    };\n    \n    $scope.removeMachine = function(ip, port) {\n      if (!confirm(\"confirm to remove machine [\" + ip + \":\" + port + \"]?\")) {\n        return;\n      }\n      MachineService.removeAppMachine($scope.app, ip, port).success(\n        function(data) {\n          if (data.code == 0) {\n            $scope.reloadMachines();\n          } else {\n            alert(\"remove failed\");\n          }\n        }\n      );\n    };\n    \n    $scope.reloadMachines();\n    \n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/main.js",
    "content": "/**\n * @ngdoc function\n * @name sentinelDashboardApp.controller:MainCtrl\n * @description\n * # MainCtrl\n * Controller of the sentinelDashboardApp\n */\nangular.module('sentinelDashboardApp')\n  .controller('DashboardCtrl', ['$scope', '$position', function ($scope, $position) {\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/metric.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout',\n  function ($scope, $stateParams, MetricService, $interval, $timeout) {\n\n    $scope.endTime = new Date();\n    $scope.startTime = new Date();\n    $scope.startTime.setMinutes($scope.endTime.getMinutes() - 30);\n    $scope.startTimeFmt = formatDate($scope.startTime);\n    $scope.endTimeFmt = formatDate($scope.endTime);\n    function formatDate(date) {\n      return moment(date).format('YYYY/MM/DD HH:mm:ss');\n    }\n    $scope.changeStartTime = function (startTime) {\n      $scope.startTime = new Date(startTime);\n      $scope.startTimeFmt = formatDate(startTime);\n    };\n    $scope.changeEndTime = function (endTime) {\n      $scope.endTime = new Date(endTime);\n      $scope.endTimeFmt = formatDate(endTime);\n    };\n\n    $scope.app = $stateParams.app;\n    // 数据自动刷新频率\n    var DATA_REFRESH_INTERVAL = 1000 * 10;\n\n    $scope.servicePageConfig = {\n      pageSize: 6,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.servicesChartConfigs = [];\n\n    $scope.pageChanged = function (newPageNumber) {\n      $scope.servicePageConfig.currentPageIndex = newPageNumber;\n      reInitIdentityDatas();\n    };\n\n    var searchT;\n    $scope.searchService = function () {\n      $timeout.cancel(searchT);\n      searchT = $timeout(function () {\n        reInitIdentityDatas();\n      }, 600);\n    }\n\n    var intervalId;\n    reInitIdentityDatas();\n    function reInitIdentityDatas() {\n      $interval.cancel(intervalId);\n      queryIdentityDatas();\n      intervalId = $interval(function () {\n        queryIdentityDatas();\n      }, DATA_REFRESH_INTERVAL);\n    };\n\n    $scope.$on('$destroy', function () {\n      $interval.cancel(intervalId);\n    });\n    $scope.initAllChart = function () {\n      $.each($scope.metrics, function (idx, metric) {\n        if (idx == $scope.metrics.length - 1) {\n          return;\n        }\n        const chart = new G2.Chart({\n          container: 'chart' + idx,\n          forceFit: true,\n          width: 100,\n          height: 250,\n          padding: [10, 30, 70, 50]\n        });\n        var maxQps = 0;\n        for (var i in metric.data) {\n          var item = metric.data[i];\n          if (item.passQps > maxQps) {\n            maxQps = item.passQps;\n          }\n          if (item.blockQps > maxQps) {\n            maxQps = item.blockQps;\n          }\n        }\n        chart.source(metric.data);\n        chart.scale('timestamp', {\n          type: 'time',\n          mask: 'YYYY-MM-DD HH:mm:ss'\n        });\n        chart.scale('passQps', {\n          min: 0,\n          max: maxQps,\n          fine: true,\n          alias: '通过 QPS'\n          // max: 10\n        });\n        chart.scale('blockQps', {\n          min: 0,\n          max: maxQps,\n          fine: true,\n          alias: '拒绝 QPS',\n        });\n        chart.scale('rt', {\n          min: 0,\n          fine: true,\n        });\n        chart.axis('rt', {\n          grid: null,\n          label: null\n        });\n        chart.axis('blockQps', {\n          grid: null,\n          label: null\n        });\n\n        chart.axis('timestamp', {\n          label: {\n            textStyle: {\n              textAlign: 'center', // 文本对齐方向，可取值为： start center end\n              fill: '#404040', // 文本的颜色\n              fontSize: '11', // 文本大小\n              //textBaseline: 'top', // 文本基准线，可取 top middle bottom，默认为middle\n            },\n            autoRotate: false,\n            formatter: function (text, item, index) {\n              return text.substring(11, 11 + 5);\n            }\n          }\n        });\n        chart.legend({\n          custom: true,\n          position: 'bottom',\n          allowAllCanceled: true,\n          itemFormatter: function (val) {\n            if ('passQps' === val) {\n              return '通过 QPS';\n            }\n            if ('blockQps' === val) {\n              return '拒绝 QPS';\n            }\n            return val;\n          },\n          items: [\n            { value: 'passQps', marker: { symbol: 'hyphen', stroke: 'green', radius: 5, lineWidth: 2 } },\n            { value: 'blockQps', marker: { symbol: 'hyphen', stroke: 'blue', radius: 5, lineWidth: 2 } },\n            //{ value: 'rt', marker: {symbol: 'hyphen', stroke: 'gray', radius: 5, lineWidth: 2} },\n          ],\n          onClick: function (ev) {\n            const item = ev.item;\n            const value = item.value;\n            const checked = ev.checked;\n            const geoms = chart.getAllGeoms();\n            for (var i = 0; i < geoms.length; i++) {\n              const geom = geoms[i];\n              if (geom.getYScale().field === value) {\n                if (checked) {\n                  geom.show();\n                } else {\n                  geom.hide();\n                }\n              }\n            }\n          }\n        });\n        chart.line().position('timestamp*passQps').size(1).color('green').shape('smooth');\n        chart.line().position('timestamp*blockQps').size(1).color('blue').shape('smooth');\n        //chart.line().position('timestamp*rt').size(1).color('gray').shape('smooth');\n        G2.track(false);\n        chart.render();\n      });\n    };\n\n    $scope.metrics = [];\n    $scope.emptyObjs = [];\n    function queryIdentityDatas() {\n      var params = {\n        app: $scope.app,\n        pageIndex: $scope.servicePageConfig.currentPageIndex,\n        pageSize: $scope.servicePageConfig.pageSize,\n        desc: $scope.isDescOrder,\n        searchKey: $scope.serviceQuery\n      };\n      MetricService.queryAppSortedIdentities(params).success(function (data) {\n        $scope.metrics = [];\n        $scope.emptyObjs = [];\n        if (data.code === 0 && data.data) {\n          var metricsObj = data.data.metric;\n          var identityNames = Object.keys(metricsObj);\n          if (identityNames.length < 1) {\n            $scope.emptyServices = true;\n          } else {\n            $scope.emptyServices = false;\n          }\n          $scope.servicePageConfig.totalPage = data.data.totalPage;\n          $scope.servicePageConfig.pageSize = data.data.pageSize;\n          var totalCount = data.data.totalCount;\n          $scope.servicePageConfig.totalCount = totalCount;\n          for (i = 0; i < totalCount; i++) {\n            $scope.emptyObjs.push({});\n          }\n          $.each(identityNames, function (idx, identityName) {\n            var identityDatas = metricsObj[identityName];\n            var metrics = {};\n            metrics.resource = identityName;\n            // metrics.data = identityDatas;\n            metrics.data = fillZeros(identityDatas);\n            metrics.shortData = lastOfArray(identityDatas, 6);\n            $scope.metrics.push(metrics);\n          });\n          // push an empty element in the last, for ng-init reasons.\n          $scope.metrics.push([]);\n        } else {\n          $scope.emptyServices = true;\n          console.log(data.msg);\n        }\n      });\n    };\n    function fillZeros(metricData) {\n      if (!metricData || metricData.length == 0) {\n        return [];\n      }\n      var filledData = [];\n      filledData.push(metricData[0]);\n      var lastTime = metricData[0].timestamp / 1000;\n      for (var i = 1; i < metricData.length; i++) {\n        var curTime = metricData[i].timestamp / 1000;\n        if (curTime > lastTime + 1) {\n          for (var j = lastTime + 1; j < curTime; j++) {\n            filledData.push({\n                \"timestamp\": j * 1000,\n                \"passQps\": 0,\n                \"blockQps\": 0,\n                \"successQps\": 0,\n                \"exception\": 0,\n                \"rt\": 0,\n                \"count\": 0\n            })\n          }\n        }\n        filledData.push(metricData[i]);\n        lastTime = curTime;\n      }\n      return filledData;\n    }\n    function lastOfArray(arr, n) {\n      if (!arr.length) {\n        return [];\n      }\n      var rs = [];\n      for (i = 0; i < n && i < arr.length; i++) {\n        rs.push(arr[arr.length - 1 - i]);\n      }\n      return rs;\n    }\n\n    $scope.isDescOrder = true;\n    $scope.setDescOrder = function () {\n      $scope.isDescOrder = true;\n      reInitIdentityDatas();\n    }\n    $scope.setAscOrder = function () {\n      $scope.isDescOrder = false;\n      reInitIdentityDatas();\n    }\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/param_flow.js",
    "content": "/**\n * Parameter flow control controller.\n * \n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog',\n  'MachineService',\n  function ($scope, $stateParams, ParamFlowService, ngDialog,\n    MachineService) {\n    const UNSUPPORTED_CODE = 4041;\n    $scope.app = $stateParams.app;\n    $scope.curExItem = {};\n\n    $scope.paramItemClassTypeList = [\n      'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte'\n    ];\n\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n      function updateSingleParamItem(arr, v, t, c) {\n          for (let i = 0; i < arr.length; i++) {\n              if (arr[i].object === v && arr[i].classType === t) {\n                  arr[i].count = c;\n                  return;\n              }\n          }\n          arr.push({object: v, classType: t, count: c});\n      }\n\n      function removeSingleParamItem(arr, v, t) {\n          for (let i = 0; i < arr.length; i++) {\n              if (arr[i].object === v && arr[i].classType === t) {\n                  arr.splice(i, 1);\n                  break;\n              }\n          }\n      }\n\n      function isNumberClass(classType) {\n        return classType === 'int' || classType === 'double' ||\n            classType === 'float' || classType === 'long' || classType === 'short';\n      }\n\n      function isByteClass(classType) {\n          return classType === 'byte';\n      }\n\n      function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n      }\n\n      function notGoodNumber(num) {\n          return num === undefined || num === '' || isNaN(num);\n      }\n\n      function notGoodNumberBetweenExclusive(num, l ,r) {\n          return num === undefined || num === '' || isNaN(num) || num < l || num > r;\n      }\n\n      $scope.notValidParamItem = (curExItem) => {\n        if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {\n          return true;\n        }\n        if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {\n          return true;\n        }\n        return curExItem.object === undefined || curExItem.classType === undefined ||\n            notNumberAtLeastZero(curExItem.count);\n      };\n\n      $scope.addParamItem = () => {\n          updateSingleParamItem($scope.currentRule.rule.paramFlowItemList,\n              $scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count);\n          let oldItem = $scope.curExItem;\n          $scope.curExItem = {classType: oldItem.classType};\n      };\n\n      $scope.removeParamItem = (v, t) => {\n          removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t);\n      };\n\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      let mac = $scope.macInputModel.split(':');\n      ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1])\n        .success(function (data) {\n          if (data.code === 0 && data.data) {\n            $scope.loadError = undefined;\n            $scope.rules = data.data;\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n            if (data.code === UNSUPPORTED_CODE) {\n              $scope.loadError = {message: \"机器 \" + mac[0] + \":\" + mac[1] + \" 的 Sentinel 客户端版本不支持热点参数限流功能，请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。\"}\n            } else {\n              $scope.loadError = {message: data.msg}\n            }\n          }\n        })\n        .error((data, header, config, status) => {\n          $scope.loadError = {message: \"未知错误\"}\n        });\n    }\n    $scope.getMachineRules = getMachineRules;\n    getMachineRules();\n\n    var paramFlowRuleDialog;\n\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.paramFlowRuleDialog = {\n        title: '编辑热点规则',\n        type: 'edit',\n        confirmBtnText: '保存',\n        supportAdvanced: true,\n        showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0\n      };\n      paramFlowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/param-flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n      $scope.curExItem = {};\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n        rule: {\n          grade: 1,\n          paramFlowItemList: [],\n          count: 0,\n          limitApp: 'default',\n          clusterMode: false,\n          clusterConfig: {\n            thresholdType: 0\n          }\n        }\n      };\n      $scope.paramFlowRuleDialog = {\n        title: '新增热点规则',\n        type: 'add',\n        confirmBtnText: '新增',\n        showAdvanceButton: true,\n      };\n      paramFlowRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/param-flow-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n      $scope.curExItem = {};\n    };\n\n      $scope.onOpenAdvanceClick = function () {\n          $scope.paramFlowRuleDialog.showAdvanceButton = false;\n      };\n      $scope.onCloseAdvanceClick = function () {\n          $scope.paramFlowRuleDialog.showAdvanceButton = true;\n      };\n\n    $scope.saveRule = function () {\n      if (!ParamFlowService.checkRuleValid($scope.currentRule.rule)) {\n        return;\n      }\n      if ($scope.paramFlowRuleDialog.type === 'add') {\n        addNewRuleAndPush($scope.currentRule);\n      } else if ($scope.paramFlowRuleDialog.type === 'edit') {\n        saveRuleAndPush($scope.currentRule, true);\n      }\n    };\n\n    function addNewRuleAndPush(rule) {\n      ParamFlowService.addNewRule(rule).success((data) => {\n        if (data.success) {\n          getMachineRules();\n          paramFlowRuleDialog.close();\n        } else {\n          alert('添加规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('添加规则失败：' + data.msg);\n        } else {\n          alert(\"添加规则失败：未知错误\");\n        }\n      });\n    }\n\n    function saveRuleAndPush(rule, edit) {\n      ParamFlowService.saveRule(rule).success(function (data) {\n        if (data.success) {\n          alert(\"修改规则成功\");\n          getMachineRules();\n          if (edit) {\n            paramFlowRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('修改规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('修改规则失败：' + data.msg);\n        } else {\n          alert(\"修改规则失败：未知错误\");\n        }\n      });\n    }\n\n    function deleteRuleAndPush(entity) {\n      if (entity.id === undefined || isNaN(entity.id)) {\n        alert('规则 ID 不合法！');\n        return;\n      }\n      ParamFlowService.deleteRule(entity).success((data) => {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert('删除规则失败：' + data.msg);\n        }\n      }).error((data) => {\n        if (data) {\n          alert('删除规则失败：' + data.msg);\n        } else {\n          alert(\"删除规则失败：未知错误\");\n        }\n      });\n    };\n\n    var confirmDialog;\n    $scope.deleteRule = function (ruleEntity) {\n      $scope.currentRule = ruleEntity;\n      console.log('deleting: ' + ruleEntity);\n      $scope.confirmDialog = {\n        title: '删除热点规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下热点参数限流规则',\n        attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx +\n            ', 限流模式: ' + (ruleEntity.rule.grade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.rule.count,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type === 'delete_rule') {\n        deleteRuleAndPush($scope.currentRule);\n      } else {\n        console.error('error');\n      }\n    };\n\n    queryAppMachines();\n\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/system.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.controller('SystemCtl', ['$scope', '$stateParams', 'SystemService', 'ngDialog', 'MachineService',\n  function ($scope, $stateParams, SystemService,\n    ngDialog, MachineService) {\n    //初始化\n    $scope.app = $stateParams.app;\n    $scope.rulesPageConfig = {\n      pageSize: 10,\n      currentPageIndex: 1,\n      totalPage: 1,\n      totalCount: 0,\n    };\n    $scope.macsInputConfig = {\n      searchField: ['text', 'value'],\n      persist: true,\n      create: false,\n      maxItems: 1,\n      render: {\n        item: function (data, escape) {\n          return '<div>' + escape(data.text) + '</div>';\n        }\n      },\n      onChange: function (value, oldValue) {\n        $scope.macInputModel = value;\n      }\n    };\n\n    getMachineRules();\n    function getMachineRules() {\n      if (!$scope.macInputModel) {\n        return;\n      }\n      var mac = $scope.macInputModel.split(':');\n      SystemService.queryMachineRules($scope.app, mac[0], mac[1]).success(\n        function (data) {\n          if (data.code == 0 && data.data) {\n            $scope.rules = data.data;\n            $.each($scope.rules, function (idx, rule) {\n              // rule.orginEnable = rule.enable;\n              if (rule.avgLoad >= 0) {\n                rule.grade = 0;\n              } else if (rule.avgRt >= 0) {\n                rule.grade = 1;\n              } else if (rule.maxThread >= 0) {\n                rule.grade = 2;\n              } else if (rule.qps >= 0) {\n                rule.grade = 3;\n              }\n            });\n            $scope.rulesPageConfig.totalCount = $scope.rules.length;\n          } else {\n            $scope.rules = [];\n            $scope.rulesPageConfig.totalCount = 0;\n          }\n        });\n    };\n    $scope.getMachineRules = getMachineRules;\n    var systemRuleDialog;\n    $scope.editRule = function (rule) {\n      $scope.currentRule = angular.copy(rule);\n      $scope.systemRuleDialog = {\n        title: '编辑系统保护规则',\n        type: 'edit',\n        confirmBtnText: '保存'\n      };\n      systemRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/system-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.addNewRule = function () {\n      var mac = $scope.macInputModel.split(':');\n      $scope.currentRule = {\n        grade: 0,\n        app: $scope.app,\n        ip: mac[0],\n        port: mac[1],\n      };\n      $scope.systemRuleDialog = {\n        title: '新增系统保护规则',\n        type: 'add',\n        confirmBtnText: '新增'\n      };\n      systemRuleDialog = ngDialog.open({\n        template: '/app/views/dialog/system-rule-dialog.html',\n        width: 680,\n        overlay: true,\n        scope: $scope\n      });\n    };\n\n    $scope.saveRule = function () {\n      if ($scope.systemRuleDialog.type == 'add') {\n        addNewRule($scope.currentRule);\n      } else if ($scope.systemRuleDialog.type == 'edit') {\n        saveRule($scope.currentRule, true);\n      }\n    }\n\n\n    var confirmDialog;\n    $scope.deleteRule = function (rule) {\n      $scope.currentRule = rule;\n      var ruleTypeDesc = '';\n      var ruleTypeCount = null;\n      if (rule.avgLoad != -1) {\n        ruleTypeDesc = 'LOAD';\n        ruleTypeCount = rule.avgLoad;\n      } else if (rule.avgRt != -1) {\n        ruleTypeDesc = 'RT';\n        ruleTypeCount = rule.avgRt;\n      } else if (rule.maxThread != -1) {\n        ruleTypeDesc = '线程数';\n        ruleTypeCount = rule.maxThread;\n      } else if (rule.qps != -1) {\n        ruleTypeDesc = 'QPS';\n        ruleTypeCount = rule.qps;\n      }\n\n      $scope.confirmDialog = {\n        title: '删除系统保护规则',\n        type: 'delete_rule',\n        attentionTitle: '请确认是否删除如下系统保护规则',\n        attention: '阈值类型: ' + ruleTypeDesc + ', 阈值: ' + ruleTypeCount,\n        confirmBtnText: '删除',\n      };\n      confirmDialog = ngDialog.open({\n        template: '/app/views/dialog/confirm-dialog.html',\n        scope: $scope,\n        overlay: true\n      });\n    };\n\n\n    $scope.confirm = function () {\n      if ($scope.confirmDialog.type == 'delete_rule') {\n        deleteRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'enable_rule') {\n        //     $scope.currentRule.enable = true;\n        //     saveRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'disable_rule') {\n        //     $scope.currentRule.enable = false;\n        //     saveRule($scope.currentRule);\n        // } else if ($scope.confirmDialog.type == 'enable_all') {\n        //     enableAll($scope.app);\n        // } else if ($scope.confirmDialog.type == 'disable_all') {\n        //     disableAll($scope.app);\n      } else {\n        console.error('error');\n      }\n    };\n\n    function deleteRule(rule) {\n      SystemService.deleteRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          confirmDialog.close();\n        } else {\n          alert\n        }\n      });\n    };\n\n    function addNewRule(rule) {\n      SystemService.newRule(rule).success(function (data) {\n        if (data.code == 0) {\n          getMachineRules();\n          systemRuleDialog.close();\n        } else {\n          alert('失败!');\n        }\n      });\n    };\n\n    function saveRule(rule, edit) {\n      SystemService.saveRule(rule).success(function (data) {\n        if (data.code == 0) {\n          // if (rule.enable) {\n          //    rule.orginEnable = true;\n          // } else {\n          //    rule.orginEnable = false;\n          // }\n          getMachineRules();\n          if (edit) {\n            systemRuleDialog.close();\n          } else {\n            confirmDialog.close();\n          }\n        } else {\n          alert('失败!');\n        }\n      });\n    }\n    queryAppMachines();\n    function queryAppMachines() {\n      MachineService.getAppMachines($scope.app).success(\n        function (data) {\n          if (data.code == 0) {\n            // $scope.machines = data.data;\n            if (data.data) {\n              $scope.machines = [];\n              $scope.macsInputOptions = [];\n              data.data.forEach(function (item) {\n                if (item.healthy) {\n                  $scope.macsInputOptions.push({\n                    text: item.ip + ':' + item.port,\n                    value: item.ip + ':' + item.port\n                  });\n                }\n              });\n            }\n            if ($scope.macsInputOptions.length > 0) {\n              $scope.macInputModel = $scope.macsInputOptions[0].value;\n            }\n          } else {\n            $scope.macsInputOptions = [];\n          }\n        }\n      );\n    };\n    $scope.$watch('macInputModel', function () {\n      if ($scope.macInputModel) {\n        getMachineRules();\n      }\n    });\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/header/header.html",
    "content": "<div class=\"header\">\n  <nav class=\"navbar navbar-fixed-top navbar-inverse\" style=\"\" role=\"navigation\">\n    <div class=\"navbar-brand\">\n      <span style=\"color: #fff;font-size: 26px;\">Sentinel 控制台</span>\n    </div>\n    <ul class=\"nav navbar-nav navbar-right\">\n      <li>\n        <a href=\"javascript:void(0);\" ng-click=\"logout()\"\n           style=\"margin: 3px 15px 0 0;\"><span class=\"glyphicon glyphicon-log-out\"></span>&nbsp;注销</a>\n      </li>\n    </ul>\n  </nav>\n  <!-- end nav -->\n  <sidebar></sidebar>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/header/header.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description\n * # adminPosHeader\n */\nangular.module('sentinelDashboardApp')\n  .directive('header', ['AuthService', function () {\n    return {\n      templateUrl: 'app/scripts/directives/header/header.html',\n      restrict: 'E',\n      replace: true,\n      controller: function ($scope, $state, $window, AuthService) {\n        if (!$window.localStorage.getItem('session_sentinel_admin')) {\n          $state.go('login');\n        }\n\n        $scope.logout = function () {\n          AuthService.logout().success(function (data) {\n            if (data.code == 0) {\n              $window.localStorage.removeItem(\"session_sentinel_admin\");\n              $state.go('login');\n            } else {\n              alert('logout error');\n            }\n          });\n        }\n      }\n    }\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.html",
    "content": "<li class=\"sidebar-search\">\n  <div class=\"input-group custom-search-form\">\n    <input type=\"text\" class=\"form-control\" ng-model=\"searchApp\" placeholder=\"Search...\">\n    <span class=\"input-group-btn\">\n      <button class=\"btn btn-default\" type=\"button\">\n        <i class=\"fa fa-search\"></i>\n      </button>\n    </span>\n  </div>\n</li>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description\n * # adminPosHeader\n */\n\nangular.module('sentinelDashboardApp')\n  .directive('sidebarSearch', function () {\n    return {\n      templateUrl: 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.html',\n      restrict: 'E',\n      replace: true,\n      scope: {\n      },\n      controller: function ($scope) {\n        $scope.selectedMenu = 'home';\n      }\n    }\n  });\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html",
    "content": "<div class=\"navbar-default sidebar\" role=\"navigation\" style=\"overflow-y: auto;\">\n  <div class=\"sidebar-nav navbar-collapse\">\n    <ul class=\"nav in\" id=\"side-menu\">\n      <li class=\"sidebar-search\">\n        <div class=\"input-group\" style=\"\">\n          <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"应用名\" ng-model=\"searchApp\">\n          <span class=\"input-group-btn\">\n            <button class=\"btn btn-secondary btn-default-inverse\" type=\"button\">搜索</button>\n          </span>\n        </div>\n      </li>\n      <li ui-sref-active=\"active\">\n        <a ui-sref=\"dashboard.home\" style=\"font-size:16px;\">\n          <span class=\"glyphicon glyphicon-dashboard\"></span>\n          &nbsp;&nbsp;首页</a>\n      </li>\n\n      <li ng-class=\"{active: true}\" ng-repeat=\"entry in apps | filter: { app: searchApp }\">{{dropDown}}\n        <a href=\"javascript:void(0);\" ng-click=\"click($event)\" collapse=\"{{collpaseall == 1}}\" style=\"font-size: 16px;word-break: break-word;\">\n          &nbsp;{{entry.app}}\n          <span class=\"fa arrow\"></span>\n          <span class=\"arrow\">({{entry.heathyCount}}/{{entry.machines.length}})</span>\n        </a>\n\n        <!--<ul class=\"nav nav-second-level\" collapse=\"{{entry.active}}\" style=\"display: none;\">-->\n        <ul class=\"nav nav-second-level\" ng-show=\"entry.active\">\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.metric({app: entry.app})\">\n              <i class=\"fa fa-bar-chart\"></i>&nbsp;&nbsp;实时监控</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.identity({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-list-alt\"></i>&nbsp;&nbsp;簇点链路</a>\n          </li>\n\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.flow({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-filter\"></i>&nbsp;&nbsp;流控规则</a>\n          </li>\n          <!--<li ui-sref-active=\"active\">-->\n            <!--<a ui-sref=\"dashboard.flow({app: entry.app})\">-->\n              <!--<i class=\"glyphicon glyphicon-filter\"></i>&nbsp;&nbsp;流控规则 V1</a>-->\n          <!--</li>-->\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.degrade({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-flash\"></i>&nbsp;&nbsp;降级规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.paramFlow({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-fire\"></i>&nbsp;&nbsp;热点规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.system({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-lock\"></i>&nbsp;&nbsp;系统规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.authority({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-check\"></i>&nbsp;&nbsp;授权规则</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.clusterAppServerList({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-cloud\"></i>&nbsp;&nbsp;集群流控</a>\n          </li>\n          <li ui-sref-active=\"active\">\n            <a ui-sref=\"dashboard.machine({app: entry.app})\">\n              <i class=\"glyphicon glyphicon-th-list\"></i>&nbsp;&nbsp;机器列表</a>\n          </li>\n\n        </ul>\n        <!-- /.nav-second-level -->\n      </li>\n    </ul>\n  </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js",
    "content": "/**\n * @ngdoc directive\n * @name izzyposWebApp.directive:adminPosHeader\n * @description # adminPosHeader\n */\n\nangular.module('sentinelDashboardApp')\n  .directive('sidebar', ['$location', '$stateParams', 'AppService', function () {\n    return {\n      templateUrl: 'app/scripts/directives/sidebar/sidebar.html',\n      restrict: 'E',\n      replace: true,\n      scope: {\n      },\n      controller: function ($scope, $stateParams, $location, AppService) {\n        $scope.app = $stateParams.app;\n        $scope.collapseVar = 0;\n\n        // app\n        AppService.getApps().success(\n          function (data) {\n            if (data.code === 0) {\n              let initHashApp = $location.path().split('/')[3];\n              $scope.apps = data.data;\n              $scope.apps = $scope.apps.map(function (item) {\n                if (item.app === initHashApp) {\n                  item.active = true;\n                }\n                var heathyCount = 0;\n                for (var i in item.machines) {\n                  if (item.machines[i].healthy) {\n                      heathyCount++;\n                  }\n                }\n                item.heathyCount = heathyCount;\n                if (item.shown) {\n                  return item;\n                }\n              });\n            }\n          }\n        );\n\n        // toggle side bar\n        $scope.click = function ($event) {\n          let entry = angular.element($event.target).scope().entry;\n          entry.active = !entry.active;// toggle this clicked app bar\n\n          $scope.apps.forEach(function (item) {// collapse other app bars\n            if (item != entry) {\n              item.active = false;\n            }\n          });\n        };\n\n        /**\n         * @deprecated\n         */\n        $scope.addSearchApp = function () {\n          let findApp = false;\n          for (let i = 0; i < $scope.apps.length; i++) {\n            if ($scope.apps[i].app === $scope.searchApp) {\n              findApp = true;\n              break;\n            }\n          }\n          if (!findApp) {\n            $scope.apps.push({ app: $scope.searchApp });\n          }\n        };\n      }\n    };\n  }]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/filters/filters.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.filter('range', [function () {\n  return function (input, length) {\n    if (isNaN(length) || length <= 0) {\n      return [];\n    }\n\n    input = [];\n    for (var index = 1; index <= length; index++) {\n      input.push(index);\n    }\n\n    return input;\n  };\n  \n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/libs/treeTable.js",
    "content": "var com_github_culmat_jsTreeTable =  (function(){\n\n\tfunction depthFirst(tree, func, childrenAttr) {\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\tfunction i_depthFirst(node) {\n\t\t\tif (node[childrenAttr]) {\n\t\t\t\t$.each(node[childrenAttr], function(i, child) {\n\t\t\t\t\ti_depthFirst(child)\n\t\t\t\t})\n\t\t\t}\n\t\t\tfunc(node)\n\t\t}\n\t\t$.each(tree, function(i, root) {\n\t\t\ti_depthFirst(root)\n\t\t})\n\t\treturn tree\n\t}\n\t\n\t/*\n\t * make a deep copy of the object\n\t */\n\tfunction copy(data){\n\t\treturn JSON.parse(JSON.stringify(data))\n\t}\n\t\n\tfunction makeTree (data, idAttr, refAttr, childrenAttr) {\n\t\tvar data_tmp = data\n\t\tidAttr = idAttr || 'id'\n\t\trefAttr = refAttr || 'parent'\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\n\t\tvar byName = []\n\t\t$.each(data_tmp, function(i, entry) {\n\t\t\tbyName[entry[idAttr]] = entry\n\t\t})\n\t\tvar tree = []\n\t\t$.each(data_tmp, function(i, entry) {\n\t\t\tvar parents = entry[refAttr]\n\t\t\tif(!$.isArray(parents)){\n\t\t\t\tparents = [parents]\n\t\t\t}\n\t\t\tif(parents.length == 0){\n\t\t\t\ttree.push(entry)\n\t\t\t} else {\n\t\t\t\tvar inTree = false;\n\t\t\t\t$.each(parents, function(i,parentID){\n\t\t\t\t\tvar parent = byName[parentID]\n\t\t\t\t\tif (parent) {\n\t\t\t\t\t\tif (!parent[childrenAttr]) {\n\t\t\t\t\t\t\tparent[childrenAttr] = []\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif($.inArray(entry, parent[childrenAttr])< 0)\n\t\t\t\t\t\t\tparent[childrenAttr].push(entry)\n\t\t\t\t\t\tinTree = true\n\t\t\t\t\t} \n\t\t\t\t})\n\t\t\t\tif(!inTree){\n\t\t\t\t\ttree.push(entry)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\treturn tree\n\t}\n\t\n\tfunction renderTree(tree, childrenAttr, idAttr, attrs, renderer, tableAttributes) {\n\t\tchildrenAttr = childrenAttr || 'children'\n\t\tidAttr = idAttr || 'id'\n\t\ttableAttributes = tableAttributes || {}\n\t\tvar maxLevel = 0;\n\t\tvar ret = []\n\t\n\t\tvar table = $(\"<table>\")\n\t\t$.each(tableAttributes, function(key, value){\n\t\t\tif(key == 'class' && value != 'jsTT') {\n\t\t\t\ttable.addClass(value)\n\t\t\t} else {\n\t\t\t\ttable.attr(key, value)\t\t\t\n\t\t\t}\n\t\t})\n\t\tvar thead = $(\"<thead>\")\n\t\tvar tr = $(\"<tr>\")\n\t\tvar tbody = $(\"<tbody>\")\n\t\n\t\ttable.append(thead)\n\t\tthead.append(tr)\n\t\ttable.append(tbody)\n\t\tif (attrs) {\n\t\t\t$.each(attrs, function(attr, desc) {\n\t\t\t\t$(tr).append($('<th>' + desc + '</th>'))\n\t\t\t})\n\t\t} else {\n\t\t\t$(tr).append($('<th>' + idAttr + '</th>'))\n\t\t\t$.each(tree[0], function(key, value) {\n\t\t\t\tif (key != childrenAttr && key != idAttr)\n\t\t\t\t\t$(tr).append($('<th>' + key + '</th>'))\n\t\t\t})\n\t\t}\n\t\n\t\tfunction render(node, parent) {\n\t\t\tvar tr = $(\"<tr>\")\n\t\t\t$(tr).attr('data-tt-id', node[idAttr])\n\t\t\t$(tr).attr('data-tt-level', node['data-tt-level'])\n\t\t\tif(!node[childrenAttr] || node[childrenAttr].length == 0)\n\t\t\t\t$(tr).attr('data-tt-isleaf', true)\n\t\t\telse\n\t\t\t\t$(tr).attr('data-tt-isnode', true)\n\t\t\tif (parent) {\n\t\t\t\t$(tr).attr('data-tt-parent-id', parent[idAttr])\n\t\t\t}\n\t\t\tif (renderer) {\n\t\t\t\trenderer($(tr), node)\n\t\t\t}else if (attrs) {\n\t\t\t\t$.each(attrs, function(attr, desc) {\n\t\t\t\t\t$(tr).append($('<td>' + node[attr] + '</td>'))\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\t$(tr).append($('<td>' + node[idAttr] + '</td>'))\n\t\t\t\t$.each(node, function(key, value) {\n\t\t\t\t\tif (key != childrenAttr && key != idAttr && key != 'data-tt-level')\n\t\t\t\t\t\t$(tr).append($('<td>' + value + '</td>'))\n\t\t\t\t})\n\t\t\t}\n\t\t\ttbody.append(tr)\n\t\t}\n\t\n\t\tfunction i_renderTree(subTree, childrenAttr, level, parent) {\n\t\t\tmaxLevel = Math.max(maxLevel, level)\n\t\t\t$.each(subTree, function(i, node) {\n\t\t\t\tnode['data-tt-level'] = level\n\t\t\t\trender(node, parent)\n\t\t\t\tif (node[childrenAttr]) {\n\t\t\t\t\t$.each(node[childrenAttr], function(i, child) {\n\t\t\t\t\t\ti_renderTree([ child ], childrenAttr, level + 1, node)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\ti_renderTree(tree, childrenAttr, 1)\n\t\tif (tree[0])\n\t\t\ttree[0].maxLevel = maxLevel\n\t\treturn table\n\t}\n\t\n\tfunction attr2attr(nodes, attrs){\n\t\t$.each(nodes,  function(i, node) {\n\t\t\t$.each(attrs,  function(j, at) {\n\t\t\t\tnode[at] = $(node).attr(at)\n\t\t\t})\t\n\t\t})\n\t\treturn nodes\n\t}\n\t\n\tfunction treeTable(table){\n\t\ttable.addClass('jsTT')\n\t\ttable.expandLevel = function (n) {\n\t\t\t$(\"tr[data-tt-level]\", table).each(function(index) {\n\t\t\t\tvar level = parseInt($(this).attr('data-tt-level'))\n\t\t\t\tif (level > n-1) {\n\t\t\t\t\tthis.trCollapse(true)\n\t\t\t\t} else if (level == n-1){\n\t\t\t\t\tthis.trExpand(true)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tfunction getLevel(node){\n\t\t\tvar level = node.attr('data-tt-level')\n\t\t\tif(level != undefined ) return parseInt(level)\n\t\t\tvar parentID = node.attr('data-tt-parent-id')\n\t\t\tif( parentID == undefined){\n\t\t\t\treturn 0\n\t\t\t} else {\n\t\t\t\treturn getLevel($('tr[data-tt-id=\"'+parentID+'\"]', table).first()) + 1\n\t\t\t} \n\t\t}\n\t\t$(\"tr[data-tt-id]\", table).each(function(i,node){\n\t\t\tnode = $(node)\n\t\t\tnode.attr('data-tt-level', getLevel(node)) \n\t\t})\n\t\tvar dat = $(\"tr[data-tt-level]\", table).get()\n\t\t$.each(dat,  function(j, d) {\n\t\t\td.trChildrenVisible = true\n\t\t\td.trChildren = []\n\t\t})\t\n\t\tdat  = attr2attr(dat, ['data-tt-id', 'data-tt-parent-id'])\n\t\tdat = makeTree(dat, 'data-tt-id', 'data-tt-parent-id', 'trChildren')\n\t\t\n\t\tvar imgExpand = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=\"\n\t\tvar imgCollapse = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC\"\n\t\t$(\"tr[data-tt-level]\", table).each(function(index, tr) {\n\t\t\tvar level = $(tr).attr('data-tt-level')\n\t\t\tvar td = $(\"td\",tr).first()\n\t\t\tif(tr.trChildren.length>0){\n\t\t\t\ttd.prepend($('<img id=\"state\" style=\"cursor:pointer\" src=\"'+imgCollapse+'\"/>'))\t\t\t\n\t\t\t}  else {\n\t\t\t\ttd.prepend($('<span style=\"padding-left:16px;\" /></span>'))\n\t\t\t}\n\t\t\ttd.prepend($('<span style=\"padding-left:'+(15*parseInt(level-1))+'px;\" /></span>'))\n\t\t\t// td.css('white-space','nowrap')\n\t\t\ttr.trExpand = function(changeState){\n\t\t\t\tif(this.trChildren.length < 1) return\n\t\t\t\tif(changeState) {\n\t\t\t\t\tthis.trChildrenVisible = true\n\t\t\t\t\t$('#state', this).get(0).src= imgCollapse\n\t\t\t\t} \n\t\t\t\tvar doit = changeState || this.trChildrenVisible\n\t\t\t\t$.each(this.trChildren, function(i, ctr) {\n\t\t\t\t\tif(doit) $(ctr).css('display', 'table-row')\n\t\t\t\t\tctr.trExpand()\n\t\t\t\t})\n\t\t\t}\n\t\t\ttr.trCollapse = function(changeState){\n\t\t\t\tif(this.trChildren.length < 1) return\n\t\t\t\tif(changeState) {\n\t\t\t\t\tthis.trChildrenVisible = false\n\t\t\t\t\t$('#state', this).get(0).src= imgExpand\n\t\t\t\t}\n\t\t\t\t$.each(this.trChildren, function(i, ctr) {\n\t\t\t\t\t$(ctr).css('display', 'none')\n\t\t\t\t\tctr.trCollapse()\n\t\t\t\t})\n\t\t\t}\n\t\t\t$(tr).click(function() {\n\t\t\t\tthis.trChildrenVisible ? this.trCollapse(true) : this.trExpand(true)\n\t\t\t})\n\t\t})\n\t\treturn table\n\t}\n\t\n\tfunction appendTreetable(tree, options) {\n\t\tfunction inALine(nodes) {\n\t\t\tvar tr = $('<tr>')\n\t\t\t$.each(nodes, function(i, node){\n\t\t\t\ttr.append($('<td style=\"padding-right: 20px;\">').append(node))\n\t\t\t})\n\t\t\treturn $('<table border=\"0\"/>').append(tr)\n\t\t\t\n\t\t}\n\t\toptions = options || {}\n\t\toptions.idAttr = (options.idAttr || 'id')\n\t\toptions.childrenAttr = (options.childrenAttr || 'children')\n\t\tvar controls = (options.controls || [])\n\t\n\t\tif (!options.mountPoint)\n\t\t\toptions.mountPoint = $('body')\n\t\t\n\t\tif (options.depthFirst)\n\t\t\tdepthFirst(tree, options.depthFirst, options.childrenAttr)\n\t\tvar rendered = renderTree(tree, options.childrenAttr, options.idAttr,\n\t\t\t\toptions.renderedAttr, options.renderer, options.tableAttributes)\n\t\n\t\ttreeTable(rendered)\n\t\tif (options.replaceContent) {\n\t\t\toptions.mountPoint.html('')\n\t\t}\n\t\tvar initialExpandLevel = options.initialExpandLevel ? parseInt(options.initialExpandLevel) : -1\n\t\tinitialExpandLevel = Math.min(initialExpandLevel, tree[0].maxLevel)\n\t\trendered.expandLevel(initialExpandLevel)\n\t\tif(options.slider){\n\t\t\tvar slider = $('<div style=\"margin-right: 15px;\">')\n\t\t\tslider.width('200px')\n\t\t\tslider.slider({\n\t\t\t\tmin : 1,\n\t\t\t\tmax : tree[0].maxLevel,\n\t\t\t\trange : \"min\",\n\t\t\t\tvalue : initialExpandLevel,\n\t\t\t\tslide : function(event, ui) {\n\t\t\t\t\trendered.expandLevel(ui.value)\n\t\t\t\t}\n\t\t\t})\n\t\t\tcontrols = [slider].concat(options.controls)\n\t\t}\n\t\t\n\t    if(controls.length >0){\n\t    \toptions.mountPoint.append(inALine(controls))    \t\n\t    }\n\t\toptions.mountPoint.append(rendered)\n\t\treturn rendered\n\t}\n\t\n\treturn {\n\t\tdepthFirst : depthFirst,\n\t\tmakeTree : makeTree,\n\t\trenderTree : renderTree,\n\t\tattr2attr : attr2attr,\n\t\ttreeTable : treeTable,\n\t\tappendTreetable : appendTreetable,\n\t\tjsTreeTable : '1.0',\n\t\tregister : function(target){\n\t\t\t$.each(this, function(key, value){ if(key != 'register') target[key] = value})\n\t\t}\n\t}\n})();\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/appservice.js",
    "content": "\nvar app = angular.module('sentinelDashboardApp');\n\napp.service('AppService', ['$http', function ($http) {\n  this.getApps = function () {\n    return $http({\n      // url: 'app/mock_infos',\n      url: 'app/briefinfos.json',\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/auth_service.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('AuthService', ['$http', function ($http) {\n  this.login = function (param) {\n    return $http({\n      url: '/auth/login',\n      params: param,\n      method: 'POST'\n    })\n  }\n\n  this.logout = function () {\n    return $http({\n      url: '/auth/logout',\n      method: 'POST'\n    })\n  }\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/authority_service.js",
    "content": "/**\n * Authority rule service.\n */\nangular.module('sentinelDashboardApp').service('AuthorityRuleService', ['$http', function ($http) {\n    this.queryMachineRules = function(app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/authority/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.addNewRule = function(rule) {\n        return $http({\n            url: '/authority/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (entity) {\n        return $http({\n            url: '/authority/rule/' + entity.id,\n            data: entity,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (entity) {\n        return $http({\n            url: '/authority/rule/' + entity.id,\n            method: 'DELETE'\n        });\n    };\n\n    this.checkRuleValid = function checkRuleValid(rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.limitApp === undefined || rule.limitApp === '') {\n            alert('流控针对应用不能为空');\n            return false;\n        }\n        if (rule.strategy === undefined) {\n            alert('必须选择黑白名单模式');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/cluster_state_service.js",
    "content": "/**\n * Cluster state control service.\n *\n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) {\n\n    this.fetchClusterUniversalStateSingle = function(app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/cluster/state_single',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterUniversalStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterServerStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/server_state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.fetchClusterClientStateOfApp = function(app) {\n        return $http({\n            url: '/cluster/client_state/' + app,\n            method: 'GET'\n        });\n    };\n\n    this.modifyClusterConfig = function(config) {\n        return $http({\n            url: '/cluster/config/modify_single',\n            data: config,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterFullAssignOfApp = function(app, clusterMap) {\n        return $http({\n            url: '/cluster/assign/all_server/' + app,\n            data: clusterMap,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterSingleServerAssignOfApp = function(app, request) {\n        return $http({\n            url: '/cluster/assign/single_server/' + app,\n            data: request,\n            method: 'POST'\n        });\n    };\n\n    this.applyClusterServerBatchUnbind = function(app, machineSet) {\n        return $http({\n            url: '/cluster/assign/unbind_server/' + app,\n            data: machineSet,\n            method: 'POST'\n        });\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/degradeservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('DegradeService', ['$http', function ($http) {\n  this.queryMachineRules = function (app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: 'degrade/rules.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.newRule = function (rule) {\n    var param = {\n      id: rule.id,\n      resource: rule.resource,\n      limitApp: rule.limitApp,\n      count: rule.count,\n      timeWindow: rule.timeWindow,\n      grade: rule.grade,\n      app: rule.app,\n      ip: rule.ip,\n      port: rule.port\n    };\n    return $http({\n      url: '/degrade/new.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.saveRule = function (rule) {\n    var param = {\n      id: rule.id,\n      resource: rule.resource,\n      limitApp: rule.limitApp,\n      grade: rule.grade,\n      count: rule.count,\n      timeWindow: rule.timeWindow,\n    };\n    return $http({\n      url: '/degrade/save.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.deleteRule = function (rule) {\n    var param = {\n      id: rule.id,\n      app: rule.app\n    };\n    return $http({\n      url: '/degrade/delete.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.checkRuleValid = function (rule) {\n      if (rule.resource === undefined || rule.resource === '') {\n          alert('资源名称不能为空');\n          return false;\n      }\n      if (rule.grade === undefined || rule.grade < 0) {\n          alert('未知的降级策略');\n          return false;\n      }\n      if (rule.count === undefined || rule.count === '' || rule.count < 0) {\n          alert('降级阈值不能为空或小于 0');\n          return false;\n      }\n      if (rule.timeWindow === undefined || rule.timeWindow === '' || rule.timeWindow <= 0) {\n          alert('降级时间窗口必须大于 0');\n          return false;\n      }\n      // 异常比率类型.\n      if (rule.grade == 1 && rule.count > 1) {\n          alert('异常比率超出范围：[0.0 - 1.0]');\n          return false;\n      }\n      return true;\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/flow_service_v1.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('FlowServiceV1', ['$http', function ($http) {\n    this.queryMachineRules = function (app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/v1/flow/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.newRule = function (rule) {\n        var param = {\n            resource: rule.resource,\n            limitApp: rule.limitApp,\n            grade: rule.grade,\n            count: rule.count,\n            strategy: rule.strategy,\n            refResource: rule.refResource,\n            controlBehavior: rule.controlBehavior,\n            warmUpPeriodSec: rule.warmUpPeriodSec,\n            maxQueueingTimeMs: rule.maxQueueingTimeMs,\n            app: rule.app,\n            ip: rule.ip,\n            port: rule.port\n        };\n\n        return $http({\n            url: '/v1/flow/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (rule) {\n        var param = {\n            id: rule.id,\n            resource: rule.resource,\n            limitApp: rule.limitApp,\n            grade: rule.grade,\n            count: rule.count,\n            strategy: rule.strategy,\n            refResource: rule.refResource,\n            controlBehavior: rule.controlBehavior,\n            warmUpPeriodSec: rule.warmUpPeriodSec,\n            maxQueueingTimeMs: rule.maxQueueingTimeMs,\n        };\n\n        return $http({\n            url: '/v1/flow/save.json',\n            params: param,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (rule) {\n        var param = {\n            id: rule.id,\n            app: rule.app\n        };\n\n        return $http({\n            url: '/v1/flow/delete.json',\n            params: param,\n            method: 'DELETE'\n        });\n    };\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notNumberGreaterThanZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num <= 0;\n    }\n\n    this.checkRuleValid = function (rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.count === undefined || rule.count < 0) {\n            alert('限流阈值必须大于等于 0');\n            return false;\n        }\n        if (rule.strategy === undefined || rule.strategy < 0) {\n            alert('无效的流控模式');\n            return false;\n        }\n        if (rule.strategy == 1 || rule.strategy == 2) {\n            if (rule.refResource === undefined || rule.refResource == '') {\n                alert('请填写关联资源或入口');\n                return false;\n            }\n        }\n        if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {\n            alert('无效的流控整形方式');\n            return false;\n        }\n        if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {\n            alert('预热时长必须大于 0');\n            return false;\n        }\n        if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {\n            alert('排队超时时间必须大于 0');\n            return false;\n        }\n        if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {\n            alert('集群限流配置不正确');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/flow_service_v2.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('FlowServiceV2', ['$http', function ($http) {\n    this.queryMachineRules = function (app, ip, port) {\n        var param = {\n            app: app,\n            ip: ip,\n            port: port\n        };\n        return $http({\n            url: '/v2/flow/rules',\n            params: param,\n            method: 'GET'\n        });\n    };\n\n    this.newRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule',\n            data: rule,\n            method: 'POST'\n        });\n    };\n\n    this.saveRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule/' + rule.id,\n            data: rule,\n            method: 'PUT'\n        });\n    };\n\n    this.deleteRule = function (rule) {\n        return $http({\n            url: '/v2/flow/rule/' + rule.id,\n            method: 'DELETE'\n        });\n    };\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notNumberGreaterThanZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num <= 0;\n    }\n\n    this.checkRuleValid = function (rule) {\n        if (rule.resource === undefined || rule.resource === '') {\n            alert('资源名称不能为空');\n            return false;\n        }\n        if (rule.count === undefined || rule.count < 0) {\n            alert('限流阈值必须大于等于 0');\n            return false;\n        }\n        if (rule.strategy === undefined || rule.strategy < 0) {\n            alert('无效的流控模式');\n            return false;\n        }\n        if (rule.strategy == 1 || rule.strategy == 2) {\n            if (rule.refResource === undefined || rule.refResource == '') {\n                alert('请填写关联资源或入口');\n                return false;\n            }\n        }\n        if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {\n            alert('无效的流控整形方式');\n            return false;\n        }\n        if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {\n            alert('预热时长必须大于 0');\n            return false;\n        }\n        if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {\n            alert('排队超时时间必须大于 0');\n            return false;\n        }\n        if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {\n            alert('集群限流配置不正确');\n            return false;\n        }\n        return true;\n    };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/identityservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('IdentityService', ['$http', function ($http) {\n\n  this.fetchIdentityOfMachine = function (ip, port, searchKey) {\n    var param = {\n      ip: ip,\n      port: port,\n      searchKey: searchKey\n    };\n    return $http({\n      url: 'resource/machineResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n  this.fetchClusterNodeOfMachine = function (ip, port, searchKey) {\n    var param = {\n      ip: ip,\n      port: port,\n      type: 'cluster',\n      searchKey: searchKey\n    };\n    return $http({\n      url: 'resource/machineResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/machineservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) {\n  this.getAppMachines = function (app) {\n    return $http({\n      url: 'app/' + app + '/machines.json',\n      method: 'GET'\n    });\n  };\n  this.removeAppMachine = function (app, ip, port) {\n    return $http({\n      url: 'app/' + app + '/machine/remove.json',\n      method: 'POST',\n      headers: {\n        \"Content-type\": 'application/x-www-form-urlencoded; charset=UTF-8'\n      },\n      data: $httpParamSerializerJQLike({\n        ip: ip,\n        port: port\n      })\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/metricservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('MetricService', ['$http', function ($http) {\n\n  this.queryAppSortedIdentities = function (params) {\n    return $http({\n      url: '/metric/queryTopResourceMetric.json',\n      params: params,\n      method: 'GET'\n    });\n  };\n\n  this.queryByAppAndIdentity = function (params) {\n    return $http({\n      url: '/metric/queryByAppAndResource.json',\n      params: params,\n      method: 'GET'\n    });\n  };\n\n  this.queryByMachineAndIdentity = function (ip, port, identity, startTime, endTime) {\n    var param = {\n      ip: ip,\n      port: port,\n      identity: identity,\n      startTime: startTime.getTime(),\n      endTime: endTime.getTime()\n    };\n\n    return $http({\n      url: '/metric/queryByAppAndResource.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/param_flow_service.js",
    "content": "/**\n * Parameter flow control service.\n * \n * @author Eric Zhao\n */\nangular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) {\n  this.queryMachineRules = function(app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: '/paramFlow/rules',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.addNewRule = function(rule) {\n    return $http({\n      url: '/paramFlow/rule',\n      data: rule,\n      method: 'POST'\n    });\n  };\n\n  this.saveRule = function (entity) {\n    return $http({\n      url: '/paramFlow/rule/' + entity.id,\n      data: entity,\n      method: 'PUT'\n    });\n  };\n\n  this.deleteRule = function (entity) {\n    return $http({\n      url: '/paramFlow/rule/' + entity.id,\n      method: 'DELETE'\n    });\n  };\n\n    function isNumberClass(classType) {\n        return classType === 'int' || classType === 'double' ||\n            classType === 'float' || classType === 'long' || classType === 'short';\n    }\n\n    function isByteClass(classType) {\n        return classType === 'byte';\n    }\n\n    function notNumberAtLeastZero(num) {\n        return num === undefined || num === '' || isNaN(num) || num < 0;\n    }\n\n    function notGoodNumber(num) {\n        return num === undefined || num === '' || isNaN(num);\n    }\n\n    function notGoodNumberBetweenExclusive(num, l ,r) {\n        return num === undefined || num === '' || isNaN(num) || num < l || num > r;\n    }\n\n    function notValidParamItem(curExItem) {\n        if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {\n            return true;\n        }\n        if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {\n            return true;\n        }\n        return curExItem.object === undefined || curExItem.classType === undefined ||\n            notNumberAtLeastZero(curExItem.count);\n    }\n\n  this.checkRuleValid = function (rule) {\n      if (!rule.resource || rule.resource === '') {\n          alert('资源名称不能为空');\n          return false;\n      }\n      if (rule.grade != 1) {\n          alert('未知的限流模式');\n          return false;\n      }\n      if (rule.count < 0) {\n          alert('限流阈值必须大于等于 0');\n          return false;\n      }\n      if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) {\n          alert('热点参数索引必须大于等于 0');\n          return false;\n      }\n      if (rule.paramFlowItemList !== undefined) {\n          for (var i = 0; i < rule.paramFlowItemList.length; i++) {\n              var item = rule.paramFlowItemList[i];\n              if (notValidParamItem(item)) {\n                  alert('热点参数例外项不合法，请检查值和类型是否正确：参数为 ' + item.object + ', 类型为 ' +\n                      item.classType + ', 限流阈值为 ' + item.count);\n                  return false;\n              }\n          }\n      }\n      return true;\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/systemservice.js",
    "content": "var app = angular.module('sentinelDashboardApp');\n\napp.service('SystemService', ['$http', function ($http) {\n  this.queryMachineRules = function (app, ip, port) {\n    var param = {\n      app: app,\n      ip: ip,\n      port: port\n    };\n    return $http({\n      url: 'system/rules.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.newRule = function (rule) {\n    var param = {\n      app: rule.app,\n      ip: rule.ip,\n      port: rule.port\n    };\n    if (rule.grade == 0) {// avgLoad\n      param.avgLoad = rule.avgLoad;\n    } else if (rule.grade == 1) {// avgRt\n      param.avgRt = rule.avgRt;\n    } else if (rule.grade == 2) {// maxThread\n      param.maxThread = rule.maxThread;\n    } else if (rule.grade == 3) {// qps\n      param.qps = rule.qps;\n    }\n\n    return $http({\n      url: '/system/new.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.saveRule = function (rule) {\n    var param = {\n      id: rule.id,\n    };\n    if (rule.grade == 0) {// avgLoad\n      param.avgLoad = rule.avgLoad;\n    } else if (rule.grade == 1) {// avgRt\n      param.avgRt = rule.avgRt;\n    } else if (rule.grade == 2) {// maxThread\n      param.maxThread = rule.maxThread;\n    } else if (rule.grade == 3) {// qps\n      param.qps = rule.qps;\n    }\n    return $http({\n      url: '/system/save.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n\n  this.deleteRule = function (rule) {\n    var param = {\n      id: rule.id,\n      app: rule.app\n    };\n\n    return $http({\n      url: '/system/delete.json',\n      params: param,\n      method: 'GET'\n    });\n  };\n}]);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/main.css",
    "content": ".browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\nbody {\n    padding: 0;\n}\n\n/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.header,\n.marketing,\n.footer {\n    padding-left: 15px;\n    padding-right: 15px;\n}\n\n\n/* Custom page header */\n\n.header {\n    border-bottom: 1px solid #e5e5e5;\n    margin-bottom: 10px;\n}\n\n\n/* Make the masthead heading the same height as the navigation */\n\n.header h3 {\n    margin-top: 0;\n    margin-bottom: 0;\n    line-height: 40px;\n    padding-bottom: 19px;\n}\n\n\n/* Custom page footer */\n\n.footer {\n    padding-top: 19px;\n    color: #777;\n    border-top: 1px solid #e5e5e5;\n}\n\n.container-narrow > hr {\n    margin: 30px 0;\n}\n\n\n/* Main marketing message and sign up button */\n\n.jumbotron {\n    text-align: center;\n    border-bottom: 1px solid #e5e5e5;\n}\n\n.jumbotron .btn {\n    font-size: 21px;\n    padding: 14px 24px;\n}\n\n\n/* Supporting marketing content */\n\n.marketing {\n    margin: 40px 0;\n}\n\n.marketing p + h4 {\n    margin-top: 28px;\n}\n\n\n/* Responsive: Portrait tablets and up */\n\n@media screen and (min-width: 768px) {\n    .container {\n        width: inherit;\n        margin-left: 60px;\n        margin-right: 5px;\n    }\n    /* Remove the padding we set earlier */\n    .header,\n    .marketing,\n    .footer {\n        padding-left: 0;\n        padding-right: 0;\n    }\n    /* Space out the masthead */\n    .header {\n        margin-bottom: 30px;\n    }\n    /* Remove the bottom border on the jumbotron for visual effect */\n    .jumbotron {\n        border-bottom: 0;\n    }\n}\n\n.navbar-inverse {\n    background-color: #1d9d74;\n    border-color: #1b926c;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n    color: #b0ddce;\n    font-size: 15px;\n}\n\n.navbar-inverse .navbar-nav>.open>a,\n.navbar-inverse .navbar-nav>.open>a:focus,\n.navbar-inverse .navbar-nav>.open>a:hover {\n    background-color: #1b926c;\n}\n\n@media (min-width: 900px) {\n    .navbar-left {\n        float: left !important;\n    }\n    .navbar-right {\n        float: right !important;\n        margin-right: 0%;\n    }\n    .navbar-right ~ .navbar-right {\n        margin-right: 0;\n    }\n}\n\n.dropdown-menu {\n    min-width: 100px !important;\n}\n\n.nav-sidebar li.active a {\n    background: #DDD;\n}\n\n.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {\n    background: #1d9d74;\n    /*background: #d9d9d9;*/\n    color: white;\n}\n\n.broadcast-message,\n.broadcast-message-preview {\n    padding: 10px;\n    text-align: center;\n    background: #555;\n    color: #BBB;\n    margin-top: 50px;\n}\n\n.card {\n    position: relative;\n    border: 1px solid #d9d9d9;\n    border-radius: 3px;\n    color: #666;\n    background-color: #fff;\n    width: 100%;\n    border-radius: 5px;\n}\n\n.card .card-header {\n    padding: 9px 0;\n    height: 40px;\n    background: #555;\n    color: #fff;\n    text-align: center;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.card .card-body {\n    padding: 12px 10px;\n}\n\n.card .card-footer {\n    height: 20px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .detail-brand {\n    float: left;\n    width: 30%;\n    line-height: 98px;\n    font-size: 30px;\n    text-align: center;\n    color: white;\n}\n\n.card .default {\n    background: #1d9d74;\n}\n\n.card .info {\n    background: #6EBEE7;\n}\n\n.card .warn {\n    background: #ED7F54;\n}\n\n.card .danger {\n    background: #6583BE;\n}\n\n.card .detail .text-default {\n    color: #1d9d74;\n}\n\n.card .detail .text-info {\n    color: #6EBEE7;\n}\n\n.card .detail .text-warn {\n    color: #ED7F54;\n}\n\n.card .detail .text-danger {\n    color: #6583BE;\n}\n\n.card .detail {\n    float: right;\n    width: 70%;\n    line-height: 98px;\n    text-align: center;\n}\n\n.card .detail .text {\n    font-size: 12px;\n}\n\n.card .detail .number {\n    font-size: 30px;\n    font-weight: 500;\n}\n\n.h100 {\n    height: 100px;\n}\n\n.inline {\n    display: inline;\n}\n\n.separator {\n    height: 1px;\n    background-color: #e5e5e5;\n    margin-top: 10px;\n}\n\n.card > .card-body > table > thead > tr > td,\n.card > .card-body > table > tbody > tr > td {\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.card > .card-body > table > thead > tr > td {\n    font-weight: 500;\n    font-size: 13px;\n    text-align: center;\n}\n\n.card > .card-body > table > thead > tr > td > span {\n    font-weight: 500;\n    font-size: 10px;\n}\n\n.card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    text-align: center;\n}\n\n.card > .card-body > table > tbody > tr > td > a {\n    color: #666;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td,\n.thumbnails > .card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    color: #777;\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) {\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) {\n    font-weight: 700;\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1),\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) {\n    text-align: left;\n}\n\n.tools-header {\n    background: whitesmoke;\n    padding: 9px 0;\n    height: 40px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.tools-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.tools-header .brand > a {\n    color: #666;\n}\n\n.tools-header > button,\n.tools-header > select,\n.tools-header > a {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 25px;\n    padding: 0 10px;\n    line-height: 25px;\n    color: #666;\n}\n\n.tools-header .paged {\n    margin-right: 0px;\n}\n\n.btn {\n    height: 32px;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn:focus,\n.btn:active {\n    outline: none !important;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n\n.btn.btn-danger-tag {\n    color: #ffffff;\n    background-color: #d9534f;\n    border-color: #d43f3a;\n    line-height: 1px;\n    font-size: 11px;\n    padding: 4px 4px;\n}\n\n.btn.btn-danger {\n    color: #333;\n    background-color: #fff;\n    border-color: #ccc;\n}\n\n.btn.btn-danger:hover,\n.btn.btn-danger:focus,\n.btn.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.form-control {\n    height : 32px;\n}\n\n.form-control:focus {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.input-label:before {\n    display: inline-block;\n    content: \"*\";\n    color: #f44336;\n    font-family: SimSun;\n    font-size: 12px;\n    -webkit-transform: TranslateX(-10px);\n    -ms-transform: TranslateX(-10px);\n    transform: TranslateX(-10px);\n}\n\n.label.label-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.badge-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.bootstrap-tagsinput {\n    background-color: #fff;\n    border: 1px solid #ccc;\n    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n    display: inline-block;\n    padding: 4px 6px;\n    color: #555;\n    vertical-align: middle;\n    border-radius: 4px;\n    /* max-width: 100%; */\n    width: 85%;\n    height: 100px;\n    line-height: 20px;\n    cursor: text;\n}\n\n.bootstrap-tagsinput > .dropdown-menu {\n    min-width: 40px;\n    font-size: 12px;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    background-color: #1d9d74;\n    background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74));\n    background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%);\n    filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);\n    background-repeat: repeat-x;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    color: #fff;\n    text-decoration: none;\n    background-color: #1d9d74;\n    outline: 0;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus {\n    color: white;\n    text-decoration: none;\n    outline: 0;\n    background-color: #1d9d74;\n}\n\n.inputs-header {\n    padding: 9px 0;\n    height: 50px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.inputs-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.inputs-header .brand > a {\n    color: #666;\n}\n\n.inputs-header > input {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n}\n\n.inputs-header > a {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 5 5px;\n}\n\n.inputs-header > select {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n    height: 25px;\n    font-size: 12px;\n}\n\n.witdh-150 {\n    max-width: 150px;\n}\n\n.witdh-200 {\n    max-width: 200px;\n}\n\n.width-200 {\n    max-width: 200px;\n}\n\n.witdh-300 {\n    max-width: 300px;\n}\n\n.width-300 {\n    max-width: 300px;\n}\n\n.card.highlight {\n    border-color: #d9534f;\n}\n\n.card .pagination-footer {\n    height: 40px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .pagination-footer .tools {\n    font-size: 12px;\n    margin: 11px 0;\n    float: right;\n    display: inline;\n    margin-right: 20px;\n}\n\n.card > .pagination-footer > .tools > span > input {\n    height: 25px;\n    max-width: 50px;\n    display: inline;\n}\n\n.pagination {\n    display: inline-block;\n    padding-left: 0;\n    margin: 8px 0;\n    float: right;\n    border-radius: 4px;\n}\n\n\n.pagination > a {\n    margin-right: 5px;\n    height: 28px;\n    width: 28px;\n    padding: 5px 0px;\n}\n\n.pagination > .btn.active {\n        color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n\n\n\n.datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td,\n.timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td   {\n    padding: 5px 3px;\n}\n\n.datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn,\n.timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn  {\n    border: 1px solid #FFFDFD;\n}\n\n.datepicker > .table > thead > tr > td > .btn-default:hover,\n.datepicker > .table > thead > tr > td > .btn-default:focus,\n.datepicker > .table > thead > tr > td > .btn-default:active,\n.datepicker > .table > tbody > tr > td > .btn-default:hover,\n.datepicker > .table > tbody > tr > td > .btn-default:focus,\n.datepicker > .table > tbody > tr > td > .btn-default:active,\n.timepicker > .table > thead > tr > td > .btn-default:hover,\n.timepicker > .table > thead > tr > td > .btn-default:focus,\n.timepicker > .table > thead > tr > td > .btn-default:active,\n.timepicker > .table > tbody > tr > td > .btn-default:hover,\n.timepicker > .table > tbody > tr > td > .btn-default:focus,\n.timepicker > .table > tbody > tr > td > .btn-default:active  {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a,\n.timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a {\n    height: 25px;\n    width: 25px;\n    padding: 3px 0px;\n}\n\n.datepicker > .table > tbody > tr:first-child > td > a {\n    padding: 4px 0px;\n}\n\n.datepicker > .table > thead > tr > td > a.btn.active, \n.datepicker > .table > tbody > tr > td > a.btn.active,\n.timepicker > .table > thead > tr > td > a.btn.active, \n.timepicker > .table > tbody > tr > td > a.btn.active {\n/*    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;*/\n        color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n    box-shadow: inset 0 0px 0px rgba(0,0,0,0.125);\n}\n\n.datepicker > .table > thead > tr > td:not(:first-child):last-child > a,\n.timepicker > .table > thead > tr > td:not(:first-child):last-child > a  {\n    height: 25px;\n    width: 50px;\n    padding: 5px 0px;\n}\n\n.datepicker > .table > tbody > tr > td > a, \n.timepicker > .table > tbody > tr > td > a {\n    margin-left: 8px;\n}\n\n\n.selectize-input-200 > .selectize-input {\n    min-width: 250px;\n}\n\n.highlight-border {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}.browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\nbody {\n    padding: 0;\n}\n\n\n/* Everything but the jumbotron gets side spacing for mobile first views */\n\n.header,\n.marketing,\n.footer {\n    padding-left: 15px;\n    padding-right: 15px;\n}\n\n\n/* Custom page header */\n\n.header {\n    border-bottom: 1px solid #e5e5e5;\n    margin-bottom: 10px;\n}\n\n\n/* Make the masthead heading the same height as the navigation */\n\n.header h3 {\n    margin-top: 0;\n    margin-bottom: 0;\n    line-height: 40px;\n    padding-bottom: 19px;\n}\n\n\n/* Custom page footer */\n\n.footer {\n    padding-top: 19px;\n    color: #777;\n    border-top: 1px solid #e5e5e5;\n}\n\n.container-narrow > hr {\n    margin: 30px 0;\n}\n\n\n/* Main marketing message and sign up button */\n\n.jumbotron {\n    text-align: center;\n    border-bottom: 1px solid #e5e5e5;\n}\n\n.jumbotron .btn {\n    font-size: 21px;\n    padding: 14px 24px;\n}\n\n\n/* Supporting marketing content */\n\n.marketing {\n    margin: 40px 0;\n}\n\n.marketing p + h4 {\n    margin-top: 28px;\n}\n\n\n/* Responsive: Portrait tablets and up */\n\n@media screen and (min-width: 768px) {\n    .container {\n        width: inherit;\n        margin-left: 60px;\n        margin-right: 5px;\n    }\n    /* Remove the padding we set earlier */\n    .header,\n    .marketing,\n    .footer {\n        padding-left: 0;\n        padding-right: 0;\n    }\n    /* Space out the masthead */\n    .header {\n        margin-bottom: 30px;\n    }\n    /* Remove the bottom border on the jumbotron for visual effect */\n    .jumbotron {\n        border-bottom: 0;\n    }\n}\n\n.navbar-inverse {\n    background-color: #1d9d74;\n    border-color: #1b926c;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n    color: #b0ddce;\n    font-size: 15px;\n}\n\n.navbar-inverse .navbar-nav>.open>a,\n.navbar-inverse .navbar-nav>.open>a:focus,\n.navbar-inverse .navbar-nav>.open>a:hover {\n    background-color: #1b926c;\n}\n\n@media (min-width: 900px) {\n    .navbar-left {\n        float: left !important;\n    }\n    .navbar-right {\n        float: right !important;\n        margin-right: 0%;\n    }\n    .navbar-right ~ .navbar-right {\n        margin-right: 0;\n    }\n}\n\n.dropdown-menu {\n    min-width: 100px !important;\n}\n\n.nav-sidebar li.active a {\n    background: #DDD;\n}\n\n.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {\n    background: #1d9d74;\n    /*background: #d9d9d9;*/\n    color: white;\n}\n\n.broadcast-message,\n.broadcast-message-preview {\n    padding: 10px;\n    text-align: center;\n    background: #555;\n    color: #BBB;\n    margin-top: 50px;\n}\n\n.card {\n    position: relative;\n    border: 1px solid #d9d9d9;\n    border-radius: 3px;\n    color: #666;\n    background-color: #fff;\n    width: 100%;\n    border-radius: 5px;\n}\n\n.card .card-header {\n    padding: 9px 0;\n    height: 40px;\n    background: #555;\n    color: #fff;\n    text-align: center;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.card .card-body {\n    padding: 12px 10px;\n}\n\n.card .card-footer {\n    height: 20px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .detail-brand {\n    float: left;\n    width: 30%;\n    line-height: 98px;\n    font-size: 30px;\n    text-align: center;\n    color: white;\n}\n\n.card .default {\n    background: #1d9d74;\n}\n\n.card .info {\n    background: #6EBEE7;\n}\n\n.card .warn {\n    background: #ED7F54;\n}\n\n.card .danger {\n    background: #6583BE;\n}\n\n.card .detail .text-default {\n    color: #1d9d74;\n}\n\n.card .detail .text-info {\n    color: #6EBEE7;\n}\n\n.card .detail .text-warn {\n    color: #ED7F54;\n}\n\n.card .detail .text-danger {\n    color: #6583BE;\n}\n\n.card .detail {\n    float: right;\n    width: 70%;\n    line-height: 98px;\n    text-align: center;\n}\n\n.card .detail .text {\n    font-size: 12px;\n}\n\n.card .detail .number {\n    font-size: 30px;\n    font-weight: 500;\n}\n\n.h100 {\n    height: 100px;\n}\n\n.inline {\n    display: inline;\n}\n\n.separator {\n    height: 1px;\n    background-color: #e5e5e5;\n    margin-top: 10px;\n}\n\n.card > .card-body > table > thead > tr > td,\n.card > .card-body > table > tbody > tr > td {\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.card > .card-body > table > thead > tr > td {\n    font-weight: 500;\n    font-size: 13px;\n    text-align: center;\n}\n\n.card > .card-body > table > thead > tr > td > span {\n    font-weight: 500;\n    font-size: 10px;\n}\n\n.card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    text-align: center;\n}\n\n.card > .card-body > table > tbody > tr > td > a {\n    color: #666;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td,\n.thumbnails > .card > .card-body > table > tbody > tr > td {\n    font-size: 12px;\n    color: #777;\n    word-wrap: break-word;\n    word-break: break-all;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) {\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) {\n    font-weight: 700;\n    text-align: center;\n}\n\n.thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1),\n.thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) {\n    text-align: left;\n}\n\n.tools-header {\n    background: whitesmoke;\n    padding: 9px 0;\n    height: 40px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.tools-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.tools-header .brand > a {\n    color: #666;\n}\n\n.tools-header > button,\n.tools-header > select,\n.tools-header > a {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 25px;\n    padding: 0 10px;\n    line-height: 25px;\n    color: #666;\n}\n\n.tools-header .paged {\n    margin-right: 0px;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.btn:focus,\n.btn:active {\n    outline: none !important;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn-default-inverse {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn-default-inverse:hover,\n.btn-default-inverse:focus,\n.btn-default:active {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.btn.btn-danger-tag {\n    color: #ffffff;\n    background-color: #d9534f;\n    border-color: #d43f3a;\n    line-height: 1px;\n    font-size: 11px;\n    padding: 4px 4px;\n}\n\n.btn.btn-danger {\n    color: #333;\n    background-color: #fff;\n    border-color: #ccc;\n}\n\n.btn.btn-danger:hover,\n.btn.btn-danger:focus,\n.btn.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.form-control {\n    height : 32px;\n}\n\n.form-control:focus {\n    border-color: #1d9d74;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.input-label:before {\n    display: inline-block;\n    content: \"*\";\n    color: #f44336;\n    font-family: SimSun;\n    font-size: 12px;\n    -webkit-transform: TranslateX(-10px);\n    -ms-transform: TranslateX(-10px);\n    transform: TranslateX(-10px);\n}\n\n.label.label-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.badge-main {\n    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;\n}\n\n.bootstrap-tagsinput {\n    background-color: #fff;\n    border: 1px solid #ccc;\n    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n    display: inline-block;\n    padding: 4px 6px;\n    color: #555;\n    vertical-align: middle;\n    border-radius: 4px;\n    /* max-width: 100%; */\n    width: 85%;\n    height: 100px;\n    line-height: 20px;\n    cursor: text;\n}\n\n.bootstrap-tagsinput > .dropdown-menu {\n    min-width: 40px;\n    font-size: 12px;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    background-color: #1d9d74;\n    background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%);\n    background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74));\n    background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%);\n    filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);\n    background-repeat: repeat-x;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover {\n    color: #fff;\n    text-decoration: none;\n    background-color: #1d9d74;\n    outline: 0;\n}\n\n.bootstrap-tagsinput > .dropdown-menu>.active>a,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:hover,\n.bootstrap-tagsinput > .dropdown-menu>.active>a:focus {\n    color: white;\n    text-decoration: none;\n    outline: 0;\n    background-color: #1d9d74;\n}\n\n.inputs-header {\n    padding: 9px 0;\n    height: 50px;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.inputs-header .brand {\n    font-size: 13px;\n    margin: 2px 10px;\n    font-weight: 700;\n    float: left;\n}\n\n.inputs-header .brand > a {\n    color: #666;\n}\n\n.inputs-header > input {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n}\n\n.inputs-header > a {\n    float: right;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 5 5px;\n}\n\n.inputs-header > select {\n    float: right;\n    max-width: 80px;\n    margin: 1px 10px;\n    height: 30px;\n    padding: 0 10px;\n    color: #666;\n    height: 25px;\n    font-size: 12px;\n}\n\n.witdh-150 {\n    max-width: 150px;\n}\n\n.witdh-200 {\n    max-width: 200px;\n}\n\n.card.highlight {\n    border-color: #d9534f;\n}\n\n.card .pagination-footer {\n    height: 40px;\n    font-size: 10px;\n    color: #777;\n    margin-top: -15px;\n    margin-bottom: 5px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.card .pagination-footer .tools {\n    font-size: 12px;\n    margin: 11px 0;\n    float: right;\n    display: inline;\n    margin-right: 20px;\n}\n\n.card > .pagination-footer > .tools > span > input {\n    height: 25px;\n    max-width: 50px;\n    display: inline;\n}\n\n.pagination {\n    display: inline-block;\n    padding-left: 0;\n    margin: 8px 0;\n    float: right;\n    border-radius: 4px;\n}\n\n\n.pagination > a {\n    margin-right: 5px;\n    height: 28px;\n    width: 28px;\n    padding: 5px 0px;\n}\n\n.pagination > .btn.active {\n        color: #ffffff;\n    background-color: #449d44;\n    border-color: #449d44;\n}\n\n\n\n\n.datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td,\n.timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td   {\n    padding: 5px 3px;\n}\n\n.datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn,\n.timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn  {\n    border: 1px solid #FFFDFD;\n}\n\n.datepicker > .table > thead > tr > td > .btn-default:hover,\n.datepicker > .table > thead > tr > td > .btn-default:focus,\n.datepicker > .table > thead > tr > td > .btn-default:active,\n.datepicker > .table > tbody > tr > td > .btn-default:hover,\n.datepicker > .table > tbody > tr > td > .btn-default:focus,\n.datepicker > .table > tbody > tr > td > .btn-default:active,\n.timepicker > .table > thead > tr > td > .btn-default:hover,\n.timepicker > .table > thead > tr > td > .btn-default:focus,\n.timepicker > .table > thead > tr > td > .btn-default:active,\n.timepicker > .table > tbody > tr > td > .btn-default:hover,\n.timepicker > .table > tbody > tr > td > .btn-default:focus,\n.timepicker > .table > tbody > tr > td > .btn-default:active  {\n    color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n}\n\n.datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a,\n.timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a {\n    height: 25px;\n    width: 25px;\n    padding: 3px 0px;\n}\n\n.datepicker > .table > tbody > tr:first-child > td > a {\n    padding: 4px 0px;\n}\n\n.datepicker > .table > thead > tr > td > a.btn.active, \n.datepicker > .table > tbody > tr > td > a.btn.active,\n.timepicker > .table > thead > tr > td > a.btn.active, \n.timepicker > .table > tbody > tr > td > a.btn.active {\n/*    color: #ffffff;\n    background-color: #1d9d74;\n    border-color: #1d9d74;*/\n        color: #1d9d74;\n    border-color: #1d9d74;\n    background: white;\n    box-shadow: inset 0 0px 0px rgba(0,0,0,0.125);\n}\n\n.datepicker > .table > thead > tr > td:not(:first-child):last-child > a,\n.timepicker > .table > thead > tr > td:not(:first-child):last-child > a  {\n    height: 25px;\n    width: 50px;\n    padding: 5px 0px;\n}\n\n.datepicker > .table > tbody > tr > td > a, \n.timepicker > .table > tbody > tr > td > a {\n    margin-left: 8px;\n}\n\n\n.selectize-input-200 > .selectize-input {\n    min-width: 250px;\n}\n\n.highlight-border {\n    border-color: #1d9d74;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n\n.sortorder:after {\n  content: '\\25b2';   \n}\n.sortorder.reverse:after {\n  content: '\\25bc';   \n}\n\n\n\n.input-control select {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  appearance: none;\n  position: relative;\n  border: 1px #d9d9d9 solid;\n  width: 100%;\n  height: 100%;\n  padding: .3125rem;\n  z-index: 0;\n}\n\n.navbar-inverse {\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.sidebar {\n    z-index: 1;\n    width: 220px;\n    /*position: fixed;*/\n    top: 0;\n    left: 0;\n    height: 100%;\n}\n\n#page-wrapper {\n    position: inherit;\n    margin: 70px 0 0 220px;\n    padding: 12px 30px;\n    border-left: 0px solid #e7e7e7;\n}\n\n.sidebar .sidebar-nav.navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n    background-color: #F5F5F5;\n    position: relative;\n    color: black;\n    width: 100%;\n    padding: 0;\n    margin: 0;\n    list-style: none inside none;\n}\n\n.sidebar a {\n    color: #555;\n}\n\n.sidebar ul li:hover {\n    color:red;\n}\n\n.form-control {\n    border-radius: 8px;\n}\n\n.form-control:focus {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}\n\n.highlight-border {\n    border-color: #337ab7;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1);\n}.browsehappy {\n    margin: 0.2em 0;\n    background: #ccc;\n    color: #000;\n    padding: 0.2em 0;\n}\n\n.btn.btn-main {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn-default-inverse {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.btn-default-inverse:hover,\n.btn-default-inverse:focus,\n.btn-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.btn-danger-inverse {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.btn-danger-inverse:hover,\n.btn-danger-inverse:focus,\n.btn-danger:active {\n    color: #d9534f;\n    border-color: #d9534f;\n    background: white;\n}\n\n.btn-tab-active,\n.btn-tab-active:hover,\n.btn-tab-active:focus, \n.btn-tab-default:hover,\n.btn-tab-default:focus,\n.btn-tab-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n    font-weight: 600;\n}\n.btn-tab-default {\n    color: #777;\n    background: white;\n    font-weight: 600;\n}\n\n.pagination > .btn.active {\n    color: #ffffff;\n    background-color: #337ab7;\n    border-color: #337ab7;\n}\n\n.btn-default:hover, .btn-default:focus, .btn-default:active {\n    color: #337ab7;\n    border-color: #337ab7;\n    background: white;\n}\n\n.bootstrap-switch.bootstrap-switch-on {\n    border-color: #337ab7;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n    color: #fff;\n    background: #337ab7;\n}\n\n.selectize-input-200 > .selectize-input {\n    min-width: 200px;\n    border-color: #337ab7;\n}\n\n.btn-outline-primary {\n    color: #007bff;\n    background-color: transparent;\n    background-image: none;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n    color: #fff;\n    background-color: #007bff;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n    color: #007bff;\n    background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n    color: #fff;\n    background-color: #007bff;\n    border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n    color: #6c757d;\n    background-color: transparent;\n    background-image: none;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n    color: #fff;\n    background-color: #6c757d;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n    box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n    color: #6c757d;\n    background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n    color: #fff;\n    background-color: #6c757d;\n    border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n    color: #28a745;\n    background-color: transparent;\n    background-image: none;\n    border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n    color: #fff;\n    background-color: #28a745;\n    border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n    box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n    color: #28a745;\n    background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n    color: #fff;\n    background-color: #28a745;\n    border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n    color: #17a2b8;\n    background-color: transparent;\n    background-image: none;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n    color: #fff;\n    background-color: #17a2b8;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n    box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n    color: #17a2b8;\n    background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n    color: #fff;\n    background-color: #17a2b8;\n    border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n    color: #ffc107;\n    background-color: transparent;\n    background-image: none;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n    color: #212529;\n    background-color: #ffc107;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n    box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n    color: #ffc107;\n    background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n    color: #212529;\n    background-color: #ffc107;\n    border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n    color: #dc3545;\n    background-color: transparent;\n    background-image: none;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n    color: #fff;\n    background-color: #dc3545;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n    color: #dc3545;\n    background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n    color: #fff;\n    background-color: #dc3545;\n    border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n    color: #f8f9fa;\n    background-color: transparent;\n    background-image: none;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n    color: #212529;\n    background-color: #f8f9fa;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n    box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n    color: #f8f9fa;\n    background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n    color: #212529;\n    background-color: #f8f9fa;\n    border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n    color: #343a40;\n    background-color: transparent;\n    background-image: none;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n    color: #fff;\n    background-color: #343a40;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n    box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n    color: #343a40;\n    background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n    color: #fff;\n    background-color: #343a40;\n    border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n    box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/page.css",
    "content": "/*!\n * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)\n * Code licensed under the Apache License v2.0.\n * For details, see http://www.apache.org/licenses/LICENSE-2.0.\n */\n\nbody {\n    background-color: #f8f8f8;\n}\n\n.example {\n  padding: .625rem 1.825rem .625rem 2.5rem;\n  border: 1px #ccc dashed;\n  position: relative;\n  margin: 0 0 .625rem 0;\n  background-color: #ffffff;\n}\n\ndl dt,\ndl dd {\n  line-height: 1.25rem;\n}\ndl dt {\n  font-style: normal;\n  font-weight: 700;\n}\ndl dd {\n  margin-left: .9375rem;\n}\ndl.horizontal dt {\n  float: left;\n  width: 10rem;\n  overflow: hidden;\n  clear: left;\n  text-align: right;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\ndl.horizontal dd {\n  margin-left: 11.25rem;\n}\n\n#wrapper {\n    width: 100%;\n}\n\n#page-wrapper {\n    padding: 0 15px;\n    min-height: 568px;\n    background-color: #fff;\n}\n\n@media(min-width:768px) {\n    #page-wrapper {\n        position: inherit;\n        margin: 0 0 0 250px;\n        padding: 0 30px;\n        border-left: 1px solid #e7e7e7;\n    }\n}\n\n.navbar-top-links {\n    margin-right: 0;\n}\n\n.navbar-top-links li {\n    display: inline-block;\n}\n\n.navbar-top-links li:last-child {\n    margin-right: 15px;\n}\n\n.navbar-top-links li a {\n    padding: 15px;\n    min-height: 50px;\n}\n\n.navbar-top-links .dropdown-menu li {\n    display: block;\n}\n\n.navbar-top-links .dropdown-menu li:last-child {\n    margin-right: 0;\n}\n\n.navbar-top-links .dropdown-menu li a {\n    padding: 3px 20px;\n    min-height: 0;\n}\n\n.navbar-top-links .dropdown-menu li a div {\n    white-space: normal;\n}\n\n.navbar-top-links .dropdown-messages,\n.navbar-top-links .dropdown-tasks,\n.navbar-top-links .dropdown-alerts {\n    width: 310px;\n    min-width: 0;\n}\n\n.navbar-top-links .dropdown-messages {\n    margin-left: 5px;\n}\n\n.navbar-top-links .dropdown-tasks {\n    margin-left: -59px;\n}\n\n.navbar-top-links .dropdown-alerts {\n    margin-left: -123px;\n}\n\n.navbar-top-links .dropdown-user {\n    right: 0;\n    left: auto;\n}\n\n.sidebar .sidebar-nav.navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n    background-color: #71b1d1;\n\tcolor: #ffffff;\n\tposition: relative;\n\twidth: 100%;\n\tpadding: 0;\n\tmargin: 0;\n\tlist-style: none inside none;\n}\n\n.sidebar .sidebar-search {\n    padding: 15px;\n}\n\n.sidebar ul li {\n    border-bottom: 1px solid #e7e7e7;\n}\n\n.sidebar ul li a.active {\n  background-color: #ffffff;\n  color: #ffffff;\n}\n\n.sidebar a{\n\tcolor: #fff;\n}\n\n.sidebar .arrow {\n    float: right;\n}\n\n.sidebar .fa.arrow:before {\n    content: \"\\f104\";\n}\n\n.sidebar .active>a>.fa.arrow:before {\n    content: \"\\f107\";\n}\n\n.sidebar .nav-second-level li,\n.sidebar .nav-third-level li {\n    border-bottom: 0!important;\n}\n\n.sidebar .nav-second-level li a {\n    padding-left: 37px;\n}\n\n.sidebar .nav-third-level li a {\n    padding-left: 52px;\n}\n\n@media(min-width:768px) {\n    .sidebar {\n        z-index: 1;\n        position: absolute;\n        width: 250px;\n        margin-top: 51px;\n    }\n\n    .navbar-top-links .dropdown-messages,\n    .navbar-top-links .dropdown-tasks,\n    .navbar-top-links .dropdown-alerts {\n        margin-left: auto;\n    }\n}\n\n\n.btn-outline {\n    color: inherit;\n    background-color: transparent;\n    transition: all .5s;\n}\n\n.btn-primary.btn-outline {\n    color: #428bca;\n}\n\n.btn-success.btn-outline {\n    color: #5cb85c;\n}\n\n.btn-info.btn-outline {\n    color: #5bc0de;\n}\n\n.btn-warning.btn-outline {\n    color: #f0ad4e;\n}\n\n.btn-danger.btn-outline {\n    color: #d9534f;\n}\n\n.btn-primary.btn-outline:hover,\n.btn-success.btn-outline:hover,\n.btn-info.btn-outline:hover,\n.btn-warning.btn-outline:hover,\n.btn-danger.btn-outline:hover {\n    color: #fff;\n}\n\n.chat {\n    margin: 0;\n    padding: 0;\n    list-style: none;\n}\n\n.chat li {\n    margin-bottom: 10px;\n    padding-bottom: 5px;\n    border-bottom: 1px dotted #999;\n}\n\n.chat li.left .chat-body {\n    margin-left: 60px;\n}\n\n.chat li.right .chat-body {\n    margin-right: 60px;\n}\n\n.chat li .chat-body p {\n    margin: 0;\n}\n\n.panel .slidedown .glyphicon,\n.chat .glyphicon {\n    margin-right: 5px;\n}\n\n.chat-panel .panel-body {\n    height: 350px;\n    overflow-y: scroll;\n}\n\n.login-panel {\n    margin-top: 25%;\n}\n\n.flot-chart {\n    display: block;\n    height: 400px;\n}\n\n.flot-chart-content {\n    width: 100%;\n    height: 100%;\n}\n\n.dataTables_wrapper {\n    position: relative;\n    clear: both;\n}\n\ntable.dataTable thead .sorting,\ntable.dataTable thead .sorting_asc,\ntable.dataTable thead .sorting_desc,\ntable.dataTable thead .sorting_asc_disabled,\ntable.dataTable thead .sorting_desc_disabled {\n    background: 0 0;\n}\n\ntable.dataTable thead .sorting_asc:after {\n    content: \"\\f0de\";\n    float: right;\n    font-family: fontawesome;\n}\n\ntable.dataTable thead .sorting_desc:after {\n    content: \"\\f0dd\";\n    float: right;\n    font-family: fontawesome;\n}\n\ntable.dataTable thead .sorting:after {\n    content: \"\\f0dc\";\n    float: right;\n    font-family: fontawesome;\n    color: rgba(50,50,50,.5);\n}\n\n.btn-circle {\n    width: 30px;\n    height: 30px;\n    padding: 6px 0;\n    border-radius: 15px;\n    text-align: center;\n    font-size: 12px;\n    line-height: 1.428571429;\n}\n\n.btn-circle.btn-lg {\n    width: 50px;\n    height: 50px;\n    padding: 10px 16px;\n    border-radius: 25px;\n    font-size: 18px;\n    line-height: 1.33;\n}\n\n.btn-circle.btn-xl {\n    width: 70px;\n    height: 70px;\n    padding: 10px 16px;\n    border-radius: 35px;\n    font-size: 24px;\n    line-height: 1.33;\n}\n\n.show-grid [class^=col-] {\n    padding-top: 10px;\n    padding-bottom: 10px;\n    border: 1px solid #ddd;\n    background-color: #eee!important;\n}\n\n.show-grid {\n    margin: 15px 0;\n}\n\n.huge {\n    font-size: 40px;\n}\n\n.panel-green {\n    border-color: #5cb85c;\n}\n\n.panel-green .panel-heading {\n    border-color: #5cb85c;\n    color: #fff;\n    background-color: #5cb85c;\n}\n\n.panel-green a {\n    color: #5cb85c;\n}\n\n.panel-green a:hover {\n    color: #3d8b3d;\n}\n\n.panel-red {\n    border-color: #d9534f;\n}\n\n.panel-red .panel-heading {\n    border-color: #d9534f;\n    color: #fff;\n    background-color: #d9534f;\n}\n\n.panel-red a {\n    color: #d9534f;\n}\n\n.panel-red a:hover {\n    color: #b52b27;\n}\n\n.panel-yellow {\n    border-color: #f0ad4e;\n}\n\n.panel-yellow .panel-heading {\n    border-color: #f0ad4e;\n    color: #fff;\n    background-color: #f0ad4e;\n}\n\n.panel-yellow a {\n    color: #f0ad4e;\n}\n\n.panel-yellow a:hover {\n    color: #df8a13;\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/timeline.css",
    "content": ".timeline {\n    position: relative;\n    padding: 20px 0 20px;\n    list-style: none;\n}\n\n.timeline:before {\n    content: \" \";\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 50%;\n    width: 3px;\n    margin-left: -1.5px;\n    background-color: #eeeeee;\n}\n\n.timeline > li {\n    position: relative;\n    margin-bottom: 20px;\n}\n\n.timeline > li:before,\n.timeline > li:after {\n    content: \" \";\n    display: table;\n}\n\n.timeline > li:after {\n    clear: both;\n}\n\n.timeline > li:before,\n.timeline > li:after {\n    content: \" \";\n    display: table;\n}\n\n.timeline > li:after {\n    clear: both;\n}\n\n.timeline > li > .timeline-panel {\n    float: left;\n    position: relative;\n    width: 46%;\n    padding: 20px;\n    border: 1px solid #d4d4d4;\n    border-radius: 2px;\n    -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);\n    box-shadow: 0 1px 6px rgba(0,0,0,0.175);\n}\n\n.timeline > li > .timeline-panel:before {\n    content: \" \";\n    display: inline-block;\n    position: absolute;\n    top: 26px;\n    right: -15px;\n    border-top: 15px solid transparent;\n    border-right: 0 solid #ccc;\n    border-bottom: 15px solid transparent;\n    border-left: 15px solid #ccc;\n}\n\n.timeline > li > .timeline-panel:after {\n    content: \" \";\n    display: inline-block;\n    position: absolute;\n    top: 27px;\n    right: -14px;\n    border-top: 14px solid transparent;\n    border-right: 0 solid #fff;\n    border-bottom: 14px solid transparent;\n    border-left: 14px solid #fff;\n}\n\n.timeline > li > .timeline-badge {\n    z-index: 100;\n    position: absolute;\n    top: 16px;\n    left: 50%;\n    width: 50px;\n    height: 50px;\n    margin-left: -25px;\n    border-radius: 50% 50% 50% 50%;\n    text-align: center;\n    font-size: 1.4em;\n    line-height: 50px;\n    color: #fff;\n    background-color: #999999;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel {\n    float: right;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel:before {\n    right: auto;\n    left: -15px;\n    border-right-width: 15px;\n    border-left-width: 0;\n}\n\n.timeline > li.timeline-inverted > .timeline-panel:after {\n    right: auto;\n    left: -14px;\n    border-right-width: 14px;\n    border-left-width: 0;\n}\n\n.timeline-badge.primary {\n    background-color: #2e6da4 !important;\n}\n\n.timeline-badge.success {\n    background-color: #3f903f !important;\n}\n\n.timeline-badge.warning {\n    background-color: #f0ad4e !important;\n}\n\n.timeline-badge.danger {\n    background-color: #d9534f !important;\n}\n\n.timeline-badge.info {\n    background-color: #5bc0de !important;\n}\n\n.timeline-title {\n    margin-top: 0;\n    color: inherit;\n}\n\n.timeline-body > p,\n.timeline-body > ul {\n    margin-bottom: 0;\n}\n\n.timeline-body > p + p {\n    margin-top: 5px;\n}\n\n@media(max-width:767px) {\n    ul.timeline:before {\n        left: 40px;\n    }\n\n    ul.timeline > li > .timeline-panel {\n        width: calc(100% - 90px);\n        width: -moz-calc(100% - 90px);\n        width: -webkit-calc(100% - 90px);\n    }\n\n    ul.timeline > li > .timeline-badge {\n        top: 16px;\n        left: 15px;\n        margin-left: 0;\n    }\n\n    ul.timeline > li > .timeline-panel {\n        float: right;\n    }\n\n    ul.timeline > li > .timeline-panel:before {\n        right: auto;\n        left: -15px;\n        border-right-width: 15px;\n        border-left-width: 0;\n    }\n\n    ul.timeline > li > .timeline-panel:after {\n        right: auto;\n        left: -14px;\n        border-right-width: 14px;\n        border-left-width: 0;\n    }\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/authority.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n            <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增授权规则</button>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">授权规则</span>\n                    <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n                    <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n                    <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n                        <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                                   placeholder=\"机器\"></selectize>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\">\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 40%\">\n                                资源名\n                            </td>\n                            <td style=\"width: 10%;\">\n                                流控应用\n                            </td>\n                            <td style=\"width: 10%;\">\n                                授权类型\n                            </td>\n                            <td style=\"width: 12%;\">\n                                操作\n                            </td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr dir-paginate=\"ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                            pagination-id=\"entriesPagination\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.resource}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.limitApp }}</td>\n                            <td>\n                                <span ng-if=\"ruleEntity.rule.strategy == 0\">白名单</span>\n                                <span ng-if=\"ruleEntity.rule.strategy == 1\">黑名单</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n                <div class=\"pagination-footer\">\n                    <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n                                             on-page-change=\"\">\n                    </dir-pagination-controls>\n                    <div class=\"tools\" style=\"\">\n                        <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n                        <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n                    </div>\n                    <!-- .tools -->\n                </div>\n                <!-- pagination-footer -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster/client.html",
    "content": "<div class=\"row clearfix\">\n    <form role=\"form\" class=\"form-horizontal\">\n        <div class=\"form-group\" ng-if=\"stateVO.currentMode == 0\">\n            <label class=\"col-sm-2 control-label\">连接状态</label>\n            <div class=\"col-sm-4\">\n                <p class=\"form-control-static text-danger\" ng-if=\"stateVO.client.clientConfig.clientState === 0\">未连接</p>\n                <p class=\"form-control-static\" ng-if=\"stateVO.client.clientConfig.clientState === 1\">连接中</p>\n                <p class=\"form-control-static text-success\" ng-if=\"stateVO.client.clientConfig.clientState === 2\">已连接</p>\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server IP</label>\n            <div class=\"col-sm-4\">\n                <input type=\"text\" class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.serverHost' placeholder='请指定 Token Server IP' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.serverPort' placeholder='请指定 Token Server 端口' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">请求超时时间（ms）</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" required class=\"form-control highlight-border\" ng-model='stateVO.client.clientConfig.requestTimeout' placeholder='请指定请求超时时间（ms）' />\n            </div>\n        </div>\n    </form>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster/server.html",
    "content": "<div class=\"row clearfix\">\n    <form role=\"form\" class=\"form-horizontal\">\n        <div class=\"form-group\" ng-if=\"stateVO.currentMode == 1\">\n            <label class=\"col-sm-2 control-label\">Token Server 模式</label>\n            <div class=\"col-sm-4\">\n                <p class=\"form-control-static\" ng-if=\"!stateVO.server.embedded\">独立模式 (Alone)</p>\n                <p class=\"form-control-static\" ng-if=\"stateVO.server.embedded\">嵌入模式 (Embedded)</p>\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"1\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">命名空间集合</label>\n            <div class=\"col-sm-4\">\n                <input type=\"text\" required class=\"form-control highlight-border\" ng-model='stateVO.server.namespaceSetStr' placeholder='请指定服务端服务的命名空间集合（以,分隔）' />\n            </div>\n        </div>\n        <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">最大全局 QPS</label>\n            <div class=\"col-sm-4\">\n                <input type=\"number\" min=\"0\" max=\"100000\" required class=\"form-control highlight-border\" ng-model='stateVO.server.flow.maxAllowedQps' placeholder='请指定服务端最大全局 QPS' />\n            </div>\n        </div>\n    </form>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_assign_manage.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - 机器分配/管控</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <form role=\"form\" class=\"form-horizontal\">\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Server 列表</label>\n                            <div class=\"col-sm-4\">\n                                <select ng-model=\"tmp.curChosenServer\" ng-change=\"onCurrentServerChange()\" size=\"8\"\n                                        ng-options=\"serverGroup.machineId for serverGroup in clusterMap\"\n                                        class=\"form-control\"></select>\n                            </div>\n                            <button type=\"button\" class=\"btn btn-outline-warning\" ng-click=\"removeFromServerList()\">移除\n                            </button>\n                        </div>\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Token Server 端口</label>\n                            <div class=\"col-sm-4\">\n                                <input type=\"number\" class=\"form-control highlight-border\"\n                                       ng-disabled=\"!tmp.curChosenServer.belongToApp\"\n                                       ng-model='tmp.curChosenServer.port' placeholder='port' min=\"1\" max=\"65535\"/>\n                            </div>\n                        </div>\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">命名空间集合</label>\n                            <div class=\"col-sm-4\">\n                                <input type=\"text\" required class=\"form-control highlight-border\"\n                                       ng-disabled=\"!tmp.curChosenServer.belongToApp\"\n                                       ng-model='tmp.curChosenServer.namespaceSetStr'\n                                       placeholder='请指定服务端服务的命名空间集合（以,分隔）'/>\n                            </div>\n                        </div>\n                    </form>\n                    <form role=\"form\" class=\"form-inline\" style=\"margin-top: 30px; margin-left: 20px;\">\n                        <div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <label class=\"control-label\" style=\"width: 200px; text-align: center;\">当前对应客户端列表</label>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curClientChosen\"\n                                            ng-options=\"ip for ip in tmp.curChosenServer.clientSet\"\n                                            class=\"form-control\" style=\"width: 100%;\"></select>\n                                </div>\n\n                            </div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-disabled=\"!tmp.curChosenServer || !tmp.curChosenServer.machineId\"\n                                            ng-click=\"moveToServerGroup()\">←\n                                    </button>\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-click=\"moveToRemainingSharePool()\">→\n                                    </button>\n                                </div>\n                            </div>\n\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-12\">\n                                    <label class=\"control-label\" style=\"width: 220px; text-align: center;\">未分配机器列表</label>\n                                    <div>\n                                        <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curRemainingClientChosen\"\n                                                ng-options=\"ip for ip in remainingClientAddressList\"\n                                                class=\"form-control\" style=\"width: 100%;\">\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <div class=\"form-group\">\n                                <div class=\"col-sm-6\">\n                                    <button type=\"button\" class=\"btn btn-outline-primary\"\n                                            ng-click=\"addToServerList()\">添加为 server\n                                    </button>\n                                </div>\n                            </div>\n                        </div>\n                    </form>\n                    <div class=\"separator\"></div>\n                    <div style=\"margin-top: 20px;\">\n                        <button type=\"button\" style=\"margin: 0 10px 10px 10px;\" class=\"btn btn-outline-success\"\n                                ng-click=\"saveAndApplyAssign()\">保存并执行分配\n                        </button>\n                    </div>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_client_list.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.clusterAppServerList({app: app})\">\n            Token Server 列表\n        </a>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Client 列表</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"min-width: 12%;\">Client ID</td>\n                            <td>Server IP</td>\n                            <td>Server 端口</td>\n                            <td>连接状态</td>\n                            <td style=\"min-width: 15%;\">操作</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"clientVO in clientVOList\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{clientVO.id}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{clientVO.state.clientConfig.serverHost}}</td>\n                            <td>{{clientVO.state.clientConfig.serverPort}}</td>\n                            <td>\n                                <span class=\"form-control-static text-danger\" ng-if=\"clientVO.state.clientConfig.clientState === 0\">未连接</span>\n                                <span class=\"form-control-static\" ng-if=\"clientVO.state.clientConfig.clientState === 1\">连接中</span>\n                                <span class=\"form-control-static text-success\" ng-if=\"clientVO.state.clientConfig.clientState === 2\">已连接</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\"\n                                        ng-click=\"modifyClientConfigDialog(clientVO)\" style=\"font-size: 12px; height:25px;\">编辑配置</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_server_list.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\">\n        <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-if=\"!loadError\" ng-click=\"newServerDialog()\">\n            <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增 Token Server</button>\n        <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.clusterAppClientList({app: app})\">\n            Token Client 列表\n        </a>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Server 列表</span>\n                    <input class=\"form-control width-200\" placeholder=\"搜索 server...\" ng-model=\"searchKey\">\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 15%;\">Server ID</td>\n                            <td style=\"width: 10%;\">Port</td>\n                            <td style=\"width: 15%;\">命名空间集合</td>\n                            <td style=\"width: 10%;\">运行模式</td>\n                            <td>总连接数</td>\n                            <td>QPS 总览</td>\n                            <td style=\"width: 20%;\">操作</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"serverVO in serverVOList | filter: {id: searchKey}\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.id}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">{{serverVO.id}}（自主指定）</span>\n                            </td>\n                            <td>{{serverVO.port}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.state.namespaceSetStr}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                                <span ng-if=\"serverVO.belongToApp && serverVO.state.embedded\">嵌入模式</span>\n                                <span ng-if=\"serverVO.belongToApp && !serverVO.state.embedded\">独立模式</span>\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.connectedCount}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td>\n                                <span ng-if=\"serverVO.belongToApp\">{{serverVO.state.requestLimitDataStr}}</span>\n                                <span ng-if=\"!serverVO.belongToApp\">未知</span>\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\" ng-if=\"serverVO.belongToApp\"\n                                        ng-click=\"viewConnectionDetail(serverVO)\" style=\"font-size: 12px; height:25px;\">连接详情</button>\n                                <button class=\"btn btn-xs btn-outline-primary\" type=\"button\"\n                                        ng-click=\"modifyServerAssignConfig(serverVO)\" style=\"font-size: 12px; height:25px;\">管理</button>\n                                <button class=\"btn btn-xs btn-outline-danger\" type=\"button\"\n                                        ng-click=\"unbindServer(serverVO.id)\" style=\"font-size: 12px; height:25px;\">移除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_server_overview.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n        <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n        <div class=\"col-md-12\">\n            <div class=\"card\">\n                <div class=\"inputs-header\">\n                    <span class=\"brand\" style=\"font-size: 13px;\">集群限流 - Token Server 总览</span>\n                </div>\n\n                <!-- error panel -->\n                <div class=\"row clearfix\" ng-if=\"loadError\">\n                    <div class=\"col-md-6 col-md-offset-3\">\n                        <div class=\"panel panel-default\">\n                            <div class=\"panel-body\">\n                                <center>\n                                    <p>{{loadError.message}}</p>\n                                </center>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <form role=\"form\" class=\"form-horizontal\">\n                        <div class=\"form-group\" hidden>\n                            <label class=\"col-sm-2 control-label\">Token Server 列表</label>\n                            <div class=\"col-sm-4\">\n                                <select ng-model=\"tmp.curChosenServer\" ng-change=\"onChosenServerChange()\"\n                                        ng-options=\"serverEntity.id for serverEntity in serverVOList\"\n                                        class=\"form-control\"></select>\n                            </div>\n                        </div>\n                    </form>\n\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 12%;\">Server ID</td>\n                            <td style=\"width: 5%;\">Port</td>\n                            <td style=\"width: 10%;\">命名空间集合</td>\n                            <td>总连接数</td>\n                            <td>连接情况</td>\n                            <td>QPS 总览</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"serverVO in clientVOList\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{serverVO.id}}</td>\n                            <td>{{serverVO.port}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{serverVO.state.namespaceSetStr}}\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{serverVO.connectedCount}}\n                            </td>\n                            <td>\n                                <p ng-repeat=\"cg in serverVO.state.connection\">\n                                    namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients:\n                                    {{generateConnectionSet(cg.connectionSet)}}\n                                </p>\n                            </td>\n                            <td>\n                                <p ng-repeat=\"crl in serverVO.state.requestLimitData\">\n                                    namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS:\n                                    {{crl.maxAllowedQps}}\n                                </p>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n            </div>\n            <!-- .card -->\n        </div>\n        <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_single_config.html",
    "content": "\n<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">集群限流</span>\n          <input class=\"form-control witdh-200\" placeholder=\"机器搜索\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                       placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!-- error panel -->\n        <div class=\"row clearfix\" ng-if=\"loadError\">\n          <div class=\"col-md-6 col-md-offset-3\">\n            <div class=\"panel panel-default\">\n              <div class=\"panel-body\">\n                <center>\n                  <p>{{loadError.message}}</p>\n                </center>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\"  ng-if=\"!loadError\">\n          <form role=\"form\" class=\"form-horizontal\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">当前模式</label>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == 0\">Client</p>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == 1\">Server</p>\n              <p class=\"col-sm-6 control-label\" style=\"text-align: left; font-weight: normal;\" ng-if=\"stateVO.currentMode == -1\">未开启</p>\n            </div>\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">集群限流模式变换</label>\n              <div class=\"col-sm-4\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"mode\" value=\"0\" ng-model='stateVO.stateInfo.mode' ng-disabled=\"!stateVO.stateInfo.clientAvailable\" />&nbsp;Client&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"mode\" value=\"1\" ng-model='stateVO.stateInfo.mode' ng-disabled=\"!stateVO.stateInfo.serverAvailable\" />&nbsp;Server\n                </div>\n              </div>\n            </div>\n          </form>\n          <!-- no-cluster-mode-available-panel.start -->\n          <div ng-if=\"!stateVO.stateInfo.clientAvailable && !stateVO.stateInfo.serverAvailable\">\n            <!-- error panel -->\n            <div class=\"row clearfix\">\n              <div class=\"col-md-6 col-md-offset-3\">\n                <div class=\"panel panel-default\">\n                  <div class=\"panel-body\">\n                    <center>\n                      <p>该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖，请引入相关依赖。</p>\n                    </center>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <!-- no-cluster-mode-available-panel.stop -->\n\n          <div ng-if=\"stateVO.stateInfo.clientAvailable || stateVO.stateInfo.serverAvailable\">\n            <div ng-if=\"stateVO.stateInfo.clientAvailable && stateVO.stateInfo.mode == 0\">\n              <div ng-include=\"'app/views/cluster/client.html'\"></div>\n            </div>\n            <div ng-if=\"stateVO.stateInfo.serverAvailable && stateVO.stateInfo.mode == 1\">\n              <div ng-include=\"'app/views/cluster/server.html'\"></div>\n            </div>\n\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n              <button style=\"margin: 0 10px 10px 10px;\" class=\"btn btn-outline-success\"\n                      ng-click=\"saveConfig()\">保存配置</button>\n            </div>\n          </div>\n\n        </div>\n        <!-- .card-body -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dashboard/home.html",
    "content": "<div>\n  <div class=\"row\">\n    <div class=\"col-lg-12\">\n      <h1 class=\"page-header\">欢迎使用 Sentinel 控制台</h1>\n    </div>\n    <!-- /.col-lg-12 -->\n  </div>\n  <!-- /.row -->\n  <!-- /.panel -->\n  <div class=\"row\">\n  </div>\n  <!-- /.row -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dashboard/main.html",
    "content": "<div id=\"wrapper\">\n  <!-- Navigation -->\n  <header></header>\n  <!-- /.navbar-top-links -->\n  <!-- /.navbar-static-side -->\n  <div id=\"page-wrapper\">\n    <div ui-view></div>\n  </div>\n  <!-- /#page-wrapper -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/degrade.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增降级规则</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">降级规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <!--<td style=\"width: 10%;\">-->\n                  <!--降级应用-->\n                <!--</td>-->\n                <td style=\"width: 10%;\">\n                  降级模式\n                </td>\n                <td style=\"width: 10%;\">\n                  阈值\n                </td>\n                <td style=\"width: 10%;\">\n                  时间窗口(s)\n                </td>\n                <!--<td style=\"width: 8%;\">-->\n                <!--状态-->\n                <!--</td>-->\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <!--<td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>-->\n                <td>\n                  <span ng-if=\"rule.grade == 0\">RT</span>\n                  <span ng-if=\"rule.grade == 1\" title=\"秒级异常比例\">异常比例</span>\n                  <span ng-if=\"rule.grade == 2\" title=\"分钟级异常数\">异常数</span>\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.timeWindow}}s\n                </td>\n\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/authority-rule-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">{{authorityRuleDialog.title}}</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">资源名</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" ng-if=\"authorityRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.rule.resource'\n                                   disabled=\"\" />\n                            <input type=\"text\" ng-if=\"authorityRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名称\" ng-model='currentRule.rule.resource'\n                            />\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">流控应用</label>\n                        <div class=\"col-sm-9\">\n                            <input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.rule.limitApp' placeholder='指调用方，多个调用方名称用半角英文逗号（,）分隔'\n                            />\n                        </div>\n                    </div>\n\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-2 control-label\">授权类型</label>\n                        <div class=\"col-sm-4\">\n                            <div class=\"form-control highlight-border\" align=\"center\">\n                                <input type=\"radio\" name=\"strategy\" value=\"0\" checked ng-model='currentRule.rule.strategy' />&nbsp;白名单&nbsp;&nbsp;\n                                <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='currentRule.rule.strategy' />&nbsp;黑名单\n                            </div>\n                        </div>\n                    </div>\n\n                </form>\n            </div>\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n                <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n                <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{authorityRuleDialog.confirmBtnText}}</button>\n                <button ng-if=\"authorityRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n                        ng-click=\"saveRuleAndContinue()\">{{authorityRuleDialog.saveAndContinueBtnText}}</button>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-client-config-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">修改 Token Client 配置</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Client ID</label>\n                <div class=\"col-sm-4\">\n                    <p class=\"form-control-static\">{{ccDialogData.clientId}}</p>\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Token Server IP</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"text\" class=\"form-control highlight-border\" ng-model='ccDialogData.serverHost' placeholder='请指定 Token Server IP' />\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">Token Server 端口</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"number\" min=\"0\" max=\"65535\" required class=\"form-control highlight-border\" ng-model='ccDialogData.serverPort' placeholder='请指定 Token Server 端口' />\n                </div>\n            </div>\n            <div class=\"form-group\">\n                <label class=\"col-sm-3 control-label\">请求超时时间（ms）</label>\n                <div class=\"col-sm-4\">\n                    <input type=\"number\" min=\"0\" required class=\"form-control highlight-border\" ng-model='ccDialogData.requestTimeout' placeholder='请指定请求超时时间（ms）' />\n                </div>\n            </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"ccDialog.close()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"doModifyClientConfig()\">保存</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-assign-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">{{serverAssignDialogData.title}}</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div ng-if=\"serverAssignDialogData.type == 'edit'\">\n                        <div class=\"form-group\">\n                            <label class=\"col-sm-2 control-label\">Token Server</label>\n                            <div class=\"col-sm-4\">\n                                <p class=\"form-control-static\">{{serverAssignDialogData.serverData.currentServer}}</p>\n                            </div>\n\n                            <label class=\"col-sm-2 control-label\">Server 端口</label>\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                       ng-disabled=\"!serverAssignDialogData.serverData.belongToApp\"\n                                       ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口'/>\n                            </div>\n                        </div>\n                        <div class=\"form-group\" ng-if=\"serverAssignDialogData.serverData.belongToApp\">\n                            <label class=\"col-sm-2 control-label\"\n                                   title=\"server 最大允许的总 QPS，注意 embedded 模式下不要设的太大\">最大允许 QPS</label>\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" min=\"0\" max=\"200000\" class=\"form-control highlight-border\"\n                                       ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div ng-if=\"serverAssignDialogData.type == 'add'\">\n                        <div class=\"form-group\" >\n                            <label class=\"col-sm-2 control-label\">机器类型</label>\n                            <div class=\"col-sm-4\">\n                                <div class=\"form-control highlight-border\" align=\"center\">\n                                    <input type=\"radio\" name=\"strategy\" value=\"0\" checked ng-model='serverAssignDialogData.serverData.serverType' />&nbsp;应用内机器&nbsp;&nbsp;\n                                    <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='serverAssignDialogData.serverData.serverType' />&nbsp;外部指定机器\n                                </div>\n                            </div>\n\n                            <div ng-if=\"serverAssignDialogData.serverData.serverType == 1\">\n                                <div class=\"col-sm-6\">\n                                    <p class=\"form-control-static text-primary\" style=\"font-size: x-small;\">若指定外部 server，请先在相应页面对外部 server 进行配置，然后在此页面指定。</p>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div ng-if=\"serverAssignDialogData.serverData.serverType == 0\">\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\">选择机器</label>\n                                <div class=\"col-sm-4\">\n                                    <select ng-model=\"serverAssignDialogData.serverData.currentServer\" ng-change=\"onCurrentServerChange()\"\n                                            ng-options=\"machineId for machineId in remainingMachineList\"\n                                            class=\"form-control\"></select>\n                                </div>\n\n                                <label class=\"col-sm-2 control-label\">Server 端口</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>\n                                </div>\n                            </div>\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\"\n                                       title=\"server 最大允许的总 QPS，注意 embedded 模式下不要设的太大\">最大允许 QPS</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"0\" max=\"200000\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div ng-if=\"serverAssignDialogData.serverData.serverType == 1\">\n                            <div class=\"form-group\">\n                                <label class=\"col-sm-2 control-label\">Server IP</label>\n                                <div class=\"col-sm-4\">\n                                    <input type=\"text\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.currentServer' placeholder='请输入独立的 Token Server IP'/>\n                                </div>\n\n                                <label class=\"col-sm-2 control-label\">Server 端口</label>\n                                <div class=\"col-sm-3\">\n                                    <input type=\"number\" min=\"1\" max=\"65535\" class=\"form-control highlight-border\"\n                                           ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </form>\n\n                <!-- assign form start -->\n                <form role=\"form\" class=\"form-inline\" ng-if=\"serverAssignDialogData.serverData.currentServer\"\n                      style=\"margin-top: 30px; margin-left: 20px; text-align: center;\">\n                    <div>\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n                                <label class=\"control-label\" style=\"width: 220px; text-align: center;\">请从中选取 client：</label>\n                                <div>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curRemainingClientChosen\"\n                                            ng-options=\"ip for ip in remainingMachineList | filter: notChosenServer\"\n                                            class=\"form-control\" style=\"width: 100%;\">\n                                    </select>\n                                </div>\n                            </div>\n                        </div>\n\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n\n                                <button type=\"button\" class=\"btn btn-outline-primary\"\n                                        ng-click=\"moveToRemainingSharePool()\">←\n                                </button>\n                                <button type=\"button\" class=\"btn btn-outline-primary\"\n                                        ng-click=\"moveToServerGroup()\">→\n                                </button>\n                            </div>\n                        </div>\n\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-12\">\n                                <label class=\"control-label\" style=\"width: 200px; text-align: center;\">已选取的 client 列表</label>\n                                <div>\n                                    <select size=\"8\" multiple=\"multiple\" ng-model=\"tmp.curClientChosen\"\n                                            ng-options=\"ip for ip in serverAssignDialogData.serverData.clientSet\"\n                                            class=\"form-control\" style=\"width: 100%;\"></select>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </form>\n            </div>\n            <div class=\"separator\"></div>\n            <div clss=\"row\" style=\"margin-top: 20px;\">\n                <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"serverAssignDialog.close()\">取消</button>\n                <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveAssignForDialog()\">{{serverAssignDialogData.confirmBtnText}}</button>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html",
    "content": "<div>\n    <span class=\"brand\" style=\"font-weight:bold;\">连接详情</span>\n    <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n        <div class=\"panel-body\">\n            <div class=\"row\">\n                <form role=\"form\" class=\"form-horizontal\">\n                    <div class=\"form-group\">\n                        <label class=\"col-sm-3 control-label\">Token Server</label>\n                        <div class=\"col-sm-4\">\n                            <p class=\"form-control-static\">{{connectionDetailDialogData.serverData.id}}</p>\n                        </div>\n                    </div>\n                </form>\n\n                <div class=\"col-md-12\">\n                    <!-- table start -->\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"min-width: 15%;\" class=\"text-center\">命名空间</td>\n                            <td class=\"text-center\">连接数</td>\n                            <td class=\"text-center\">连接详情</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr ng-repeat=\"cg in connectionDetailDialogData.serverData.state.connection\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\" class=\"text-center\">{{cg.namespace}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\" class=\"text-center\">{{cg.connectedCount}}</td>\n                            <td class=\"text-center\">{{generateConnectionSet(cg.connectionSet)}}</td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/confirm-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{confirmDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <p style=\"margin-left: 20px; margin-right: 20px;\">\n          {{confirmDialog.attentionTitle}}:\n          <br/>\n          <br/>\n          <code style=\"word-wrap: break-word; white-space:normal\">{{confirmDialog.attention}}</code>\n        </p>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-default-inverse\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;\" ng-click=\"confirm()\">{{confirmDialog.confirmBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/degrade-rule-dialog.html",
    "content": "<div>\n\t<span class=\"brand\" style=\"font-weight:bold;\">{{degradeRuleDialog.title}}</span>\n\t<div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n\t\t<div class=\"panel-body\">\n\t\t\t<div class=\"row\">\n\t\t\t\t<form role=\"form\" class=\"form-horizontal\">\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">资源名</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"degradeRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.resource'\n\t\t\t\t\t\t\t disabled=\"\" />\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"degradeRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.resource'\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!--<div class=\"form-group\">-->\n\t\t\t\t\t\t<!--<label class=\"col-sm-2 control-label\">流控应用</label>-->\n\t\t\t\t\t\t<!--<div class=\"col-sm-9\">-->\n\t\t\t\t\t\t\t<!--<input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.limitApp' placeholder='\"default\"表示所有应用。' />-->\n\t\t\t\t\t\t<!--</div>-->\n\t\t\t\t\t<!--</div>-->\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">降级策略</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<div class=\"form-control highlight-border\" align=\"center\">\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"0\" checked ng-model='currentRule.grade' title=\"秒级平均响应时间\" />&nbsp;RT&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' title=\"秒级异常比例\" />&nbsp;异常比例&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' title=\"分钟级异常数，仅 1.3.0 及以上版本生效\" />&nbsp;异常数\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 0\" class=\"col-sm-2 control-label\">RT</label>\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 1\" class=\"col-sm-2 control-label\">异常比例</label>\n\t\t\t\t\t\t<label ng-if=\"currentRule.grade == 2\" class=\"col-sm-2 control-label\">异常数</label>\n\t\t\t\t\t\t<div class=\"col-sm-3\">\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 0\" placeholder=\"毫秒\"/>\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 1\" placeholder=\"0.0~1.0\"/>\n\t\t\t\t\t\t\t<input type='number' class=\"form-control highlight-border\" ng-model='currentRule.count' ng-if=\"currentRule.grade == 2\" placeholder=\"异常数\"/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">时间窗口</label>\n\t\t\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t\t\t\t<input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.timeWindow' placeholder=\"降级时间间隔, 单位秒\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t\t<div class=\"separator\"></div>\n\t\t\t<div clss=\"row\" style=\"margin-top: 20px;\">\n\t\t\t\t<button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n\t\t\t\t<button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{degradeRuleDialog.confirmBtnText}}</button>\n\t\t\t\t<button ng-if=\"degradeRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n\t\t\t\t ng-click=\"saveRuleAndContinue()\">{{degradeRuleDialog.saveAndContinueBtnText}}</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/flow-rule-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{flowRuleDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">资源名</label>\n            <div class=\"col-sm-9\">\n              <input type=\"text\" ng-if=\"flowRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.resource'\n                disabled=\"\" />\n              <input type=\"text\" ng-if=\"flowRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.resource'\n              />\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\" data-toggle=\"tooltip\" title=\"流控针对应用，即流量入口的调用来源（origin）\">来源应用</label>\n            <div class=\"col-sm-9\">\n              <input type=\"text\" class=\"form-control highlight-border\" ng-model='currentRule.limitApp' placeholder='指调用方，\"default\"表示所有应用。'\n              />\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值类型</label>\n            <div class=\"col-sm-4\">\n              <div class=\"form-control highlight-border\" align=\"center\">\n                <input type=\"radio\" name=\"grade\" value=\"1\" checked ng-model='currentRule.grade' />&nbsp;QPS&nbsp;&nbsp;\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' />&nbsp;线程数\n              </div>\n            </div>\n            <div ng-if=\"!currentRule.clusterMode\">\n              <label class=\"col-sm-2 control-label\">单机阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"单机阈值\" />\n              </div>\n            </div>\n            <div ng-if=\"currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 0\">\n              <label class=\"col-sm-2 control-label\">均摊阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"单机均摊阈值\" />\n              </div>\n            </div>\n            <div ng-if=\"currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 1\">\n              <label class=\"col-sm-2 control-label\">集群阈值</label>\n              <div class=\"col-sm-3\">\n                <input type='number' min=\"0\" class=\"form-control highlight-border\" ng-model='currentRule.count' placeholder=\"集群总体阈值\" />\n              </div>\n            </div>\n          </div>\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">是否集群</label>\n            <div class=\"col-sm-2\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.clusterMode\">\n              </label>\n            </div>\n            <div ng-if=\"currentRule.clusterMode\">\n              <label class=\"col-sm-3 control-label\">集群阈值模式</label>\n              <div class=\"col-sm-4\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"clusterThresholdType\" value=\"0\" ng-model='currentRule.clusterConfig.thresholdType' />&nbsp;单机均摊&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"clusterThresholdType\" value=\"1\" ng-model='currentRule.clusterConfig.thresholdType' />&nbsp;总体阈值\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"form-group\" ng-if=\"currentRule.clusterMode\">\n            <label class=\"col-sm-2 control-label\">失败退化</label>\n            <div class=\"col-sm-8\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.clusterConfig.fallbackToLocalWhenFail\">\n              </label>\n              <i class=\"glyphicon glyphicon-info-sign\"></i>&nbsp;如果 Token Server 不可用是否退化到单机限流\n            </div>\n          </div>\n\n          <div ng-if=\"!flowRuleDialog.showAdvanceButton\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\" title=\"调用关系流控模式\">流控模式</label>\n              <div class=\"col-sm-9\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"strategy\" value=\"0\" ng-model='currentRule.strategy' />&nbsp;直接&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"strategy\" value=\"1\" ng-model='currentRule.strategy' />&nbsp;关联&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"strategy\" value=\"2\" ng-model='currentRule.strategy' />&nbsp;链路&nbsp;&nbsp;\n                </div>\n              </div>\n            </div>\n\n            <div class=\"form-group\" ng-show=\"currentRule.strategy==1\">\n              <label class=\"col-sm-2 control-label\">关联资源</label>\n              <div class=\"col-sm-9\">\n                <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"关联资源\" ng-model='currentRule.refResource' />\n              </div>\n            </div>\n            <div class=\"form-group\" ng-show=\"currentRule.strategy==2\">\n              <label class=\"col-sm-2 control-label\">入口资源</label>\n              <div class=\"col-sm-9\">\n                <input type=\"text\" class=\"form-control highlight-border\" placeholder=\"入口资源\" ng-model='currentRule.refResource' />\n              </div>\n            </div>\n          </div>\n          <div ng-if=\"currentRule.grade==1 && !flowRuleDialog.showAdvanceButton\">\n            <div class=\"form-group\">\n              <label class=\"col-sm-2 control-label\">流控效果</label>\n              <div class=\"col-sm-9\">\n                <div class=\"form-control highlight-border\" align=\"center\">\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"0\" checked ng-model='currentRule.controlBehavior' />&nbsp;快速失败&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"1\" ng-model='currentRule.controlBehavior' />&nbsp;Warm Up&nbsp;&nbsp;\n                  <input type=\"radio\" name=\"controlBehavior\" value=\"2\" ng-model='currentRule.controlBehavior' />&nbsp;排队等待\n                </div>\n              </div>\n\n            </div>\n            <div class=\"form-group\">\n              <div ng-if=\"currentRule.controlBehavior==1\">\n                <label class=\"col-sm-2 control-label\">预热时长</label>\n                <div class=\"col-sm-9\">\n                  <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.warmUpPeriodSec' placeholder=\"秒\" />\n                </div>\n              </div>\n              <div ng-if=\"currentRule.controlBehavior==2\">\n                <label class=\"col-sm-2 control-label\">超时时间</label>\n                <div class=\"col-sm-9\">\n                  <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.maxQueueingTimeMs' placeholder=\"毫秒\" />\n                </div>\n              </div>\n            </div>\n          </div>\n          <div class=\"form-group text-center\">\n            <a ng-click=\"onOpenAdvanceClick()\" ng-if=\"flowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">高级选项</a>\n            <a ng-click=\"onCloseAdvanceClick()\" ng-if=\"!flowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">关闭高级选项</a>\n          </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{flowRuleDialog.confirmBtnText}}</button>\n        <button ng-if=\"flowRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n          ng-click=\"saveRuleAndContinue()\">{{flowRuleDialog.saveAndContinueBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/param-flow-rule-dialog.html",
    "content": "<div>\n\t<span class=\"brand\" style=\"font-weight:bold;\">{{paramFlowRuleDialog.title}}</span>\n\t<div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n\t\t<div class=\"panel-body\">\n\t\t\t<div class=\"clearfix\">\n\t\t\t\t<form role=\"form\" class=\"form-horizontal\">\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">资源名</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"paramFlowRuleDialog.type == 'edit'\" class=\"form-control\" placeholder=\"资源名\" ng-model='currentRule.rule.resource' disabled=\"\" />\n\t\t\t\t\t\t\t<input type=\"text\" ng-if=\"paramFlowRuleDialog.type == 'add'\" class=\"form-control highlight-border\" placeholder=\"资源名\" ng-model='currentRule.rule.resource' required />\n\t\t\t\t\t\t</div>\n                    </div>\n                    \n                    <div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">限流模式</label>\n\t\t\t\t\t\t<p class=\"col-sm-9 control-label\" style=\"text-align: left; font-weight: normal;\">QPS 模式</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数索引</label>\n\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.paramIdx' placeholder='请填入传入的热点参数的索引（从 0 开始）' />\n\t\t\t\t\t\t</div>\n                    </div>\n                    \n                    <div class=\"form-group\">\n\t\t\t\t\t\t<div ng-if=\"!currentRule.rule.clusterMode\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">单机阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='单机阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 0\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">均摊阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='集群均摊阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 1\">\n\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">集群阈值</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"number\" class=\"form-control highlight-border\" ng-model='currentRule.rule.count' placeholder='集群总体阈值' />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n                    </div>\n\n\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">是否集群</label>\n\t\t\t\t\t\t<div class=\"col-sm-2\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"clusterMode\" ng-model=\"currentRule.rule.clusterMode\">\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div ng-if=\"currentRule.rule.clusterMode\">\n\t\t\t\t\t\t\t<label class=\"col-sm-3 control-label\">集群阈值模式</label>\n\t\t\t\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t\t\t\t\t<div class=\"form-control highlight-border\" align=\"center\">\n\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"clusterThresholdType\" value=\"0\" ng-model='currentRule.rule.clusterConfig.thresholdType' />&nbsp;单机均摊&nbsp;&nbsp;\n\t\t\t\t\t\t\t\t\t<input type=\"radio\" name=\"clusterThresholdType\" value=\"1\" ng-model='currentRule.rule.clusterConfig.thresholdType' />&nbsp;总体阈值\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\n                    <!-- exclusion item part start -->\n\t\t\t\t\t<div ng-if=\"!paramFlowRuleDialog.showAdvanceButton\">\n\t\t\t\t\t\t<hr />\n\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t<div class=\"form-group\" style=\"text-align: center\">\n\t\t\t\t\t\t\t\t<label class=\"control-label\">参数例外项</label>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数类型</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-9\">\n\t\t\t\t\t\t\t\t\t<select ng-model=\"curExItem.classType\" ng-options=\"classType for classType in paramItemClassTypeList\" class=\"form-control\" placeholder=\"请选择参数类型\">\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">参数值</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-3\">\n\t\t\t\t\t\t\t\t\t<input ng-model=\"curExItem.object\" type=\"text\" class=\"form-control\" placeholder=\"例外项参数值\">\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<label class=\"col-sm-2 control-label\">限流阈值</label>\n\t\t\t\t\t\t\t\t<div class=\"col-md-3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" ng-model=\"curExItem.count\" class=\"form-control\" placeholder=\"限流阈值\">\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div class=\"col-md-2\">\n\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-success\"\n\t\t\t\t\t\t\t\t\t\t\tng-disabled=\"notValidParamItem(curExItem)\"\n\t\t\t\t\t\t\t\t\t\t\tng-click=\"addParamItem()\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"fa fa-plus\">&nbsp;添加</span>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<div class=\"col-md-12\">\n\t\t\t\t\t\t\t\t\t<table class=\"table table-condensed table-hover\">\n\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t<th>参数值</th>\n\t\t\t\t\t\t\t\t\t\t<th>参数类型</th>\n\t\t\t\t\t\t\t\t\t\t<th>限流阈值</th>\n\t\t\t\t\t\t\t\t\t\t<th>操作</th>\n\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t<tr ng-repeat=\"paramItem in currentRule.rule.paramFlowItemList\">\n\t\t\t\t\t\t\t\t\t\t\t<td><input ng-model=\"paramItem.object\" type=\"text\" class=\"form-control\" placeholder=\"例外项参数\"></td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<p>{{paramItem.classType}}</p>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"number\" ng-model=\"paramItem.count\" class=\"form-control\" placeholder=\"限流阈值\">\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"btn btn-danger\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tng-click=\"removeParamItem(paramItem.object, paramItem.classType)\"><span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"fa fa-trash-o\">&nbsp;删除</span></button>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- exclusion item part end -->\n\t\t\t\t\t<div class=\"form-group text-center\" ng-if=\"paramFlowRuleDialog.supportAdvanced\">\n\t\t\t\t\t\t<a ng-click=\"onOpenAdvanceClick()\" ng-if=\"paramFlowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">高级选项</a>\n\t\t\t\t\t\t<a ng-click=\"onCloseAdvanceClick()\" ng-if=\"!paramFlowRuleDialog.showAdvanceButton\" style=\"cursor: pointer;\">关闭高级选项</a>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t\t<div class=\"separator\"></div>\n\t\t\t<div clss=\"row\" style=\"margin-top: 20px;\">\n\t\t\t\t<button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n\t\t\t\t<button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"saveRule()\">{{paramFlowRuleDialog.confirmBtnText}}</button>\n\t\t\t\t<button ng-if=\"paramFlowRuleDialog.saveAndContinueBtnText\" class=\"btn btn-default\" style=\"float:right; height: 30px;font-size: 12px;\"\n\t\t\t\t ng-click=\"saveRuleAndContinue()\">{{paramFlowRuleDialog.saveAndContinueBtnText}}</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/system-rule-dialog.html",
    "content": "<div>\n  <span class=\"brand\" style=\"font-weight:bold;\">{{systemRuleDialog.title}}</span>\n  <div class=\"card\" style=\"margin-top: 20px;margin-bottom: 10px;\">\n    <div class=\"panel-body\">\n      <div class=\"row\">\n        <form role=\"form\" class=\"form-horizontal\">\n\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值类型</label>\n            <div class=\"col-sm-9\">\n              <div class=\"form-control\" ng-if=\"systemRuleDialog.type == 'edit'\" align=\"center\">\n                <!--avgLoad -->\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;LOAD&nbsp;&nbsp;\n                <!--avgRt -->\n                <input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;RT&nbsp;&nbsp;\n                <!--maxThread -->\n                <input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;线程数&nbsp;&nbsp;\n                <!--qps -->\n                <input type=\"radio\" name=\"grade\" value=\"3\" checked ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\"\n                />&nbsp;入口 QPS\n              </div>\n              <div class=\"form-control highlight-border\" ng-if=\"systemRuleDialog.type == 'add'\" align=\"center\">\n                <!--avgLoad -->\n                <input type=\"radio\" name=\"grade\" value=\"0\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;LOAD&nbsp;&nbsp;\n                <!--avgRt -->\n                <input type=\"radio\" name=\"grade\" value=\"1\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;RT&nbsp;&nbsp;\n                <!--maxThread -->\n                <input type=\"radio\" name=\"grade\" value=\"2\" ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\" />&nbsp;线程数&nbsp;&nbsp;\n                <!--qps -->\n                <input type=\"radio\" name=\"grade\" value=\"3\" checked ng-model='currentRule.grade' ng-disabled=\"systemRuleDialog.type == 'edit'\"\n                />&nbsp;入口 QPS\n              </div>\n            </div>\n          </div>\n          <div class=\"form-group\">\n            <label class=\"col-sm-2 control-label\">阈值</label>\n            <div class=\"col-sm-9\">\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.avgLoad' placeholder=\"[0, ~)的小数\" ng-if=\"currentRule.grade == 0\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.avgRt' placeholder=\"[0, ~)的整数\" ng-if=\"currentRule.grade == 1\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.maxThread' placeholder=\"[0, ~)的整数\" ng-if=\"currentRule.grade == 2\"\n              />\n              <input type='number' class=\"form-control highlight-border\" ng-model='currentRule.qps' placeholder=\"[0, ~)的小数\" ng-if=\"currentRule.grade == 3\"\n              />\n            </div>\n          </div>\n        </form>\n      </div>\n      <div class=\"separator\"></div>\n      <div clss=\"row\" style=\"margin-top: 20px;\">\n        <button class=\"btn btn-outline-danger\" style=\"float:right; height: 30px;font-size: 12px;margin-left: 10px;\" ng-click=\"closeThisDialog()\">取消</button>\n        <button class=\"btn btn-outline-success\" style=\"float:right; height: 30px;font-size: 12px;\" ng-click=\"saveRule()\">{{systemRuleDialog.confirmBtnText}}</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/flow_v1.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增流控规则</button>\n    <!--<a class=\"btn btn-outline-success\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.flow({app: app})\">-->\n      <!--回到集群页面</a>-->\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">流控规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <td style=\"width: 8%;\">\n                  来源应用\n                </td>\n                <td style=\"width: 8%;\">\n                  流控模式\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 6%;\">\n                  阈值\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值模式\n                </td>\n                <td style=\"width: 10%;\">\n                  流控效果\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>\n                <td>\n                  <span ng-if=\"rule.strategy == 0\">直接</span>\n                  <span ng-if=\"rule.strategy == 1\">关联</span>\n                  <span ng-if=\"rule.strategy == 2\">链路</span>\n                </td>\n                <td>\n                  {{rule.grade==0 ? '线程数' : 'QPS'}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td>\n                  <span>{{generateThresholdTypeShow(rule)}}</span>\n                </td>\n                <td>\n                  <span ng-if=\"rule.controlBehavior == 0\">快速失败</span>\n                  <span ng-if=\"rule.controlBehavior == 1\">Warm Up</span>\n                  <span ng-if=\"rule.controlBehavior == 2\">排队等待</span>\n                  <span ng-if=\"rule.controlBehavior == 3\">预热排队</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/flow_v2.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增流控规则\n    </button>\n    <a class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ui-sref=\"dashboard.flowV1({app: app})\">\n      回到单机页面\n    </a>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">流控规则</span>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%\">\n                  资源名\n                </td>\n                <td style=\"width: 10%;\">\n                  来源应用\n                </td>\n                <td style=\"width: 8%;\">\n                  流控模式\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值\n                </td>\n                <td style=\"width: 8%;\">\n                  阈值模式\n                </td>\n                <td style=\"width: 8%;\">\n                  流控效果\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.resource}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{rule.limitApp }}</td>\n                <td>\n                  <span ng-if=\"rule.strategy == 0\">直接</span>\n                  <span ng-if=\"rule.strategy == 1\">关联</span>\n                  <span ng-if=\"rule.strategy == 2\">链路</span>\n                </td>\n                <td>\n                  {{rule.grade == 0 ? '线程数' : 'QPS'}}\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  {{rule.count}}\n                </td>\n                <td>\n                  <span>{{generateThresholdTypeShow(rule)}}</span>\n                </td>\n                <td>\n                  <span ng-if=\"rule.controlBehavior == 0\">快速失败</span>\n                  <span ng-if=\"rule.controlBehavior == 1\">Warm Up</span>\n                  <span ng-if=\"rule.controlBehavior == 2\">排队等待</span>\n                  <span ng-if=\"rule.controlBehavior == 3\">预热排队</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/identity.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"btn-group\" style=\"float:right; margin-right: 30px;\" ng-show=\"!isTreeView\" ng-click=\"treeView()\">\n    <button class=\"btn btn-default-inverse\" style=\"height: 30px;font-size: 12px;\">树状视图</button>\n    <button class=\"btn btn-main\" style=\"height: 30px;font-size: 12px;\">列表视图</button>\n  </div>\n  <div class=\"btn-group\" style=\"float:right; margin-right: 30px;\" ng-show=\"isTreeView\" ng-click=\"listView()\">\n    <button class=\"btn btn-main\" style=\"height: 30px;font-size: 12px;\">树状视图</button>\n    <button class=\"btn btn-default-inverse\" style=\"height: 30px;font-size: 12px;\">列表视图</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">簇点链路</span>\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"queryIdentities()\">刷新</button>\n          <!-- <button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"expandAll()\"\n            ng-if=\"!isExpand && isTreeView\">展开</button>\n          <button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"collapseAll()\"\n            ng-if=\"isExpand && isTreeView\">折叠</button> -->\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\" ng-change=\"searchChange(searchKey)\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table rz-table id=\"identities\" class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <thead>\n                <tr style=\"background: #F3F5F7;\">\n                  <td style=\"width: 40%;\">\n                    资源名\n                  </td>\n                  <td style=\"width: 7%;\">通过QPS</td>\n                  <td style=\"width: 7%;\">拒绝QPS</td>\n                  <td style=\"width: 5%;\">线程数</td>\n                  <td style=\"width: 6%;\">平均RT</td>\n                  <td style=\"width: 6%;\">分钟通过</td>\n                  <td style=\"width: 6%;\">分钟拒绝</td>\n                  <td style=\"width: 23%\">操作</td>\n                </tr>\n                <tr></tr>\n              </thead>\n              <tbody>\n                <tr dir-paginate=\"resource in identities | itemsPerPage: pageSize\" current-page=\"currentPage\" pagination-id=\"entriesPagination\"\n                  data-tt-id=\"{{resource.ttId}}\" data-tt-parent-id=\"{{resource.parentTtId}}\" data-tt-visible=\"{{resource.visible}}\">\n                  <td style=\"white-space: normal; text-align: left;\">\n                    <!--<a ng-click=\"copyIdentity(resource.resource)\"-->\n                    <!--title=\"{{resource.resource}} 单击复制到剪切板\">-->\n                    <span style=\"word-wrap:break-word;word-break:break-all;\">{{resource.resource}}</span>\n                    <!--</a>-->\n                  </td>\n                  <td>{{resource.passQps}}</td>\n                  <td>{{resource.blockQps}}</td>\n                  <td>{{resource.threadNum}}</td>\n                  <td>{{resource.averageRt}}</td>\n                  <td>{{resource.oneMinutePass}}</td>\n                  <td ng-if=\"$index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1)\" ng-init=\"initTreeTable()\">\n                    {{resource.oneMinuteBlock}}</td>\n                  <td ng-if=\"!($index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1))\"> {{resource.oneMinuteBlock}}</td>\n                  <td>\n                    <div class=\"control-group\">\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewFlowRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;流控</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewDegradeRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;降级</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewParamFlowRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;热点</button>\n                      <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"addNewAuthorityRule(resource.resource)\" style=\"font-size: 12px; height:25px;\">\n                        <i class=\"fa fa-plus\"></i>&nbsp;授权</button>\n                    </div>\n                  </td>\n                </tr>\n              </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\">\n            <span>共 {{totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"pageSize\"> 条记录\n            </span>\n            <!--<span>第 {{currentPage}} / {{totalPage}} 页</span>-->\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/login.html",
    "content": "<div class=\"container\">\n    <div class=\"row\" style=\"margin: 200px auto 15px auto; text-align: center;\">\n        <img src=\"../../assets/img/sentinel-logo.png\" alt=\"Sentinel Logo\"\n             style=\"max-height: 25%; max-width: 25%;\">\n        <!-- <h1 id='login_title'>Sentinel 控制台</h1> -->\n    </div>\n    <div class=\"row\">\n        <div class=\"col-md-4\" >\n        </div>\n        <div class=\"col-md-4\">\n            <form class=\"form-horizontal\">\n                <div class=\"form-group\">\n                    <label class=\"col-md-2 control-label\">用户</label>\n                    <div class=\"col-md-9\">\n                        <input class=\"form-control\" type=\"text\" ng-model=\"username\" autofocus=\"autofocus\" required/>\n                    </div>\n                </div>\n\n                <div class=\"form-group\">\n                    <label class=\"col-md-2 control-label\">密码</label>\n                    <div class=\"col-md-9\">\n                        <input class=\"form-control\" type=\"password\" ng-model=\"password\" />\n                    </div>\n                </div>\n                <div class=\"row text-center\" style=\"margin-top: 20px;\">\n                    <button class=\"btn btn-outline-success\" style=\"height: 30px;font-size: 12px;\"\n                            ng-click=\"login()\">登录</button>\n                    <button class=\"btn btn-outline-primary\" style=\"height: 30px;font-size: 12px;margin-left: 10px;\"\n                            type=\"reset\">清空</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/machine.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<!--<div>-->\n<!--<span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length-healthyCount}}</span>-->\n<!--</div>-->\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">机器列表</span>\n          <span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length - healthyCount}}.</span>\n          <input class=\"form-control width-300\" placeholder=\"关键字\" ng-model=\"searchKey\">\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td>机器名</td>\n                <td>IP 地址</td>\n                <td>端口号</td>\n                <td>Sentinel 客户端版本</td>\n                <td>健康状态</td>\n                <td>心跳时间</td>\n                <td>操作</td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"entry in machines | filter : searchKey | itemsPerPage: machinesPageConfig.pageSize \" current-page=\"machinesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{entry.hostname}}</td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">{{entry.ip}}</td>\n                <td> {{entry.port}} </td>\n                <td> {{entry.version}} </td>\n                <td ng-if=\"entry.healthy\">健康</td>\n                <td ng-if=\"!entry.healthy\" style=\"color: red\">失联</td>\n                <td>{{entry.lastHeartbeat | date: 'yyyy/MM/dd HH:mm:ss'}}</td>\n                <td>\n                    <button ng-if=\"!entry.healthy\" class=\"btn btn-xs btn-outline-danger\" style=\"height: 25px; font-size: 12px;\" ng-click=\"removeMachine(entry.ip, entry.port)\">移除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{machinesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"machinesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{machinesPageConfig.currentPageIndex}} / {{machinesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/metric.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">\n            <i class=\"fa fa-sort-amount-asc\" aria-hidden=\"true\" ng-show=\"!isDescOrder\"></i>\n            <i class=\"fa fa-sort-amount-desc\" aria-hidden=\"true\" ng-show=\"isDescOrder\"></i>\n            {{metricTypeDesc}}&nbsp;实时监控\n          </span>\n          <button class=\"btn btn-outline-primary\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"setDescOrder()\"\n            ng-if=\"!isDescOrder\">降序</button>\n          <button class=\"btn btn-outline-primary\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"setAscOrder()\"\n            ng-if=\"isDescOrder\">升序</button>\n\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"serviceQuery\" ng-change=\"searchService()\" style=\"margin-right: 20px;\">\n        </div>\n        <!--.tools-header -->\n        <!--<div id=\"g2container\"></div>-->\n        <div class=\"card-body\">\n          <div class=\"row\" style=\"margin-left: 3px;margin-right: 3px;\">\n            <div class=\"card-body\" style=\"padding: 0px 0px;\">\n              <div class=\"col-md-12\" ng-repeat=\"metric in metrics\" style=\"padding-left: 10px;padding-right: 10px;\">\n\n                <span ng-if=\"$index == metrics.length-1\" ng-init=\"initAllChart()\"></span>\n                <div class=\"card\" style=\"margin-bottom:20px;\" ng-if=\"$index < metrics.length-1\">\n                  <div class=\"tools-header\" style=\"height: 45px;\">\n                    <span class=\"brand\" style=\"font-weight:normal;;word-break:break-all;word-wrap:break-word;\">&nbsp{{metric.resource}}</span>\n                    <a ng-href=\"\">\n                      <i class=\"fa fa-align-justify\"></i>\n                    </a>\n                  </div>\n                  <!-- .tools-header -->\n\n                  <div class=\"card-body\">\n                    <div class=\"row\">\n                      <div class=\"col-md-6\">\n                        <!--<div ng-if=\"$index != metrics.length-1\" id=\"chart{{$index}}\"></div>-->\n                        <!--<div ng-if=\"$index == metrics.length-1\" id=\"chart{{$index}}\" ng-init=\"initAllChart()\"></div>-->\n                        <div id=\"chart{{$index}}\"></div>\n                      </div>\n                      <div class=\"col-md-6\">\n                        <table class=\"table table-hover table-striped table-bordered\">\n                          <thead>\n                            <tr style=\"font-size: 13px;text-align:center;font-weight: bold;\">\n                              <td style=\"word-wrap:break-word;word-break:break-all;\">时间</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"通过 QPS (pass qps)\">通过 QPS</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"拒绝 QPS (blocked qps)\">拒绝QPS</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\" title=\"RT: 平均响应时间（毫秒）\">响应时间（ms）</td>\n                            </tr>\n                          </thead>\n                          <!-- thead -->\n                          <tbody>\n                            <tr style=\"font-size: 12px;text-align:center;\" ng-repeat=\"tableObj in metric.shortData\">\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.timestamp | date: 'HH:mm:ss'}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.passQps | number : 1}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all; \">{{tableObj.blockQps | number : 1}}</td>\n                              <td style=\"word-wrap:break-word;word-break:break-all;\">{{tableObj.rt | number : 1}}</td>\n                            </tr>\n                            <tr style=\"font-size: 12px;text-align:center;\" ng-repeat=\"_tr in [] | range : (6 - metric.shortData.length)\">\n                              <td ng-repeat=\"_td in [] | range : 4\">-</td>\n                            </tr>\n                          </tbody>\n                          <!-- tbody -->\n                        </table>\n                      </div>\n                    </div>\n                  </div>\n                  <!-- .card-body -->\n                </div>\n                <!-- .card -->\n              </div>\n              <!-- .col-md-6 -->\n\n            </div>\n            <!-- .card-body -->\n          </div>\n          <!-- .row -->\n        </div>\n\n        <div style=\"display:none;\">\n          <li dir-paginate=\"empty in emptyObjs| itemsPerPage : servicePageConfig.pageSize\" current-page=\"servicePageConfig.currentPageIndex\"\n            pagination-id=\"servicesPagination\"></li>\n        </div>\n\n        <div class=\"pagination-footer\" style=\"margin-top:5px;\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"servicesPagination\"\n            on-page-change=\"pageChanged(newPageNumber)\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{emptyObjs.length}} 条记录, </span>\n            <span>\n              每页 {{servicePageConfig.pageSize}} 条记录\n              <!--每页-->\n              <!--<input class=\"form-control\" ng-model=\"servicePageConfig.pageSize\" ng-change=\"pageSizeChanged()\">-->\n              <!--条记录,-->\n            </span>\n            <span>第 {{servicePageConfig.currentPageIndex}} / {{servicePageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n        <div class=\"row\" style=\"margin-bottom:20px\"></div>\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/pagination.tpl.html",
    "content": "<div class=\"pagination\" ng-if=\"1 < pages.length || !autoHide\">\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == 1 }\" ng-click=\"setCurrent(1)\">\n    <i class=\"fa fa-angle-double-left\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == 1 }\" ng-click=\"setCurrent(pagination.current - 1)\">\n    <i class=\"fa fa-angle-left\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-repeat=\"pageNumber in pages track by tracker(pageNumber, $index)\" ng-class=\"{ active : pagination.current == pageNumber, disabled : pageNumber == '...' }\"\n    ng-click=\"setCurrent(pageNumber)\">{{ pageNumber }}</a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"\n    ng-click=\"setCurrent(pagination.current + 1)\">\n    <i class=\"fa fa-angle-right\"></i>\n  </a>\n  <a href=\"\" class=\"btn btn-default btn-xs\" ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"\n    ng-click=\"setCurrent(pagination.last)\">\n    <i class=\"fa fa-angle-double-right\"></i>\n  </a>\n</div>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/param_flow.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n    <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n      <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n    </div>\n    <div class=\"col-md-6\" ng-if=\"!loadError\">\n      <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n        <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增热点限流规则</button>\n    </div>\n  </div>\n  \n  <div class=\"separator\"></div>\n  \n  <div class=\"container-fluid\">\n    <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n      <div class=\"col-md-12\">\n        <div class=\"card\">\n          <div class=\"inputs-header\">\n            <span class=\"brand\" style=\"font-size: 13px;\">热点参数限流规则</span>\n            <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n            <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n            <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n              <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n                placeholder=\"机器\"></selectize>\n            </div>\n          </div>\n\n            <!-- error panel -->\n            <div class=\"row clearfix\" ng-if=\"loadError\">\n                <div class=\"col-md-6 col-md-offset-3\">\n                    <div class=\"panel panel-default\">\n                        <div class=\"panel-body\">\n                            <center>\n                                <p>{{loadError.message}}</p>\n                            </center>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Table and pagination start -->\n                <!--.tools-header -->\n                <div class=\"card-body\" style=\"padding: 0px 0px;\" ng-if=\"!loadError\">\n                    <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n                        <thead>\n                        <tr style=\"background: #F3F5F7;\">\n                            <td style=\"width: 40%\">\n                                资源名\n                            </td>\n                            <td style=\"width: 10%;\">\n                                参数索引\n                            </td>\n                            <td style=\"width: 10%;\">\n                                流控模式\n                            </td>\n                            <td style=\"width: 10%;\">\n                                阈值\n                            </td>\n                            <td style=\"width: 8%;\">\n                                是否集群\n                            </td>\n                            <td style=\"width: 10%;\">\n                                例外项数目\n                            </td>\n                            <td style=\"width: 12%;\">\n                                操作\n                            </td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <tr dir-paginate=\"ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                            pagination-id=\"entriesPagination\">\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.resource}}</td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">{{ruleEntity.rule.paramIdx}}</td>\n                            <td>\n                                {{ruleEntity.rule.grade == 1 ? 'QPS' : '未知'}}\n                            </td>\n                            <td style=\"word-wrap:break-word;word-break:break-all;\">\n                                {{ruleEntity.rule.count}}\n                            </td>\n                            <td>\n                                <span ng-if=\"ruleEntity.rule.clusterMode\">是</span>\n                                <span ng-if=\"!ruleEntity.rule.clusterMode\">否</span>\n                            </td>\n                            <td>\n                                {{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}\n                            </td>\n                            <td>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                                <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(ruleEntity)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                            </td>\n                        </tr>\n                        </tbody>\n                    </table>\n                </div>\n                <!-- .card-body -->\n                <div class=\"pagination-footer\" ng-if=\"!loadError\">\n                    <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n                                             on-page-change=\"\">\n                    </dir-pagination-controls>\n                    <div class=\"tools\" style=\"\">\n                        <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n                        <span>每页 <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录</span>\n                        <!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->\n                    </div>\n                    <!-- .tools -->\n                </div>\n                <!-- pagination-footer -->\n            <!-- Table and pagination end -->\n\n        </div>\n        <!-- .card -->\n      </div>\n      <!-- .col-md-12 -->\n    </div>\n    <!-- -->\n  </div>\n  <!-- .container-fluid -->\n  "
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/system.html",
    "content": "<div class=\"row\" style=\"margin-left: 1px; margin-top:10px; height: 50px;\">\n  <div class=\"col-md-6\" style=\"margin-bottom: 10px;\">\n    <span style=\"font-size: 30px;font-weight: bold;\">{{app}}</span>\n  </div>\n  <div class=\"col-md-6\">\n    <button class=\"btn btn-default-inverse\" style=\"float: right; margin-right: 10px;\" ng-disabled=\"!macInputModel\" ng-click=\"addNewRule()\">\n      <i class=\"fa fa-plus\"></i>&nbsp;&nbsp;新增系统规则</button>\n  </div>\n</div>\n\n<div class=\"separator\"></div>\n\n<div class=\"container-fluid\">\n  <div class=\"row\" style=\"margin-top: 20px; margin-bottom: 20px;\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"inputs-header\">\n          <span class=\"brand\" style=\"font-size: 13px;\">系统规则</span>\n          <!--<button class=\"btn btn-danger\" style=\"float: right;margin-right: 10px;height: 30px;font-size: 12px;\" ng-click=\"disableAll()\">全部禁用</button>-->\n          <button class=\"btn btn-primary\" style=\"float: right; margin-right: 10px; height: 30px;font-size: 12px;\" ng-click=\"getMachineRules()\">刷新</button>\n          <input class=\"form-control witdh-200\" placeholder=\"关键字\" ng-model=\"searchKey\">\n          <div class=\"control-group\" style=\"float:right;margin-right: 10px;margin-bottom: -10px;\">\n            <selectize id=\"gsInput\" class=\"selectize-input-200\" config=\"macsInputConfig\" options=\"macsInputOptions\" ng-model=\"macInputModel\"\n              placeholder=\"机器\"></selectize>\n          </div>\n        </div>\n\n        <!--.tools-header -->\n        <div class=\"card-body\" style=\"padding: 0px 0px;\">\n          <table class=\"table\" style=\"border-left: none; border-right:none;margin-top: 10px;\">\n            <thead>\n              <tr style=\"background: #F3F5F7;\">\n                <td style=\"width: 40%;\">\n                  阈值类型\n                </td>\n                <td style=\"width: 40%;\">\n                  单机阈值\n                </td>\n                <td style=\"width: 12%;\">\n                  操作\n                </td>\n              </tr>\n            </thead>\n            <tbody>\n              <tr dir-paginate=\"rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize \" current-page=\"rulesPageConfig.currentPageIndex\"\n                pagination-id=\"entriesPagination\">\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  <span ng-if=\"rule.avgLoad >= 0\">LOAD</span>\n                  <span ng-if=\"rule.avgRt >= 0\">RT</span>\n                  <span ng-if=\"rule.maxThread >= 0\">线程数</span>\n                  <span ng-if=\"rule.qps >= 0\">QPS</span>\n                </td>\n                <td style=\"word-wrap:break-word;word-break:break-all;\">\n                  <span ng-if=\"rule.avgLoad >= 0\">{{rule.avgLoad}}</span>\n                  <span ng-if=\"rule.avgRt >= 0\">{{rule.avgRt}}</span>\n                  <span ng-if=\"rule.maxThread >= 0\">{{rule.maxThread}}</span>\n                  <span ng-if=\"rule.qps >= 0\">{{rule.qps}}</span>\n                </td>\n                <td>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"editRule(rule)\" style=\"font-size: 12px; height:25px;\">编辑</button>\n                  <button class=\"btn btn-xs btn-default\" type=\"button\" ng-click=\"deleteRule(rule)\" style=\"font-size: 12px; height:25px;\">删除</button>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n        <!-- .card-body -->\n        <div class=\"pagination-footer\">\n          <dir-pagination-controls boundary-links=\"true\" template-url=\"app/views/pagination.tpl.html\" pagination-id=\"entriesPagination\"\n            on-page-change=\"\">\n          </dir-pagination-controls>\n          <div class=\"tools\" style=\"\">\n            <span>共 {{rulesPageConfig.totalCount}} 条记录, </span>\n            <span>\n              每页\n              <input class=\"form-control\" ng-model=\"rulesPageConfig.pageSize\"> 条记录,\n            </span>\n            <span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>\n          </div>\n          <!-- .tools -->\n        </div>\n        <!-- pagination-footer -->\n      </div>\n      <!-- .card -->\n    </div>\n    <!-- .col-md-12 -->\n  </div>\n  <!-- -->\n</div>\n<!-- .container-fluid -->\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/dist/css/app.css",
    "content": ".chat,.timeline{list-style:none}#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner .4s linear infinite;-moz-animation:loading-bar-spinner .4s linear infinite;-ms-animation:loading-bar-spinner .4s linear infinite;-o-animation:loading-bar-spinner .4s linear infinite;animation:loading-bar-spinner .4s linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,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}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:table-cell;vertical-align:middle;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch span::before{content:\"\\200b\"}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0);visibility:hidden}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{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)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}.btn:active,.btn:focus,.selectize-input>input:focus{outline:0!important}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}.datetimepicker{border-radius:4px;direction:ltr;display:block;margin-top:1px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:320px}.datetimepicker>div{display:none}.datetimepicker .hour,.datetimepicker .minute{height:34px;line-height:34px;margin:0;width:25%}.datetimepicker .table{margin:0}.datetimepicker .table td,.datetimepicker .table th{border:0;border-radius:4px;height:20px;text-align:center}.datetimepicker .day:hover,.datetimepicker .hour:hover,.datetimepicker .left:hover,.datetimepicker .minute:hover,.datetimepicker .right:hover,.datetimepicker .switch:hover{background:#eee;cursor:pointer}.datetimepicker .disabled,.datetimepicker .disabled:hover{background:0 0;color:#ebebeb;cursor:default}.datetimepicker .active,.datetimepicker .active.disabled,.datetimepicker .active.disabled:hover,.datetimepicker .active:hover{background-color:#04c;background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;color:#fff;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#08c', endColorstr='#04c', GradientType=0);text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datetimepicker .current,.datetimepicker .current.disabled,.datetimepicker .current.disabled:hover,.datetimepicker .current:hover{background-color:#e5e5e5}.datetimepicker .active.active,.datetimepicker .active.disabled,.datetimepicker .active.disabled.active,.datetimepicker .active.disabled.disabled,.datetimepicker .active.disabled:active,.datetimepicker .active.disabled:hover,.datetimepicker .active.disabled:hover.active,.datetimepicker .active.disabled:hover.disabled,.datetimepicker .active.disabled:hover:active,.datetimepicker .active.disabled:hover:hover,.datetimepicker .active:active,.datetimepicker .active:hover,.datetimepicker .active:hover.active,.datetimepicker .active:hover.disabled,.datetimepicker .active:hover:active,.datetimepicker .active:hover:hover,.datetimepicker span.active.disabled:hover[disabled],.datetimepicker span.active.disabled[disabled],.datetimepicker span.active:hover[disabled],.datetimepicker span.active[disabled],.datetimepicker td.active.disabled:hover[disabled],.datetimepicker td.active.disabled[disabled],.datetimepicker td.active:hover[disabled],.datetimepicker td.active[disabled]{background-color:#04c}.datetimepicker span{border-radius:4px;cursor:pointer;display:block;float:left;height:54px;line-height:54px;margin:1%;width:23%}.datetimepicker span:hover{background:#eee}.datetimepicker .future,.datetimepicker .past{color:#999}.ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de}table.rz-table{table-layout:fixed;border-collapse:collapse}table.rz-table th{position:relative;min-width:25px}table.rz-table th .rz-handle{width:10px;height:100%;position:absolute;top:0;right:0;cursor:ew-resize!important}table.rz-table th .rz-handle.rz-handle-active{border-right:1px dotted #000}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;-webkit-box-shadow:inset 0 0 12px 4px #fff;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after{content:'!';visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{-webkit-box-shadow:0 2px 5px rgba(0,0,0,.2);box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#303030;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove,.selectize-input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:inline-block}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;padding:2px 0 0;border-left:1px solid #d0d0d0;-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0 0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.selectize-control,.selectize-input{position:relative}.selectize-dropdown,.selectize-input,.selectize-input input{color:#303030;font-family:inherit;font-size:13px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid #d0d0d0;padding:8px;width:100%;overflow:hidden;z-index:1;box-sizing:border-box;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.selectize-control.multi .selectize-input.has-items{padding:6px 8px 3px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input.focus{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.selectize-input.dropdown-active{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:baseline;display:-moz-inline-stack;display:inline-block;zoom:1}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#f2f2f2;color:#303030;border:0 solid #d0d0d0}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0 2px 0 0!important;text-indent:0!important;border:0!important;background:0 0!important;line-height:inherit!important;-webkit-user-select:auto!important;-webkit-box-shadow:none!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input::after{content:' ';display:block;clear:left}.selectize-input.dropdown-active::before{content:' ';display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;border:1px solid #d0d0d0;background:#fff;margin:-1px 0 0;border-top:0 none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.1);-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown .option{padding:5px 8px}.selectize-dropdown .option,.selectize-dropdown [data-disabled],.selectize-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.selectize-dropdown [data-selectable].option{opacity:1}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.selectize-dropdown .optgroup-header{color:#303030;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f5fafd;color:#495c68}.selectize-dropdown .active.create{color:#495c68}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:' ';display:block;position:absolute;top:50%;right:15px;margin-top:-3px;width:0;height:0;border-style:solid;border-width:5px 5px 0;border-color:grey transparent transparent}.selectize-control.single .selectize-input.dropdown-active:after{margin-top:-4px;border-width:0 5px 5px;border-color:transparent transparent grey}.selectize-control.rtl.single .selectize-input:after{left:15px;right:auto}.selectize-control.rtl .selectize-input>input{margin:0 4px 0 -2px!important}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}/*!\n * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)\n * Code licensed under the Apache License v2.0.\n * For details, see http://www.apache.org/licenses/LICENSE-2.0.\n */body{background-color:#f8f8f8}.example{padding:.625rem 1.825rem .625rem 2.5rem;border:1px dashed #ccc;position:relative;margin:0 0 .625rem;background-color:#fff}dl dd,dl dt{line-height:1.25rem}dl dt{font-style:normal;font-weight:700}dl dd{margin-left:.9375rem}dl.horizontal dt{float:left;width:10rem;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}dl.horizontal dd{margin-left:11.25rem}#wrapper{width:100%}#page-wrapper{min-height:568px;background-color:#fff}@media(min-width:768px){#page-wrapper{position:inherit;margin:0 0 0 250px;padding:0 30px;border-left:1px solid #e7e7e7}}.navbar-top-links{margin-right:0}.navbar-top-links li{display:inline-block}.flot-chart,.navbar-top-links .dropdown-menu li{display:block}.navbar-top-links li:last-child{margin-right:15px}.navbar-top-links li a{padding:15px;min-height:50px}.navbar-top-links .dropdown-menu li:last-child{margin-right:0}.navbar-top-links .dropdown-menu li a{padding:3px 20px;min-height:0}.navbar-top-links .dropdown-menu li a div{white-space:normal}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{width:310px;min-width:0}.navbar-top-links .dropdown-messages{margin-left:5px}.navbar-top-links .dropdown-tasks{margin-left:-59px}.navbar-top-links .dropdown-alerts{margin-left:-123px}.navbar-top-links .dropdown-user{right:0;left:auto}.sidebar .sidebar-search{padding:15px}.sidebar ul li{border-bottom:1px solid #e7e7e7}.sidebar ul li a.active{background-color:#fff;color:#fff}.sidebar .arrow{float:right}.sidebar .fa.arrow:before{content:\"\\f104\"}.sidebar .active>a>.fa.arrow:before{content:\"\\f107\"}.sidebar .nav-second-level li,.sidebar .nav-third-level li{border-bottom:0!important}.sidebar .nav-second-level li a{padding-left:37px}.sidebar .nav-third-level li a{padding-left:52px}@media(min-width:768px){.sidebar{z-index:1;position:absolute;width:250px;margin-top:51px}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{margin-left:auto}}.btn-outline{color:inherit;background-color:transparent;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.chat{margin:0;padding:0}.chat li{margin-bottom:10px;padding-bottom:5px;border-bottom:1px dotted #999}.chat li.left .chat-body{margin-left:60px}.chat li.right .chat-body{margin-right:60px}.chat li .chat-body p{margin:0}.chat .glyphicon,.panel .slidedown .glyphicon{margin-right:5px}.chat-panel .panel-body{height:350px;overflow-y:scroll}.login-panel{margin-top:25%}.flot-chart{height:400px}.flot-chart-content{width:100%;height:100%}.dataTables_wrapper{position:relative;clear:both}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background:0 0}table.dataTable thead .sorting_asc:after{content:\"\\f0de\";float:right;font-family:fontawesome}table.dataTable thead .sorting_desc:after{content:\"\\f0dd\";float:right;font-family:fontawesome}table.dataTable thead .sorting:after{content:\"\\f0dc\";float:right;font-family:fontawesome;color:rgba(50,50,50,.5)}.btn-circle{width:30px;height:30px;padding:6px 0;border-radius:15px;text-align:center;font-size:12px;line-height:1.428571429}.btn-circle.btn-lg{width:50px;height:50px;padding:10px 16px;border-radius:25px;font-size:18px;line-height:1.33}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 16px;border-radius:35px;font-size:24px;line-height:1.33}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;border:1px solid #ddd;background-color:#eee!important}.show-grid{margin:15px 0}.huge{font-size:40px}.panel-green{border-color:#5cb85c}.panel-green .panel-heading{border-color:#5cb85c;color:#fff;background-color:#5cb85c}.panel-green a{color:#5cb85c}.panel-green a:hover{color:#3d8b3d}.panel-red{border-color:#d9534f}.panel-red .panel-heading{border-color:#d9534f;color:#fff;background-color:#d9534f}.panel-red a{color:#d9534f}.panel-red a:hover{color:#b52b27}.panel-yellow{border-color:#f0ad4e}.panel-yellow .panel-heading{border-color:#f0ad4e;color:#fff;background-color:#f0ad4e}.panel-yellow a{color:#f0ad4e}.panel-yellow a:hover{color:#df8a13}.timeline{position:relative;padding:20px 0}.timeline:before{content:\" \";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background-color:#eee}.timeline>li{position:relative;margin-bottom:20px}.timeline>li:after,.timeline>li:before{content:\" \";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{float:left;position:relative;width:46%;padding:20px;border:1px solid #d4d4d4;border-radius:2px;-webkit-box-shadow:0 1px 6px rgba(0,0,0,.175);box-shadow:0 1px 6px rgba(0,0,0,.175)}.timeline>li>.timeline-panel:before{content:\" \";display:inline-block;position:absolute;top:26px;right:-15px;border-top:15px solid transparent;border-right:0 solid #ccc;border-bottom:15px solid transparent;border-left:15px solid #ccc}.timeline>li>.timeline-panel:after{content:\" \";display:inline-block;position:absolute;top:27px;right:-14px;border-top:14px solid transparent;border-right:0 solid #fff;border-bottom:14px solid transparent;border-left:14px solid #fff}.timeline>li>.timeline-badge{z-index:100;position:absolute;top:16px;left:50%;width:50px;height:50px;margin-left:-25px;border-radius:50%;text-align:center;font-size:1.4em;line-height:50px;color:#fff;background-color:#999}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline-badge.primary{background-color:#2e6da4!important}.timeline-badge.success{background-color:#3f903f!important}.timeline-badge.warning{background-color:#f0ad4e!important}.timeline-badge.danger{background-color:#d9534f!important}.timeline-badge.info{background-color:#5bc0de!important}.timeline-title{margin-top:0;color:inherit}.timeline-body>p,.timeline-body>ul{margin-bottom:0}.timeline-body>p+p{margin-top:5px}@media(max-width:767px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 90px);width:-moz-calc(100% - 90px);width:-webkit-calc(100% - 90px);float:right}ul.timeline>li>.timeline-badge{top:16px;left:15px;margin-left:0}ul.timeline>li>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}ul.timeline>li>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}}.header,.jumbotron{border-bottom:1px solid #e5e5e5}.btn{height:32px}.width-200{max-width:200px}.width-300,.witdh-300{max-width:300px}body{padding:0}.footer,.header,.marketing{padding-left:15px;padding-right:15px}.header{margin-bottom:10px}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.card .detail,.card .detail-brand{line-height:98px;text-align:center}.footer{padding-top:19px;color:#777;border-top:1px solid #e5e5e5}.container-narrow>hr{margin:30px 0}.jumbotron{text-align:center}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing{margin:40px 0}.marketing p+h4{margin-top:28px}@media screen and (min-width:768px){.container{width:inherit;margin-left:60px;margin-right:5px}.footer,.header,.marketing{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}.navbar-inverse .navbar-nav>li>a{color:#b0ddce;font-size:15px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{background-color:#1b926c}@media (min-width:900px){.navbar-right,.navbar-right~.navbar-right{margin-right:0}.navbar-left{float:left!important}.navbar-right{float:right!important}}.dropdown-menu{min-width:100px!important}.nav-sidebar li.active a{background:#DDD}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background:#1d9d74;color:#fff}.broadcast-message,.broadcast-message-preview{padding:10px;text-align:center;background:#555;color:#BBB;margin-top:50px}.card{position:relative;border:1px solid #d9d9d9;color:#666;background-color:#fff;width:100%;border-radius:5px}.card .card-header,.tools-header{border-top-left-radius:4px;border-top-right-radius:4px}.card .card-header{padding:9px 0;height:40px;background:#555;color:#fff;text-align:center}.card .card-body{padding:12px 10px}.card .card-footer{height:20px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .detail-brand{float:left;width:30%;font-size:30px;color:#fff}.card .default{background:#1d9d74}.card .info{background:#6EBEE7}.card .warn{background:#ED7F54}.card .danger{background:#6583BE}.card .detail .text-default{color:#1d9d74}.card .detail .text-info{color:#6EBEE7}.card .detail .text-warn{color:#ED7F54}.card .detail .text-danger{color:#6583BE}.card .detail{float:right;width:70%}.card .detail .text{font-size:12px}.card .detail .number{font-size:30px;font-weight:500}.h100{height:100px}.inline{display:inline}.separator{height:1px;background-color:#e5e5e5;margin-top:10px}.card>.card-body>table>tbody>tr>td,.card>.card-body>table>thead>tr>td{word-wrap:break-word;word-break:break-all}.card>.card-body>table>thead>tr>td{font-weight:500;font-size:13px;text-align:center}.card>.card-body>table>thead>tr>td>span{font-weight:500;font-size:10px}.card>.card-body>table>tbody>tr>td{font-size:12px;text-align:center}.card>.card-body>table>tbody>tr>td>a{color:#666}.thumbnails>.card>.card-body>table>tbody>tr>td,.thumbnails>.card>.card-body>table>thead>tr>td{font-size:12px;color:#777;word-wrap:break-word;word-break:break-all}.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(n+2){text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(n+2){font-weight:700;text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(1),.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(1){text-align:left}.tools-header{background:#f5f5f5;padding:9px 0;height:40px}.tools-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.tools-header .brand>a{color:#666}.tools-header>a,.tools-header>button,.tools-header>select{float:right;max-width:80px;margin:1px 10px;height:25px;padding:0 10px;line-height:25px;color:#666}.tools-header .paged{margin-right:0}.btn.btn-danger-tag{color:#fff;background-color:#d9534f;border-color:#d43f3a;line-height:1px;font-size:11px;padding:4px}.btn.btn-danger{color:#333;background-color:#fff;border-color:#ccc}.btn.btn-danger:active,.btn.btn-danger:focus,.btn.btn-danger:hover{color:#d9534f;border-color:#d9534f;background:#fff}.form-control{height:32px}.input-label:before{display:inline-block;content:\"*\";color:#f44336;font-family:SimSun;font-size:12px;-webkit-transform:TranslateX(-10px);-ms-transform:TranslateX(-10px);transform:TranslateX(-10px)}.badge-main,.label.label-main{color:#fff;background-color:#1d9d74;border-color:#1d9d74}.bootstrap-tagsinput{background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);display:inline-block;padding:4px 6px;color:#555;vertical-align:middle;border-radius:4px;width:85%;height:100px;line-height:20px;cursor:text}.bootstrap-tagsinput>.dropdown-menu{min-width:40px;font-size:12px}.bootstrap-tagsinput>.dropdown-menu>.active>a,.bootstrap-tagsinput>.dropdown-menu>.active>a:focus,.bootstrap-tagsinput>.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-o-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#1d9d74),to(#1d9d74));background-image:linear-gradient(to bottom,#1d9d74 0,#1d9d74 100%);filter:progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);background-repeat:repeat-x;color:#fff;text-decoration:none;outline:0;background-color:#1d9d74}.inputs-header{padding:9px 0;height:50px;border-top-left-radius:4px;border-top-right-radius:4px}.inputs-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.inputs-header .brand>a{color:#666}.inputs-header>input{float:right;margin:1px 10px;height:30px;padding:0 10px;color:#666}.inputs-header>a{float:right;margin:1px 10px;height:30px;padding:5 5px}.inputs-header>select{float:right;max-width:80px;margin:1px 10px;padding:0 10px;color:#666;height:25px;font-size:12px}.witdh-150{max-width:150px}.witdh-200{max-width:200px}.card.highlight{border-color:#d9534f}.card .pagination-footer{height:40px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .pagination-footer .tools{font-size:12px;margin:11px 20px 11px 0;float:right;display:inline}.card>.pagination-footer>.tools>span>input{height:25px;max-width:50px;display:inline}.pagination{display:inline-block;padding-left:0;margin:8px 0;float:right;border-radius:4px}.pagination>a{margin-right:5px;height:28px;width:28px;padding:5px 0}.datepicker>.table>tbody>tr>td,.datepicker>.table>thead>tr>td,.timepicker>.table>tbody>tr>td,.timepicker>.table>thead>tr>td{padding:5px 3px}.datepicker>.table>tbody>tr>td>.btn,.datepicker>.table>thead>tr>td>.btn,.timepicker>.table>tbody>tr>td>.btn,.timepicker>.table>thead>tr>td>.btn{border:1px solid #FFFDFD}.datepicker>.table>tbody>tr>td>.btn-default:active,.datepicker>.table>tbody>tr>td>.btn-default:focus,.datepicker>.table>tbody>tr>td>.btn-default:hover,.datepicker>.table>thead>tr>td>.btn-default:active,.datepicker>.table>thead>tr>td>.btn-default:focus,.datepicker>.table>thead>tr>td>.btn-default:hover,.timepicker>.table>tbody>tr>td>.btn-default:active,.timepicker>.table>tbody>tr>td>.btn-default:focus,.timepicker>.table>tbody>tr>td>.btn-default:hover,.timepicker>.table>thead>tr>td>.btn-default:active,.timepicker>.table>thead>tr>td>.btn-default:focus,.timepicker>.table>thead>tr>td>.btn-default:hover{color:#1d9d74;border-color:#1d9d74;background:#fff}.datepicker>.table>tbody>tr>td>a,.datepicker>.table>thead>tr>td>a,.timepicker>.table>tbody>tr>td>a,.timepicker>.table>thead>tr>td>a{height:25px;width:25px;padding:3px 0}.datepicker>.table>tbody>tr:first-child>td>a{padding:4px 0}.datepicker>.table>tbody>tr>td>a.btn.active,.datepicker>.table>thead>tr>td>a.btn.active,.timepicker>.table>tbody>tr>td>a.btn.active,.timepicker>.table>thead>tr>td>a.btn.active{color:#1d9d74;border-color:#1d9d74;background:#fff;box-shadow:inset 0 0 0 rgba(0,0,0,.125)}.datepicker>.table>thead>tr>td:not(:first-child):last-child>a,.timepicker>.table>thead>tr>td:not(:first-child):last-child>a{height:25px;width:50px;padding:5px 0}.datepicker>.table>tbody>tr>td>a,.timepicker>.table>tbody>tr>td>a{margin-left:8px}.sortorder:after{content:'\\25b2'}.sortorder.reverse:after{content:'\\25bc'}.input-control select{-moz-appearance:none;-webkit-appearance:none;appearance:none;position:relative;border:1px solid #d9d9d9;width:100%;height:100%;padding:.3125rem;z-index:0}.navbar-inverse{background-color:#337ab7;border-color:#337ab7}.sidebar{z-index:1;width:220px;top:0;left:0;height:100%}#page-wrapper{position:inherit;margin:70px 0 0 220px;padding:12px 30px;border-left:0 solid #e7e7e7}.sidebar .sidebar-nav.navbar-collapse{background-color:#F5F5F5;position:relative;color:#000;width:100%;padding:0;margin:0;list-style:none inside}.sidebar a{color:#555}.sidebar ul li:hover{color:red}.form-control{border-radius:8px}.form-control:focus,.highlight-border{border-color:#337ab7;box-shadow:0 0 0 rgba(0,0,0,.075) inset,0 0 0 rgba(29,157,116,1)}.btn-outline-primary.focus,.btn-outline-primary:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.browsehappy{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.btn.btn-main{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default-inverse,.btn-default-inverse:focus,.btn-default-inverse:hover,.btn-default:active{color:#337ab7;border-color:#337ab7;background:#fff}.btn-danger-inverse,.btn-danger-inverse:focus,.btn-danger-inverse:hover,.btn-danger:active{color:#d9534f;border-color:#d9534f;background:#fff}.btn-tab-active,.btn-tab-active:focus,.btn-tab-active:hover,.btn-tab-default:active,.btn-tab-default:focus,.btn-tab-default:hover{color:#337ab7;border-color:#337ab7;background:#fff;font-weight:600}.btn-tab-default{color:#777;background:#fff;font-weight:600}.pagination>.btn.active{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default:active,.btn-default:focus,.btn-default:hover{color:#337ab7;border-color:#337ab7;background:#fff}.bootstrap-switch.bootstrap-switch-on{border-color:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#337ab7}.selectize-input-200>.selectize-input{min-width:200px;border-color:#337ab7}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-secondary.focus,.btn-outline-secondary:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-success.focus,.btn-outline-success:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-info.focus,.btn-outline-info:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-warning.focus,.btn-outline-warning:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-danger.focus,.btn-outline-danger:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-light.focus,.btn-outline-light:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-dark.focus,.btn-outline-dark:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/dist/js/app.js",
    "content": "\"use strict\";var app;angular.module(\"sentinelDashboardApp\",[\"oc.lazyLoad\",\"ui.router\",\"ui.bootstrap\",\"angular-loading-bar\",\"ngDialog\",\"ui.bootstrap.datetimepicker\",\"ui-notification\",\"rzTable\",\"angular-clipboard\",\"selectize\",\"angularUtils.directives.dirPagination\"]).factory(\"AuthInterceptor\",[\"$window\",\"$state\",function(r,t){return{responseError:function(e){return 401==e.status&&(r.localStorage.removeItem(\"session_sentinel_admin\"),t.go(\"login\")),e},response:function(e){return e},request:function(e){return e},requestError:function(e){return e}}}]).config([\"$stateProvider\",\"$urlRouterProvider\",\"$ocLazyLoadProvider\",\"$httpProvider\",function(e,r,t,a){a.interceptors.push(\"AuthInterceptor\"),t.config({debug:!1,events:!0}),r.otherwise(\"/dashboard/home\"),e.state(\"login\",{url:\"/login\",templateUrl:\"app/views/login.html\",controller:\"LoginCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/login.js\"]})}]}}).state(\"dashboard\",{url:\"/dashboard\",templateUrl:\"app/views/dashboard/main.html\",resolve:{loadMyDirectives:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/directives/header/header.js\",\"app/scripts/directives/sidebar/sidebar.js\",\"app/scripts/directives/sidebar/sidebar-search/sidebar-search.js\"]})}]}}).state(\"dashboard.home\",{url:\"/home\",templateUrl:\"app/views/dashboard/home.html\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/main.js\"]})}]}}).state(\"dashboard.flowV1\",{templateUrl:\"app/views/flow_v1.html\",url:\"/flow/:app\",controller:\"FlowControllerV1\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/flow_v1.js\"]})}]}}).state(\"dashboard.flow\",{templateUrl:\"app/views/flow_v2.html\",url:\"/v2/flow/:app\",controller:\"FlowControllerV2\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/flow_v2.js\"]})}]}}).state(\"dashboard.paramFlow\",{templateUrl:\"app/views/param_flow.html\",url:\"/paramFlow/:app\",controller:\"ParamFlowController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/param_flow.js\"]})}]}}).state(\"dashboard.clusterAppAssignManage\",{templateUrl:\"app/views/cluster_app_assign_manage.html\",url:\"/cluster/assign_manage/:app\",controller:\"SentinelClusterAppAssignManageController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_assign_manage.js\"]})}]}}).state(\"dashboard.clusterAppServerList\",{templateUrl:\"app/views/cluster_app_server_list.html\",url:\"/cluster/server/:app\",controller:\"SentinelClusterAppServerListController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_server_list.js\"]})}]}}).state(\"dashboard.clusterAppClientList\",{templateUrl:\"app/views/cluster_app_client_list.html\",url:\"/cluster/client/:app\",controller:\"SentinelClusterAppTokenClientListController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_app_token_client_list.js\"]})}]}}).state(\"dashboard.clusterSingle\",{templateUrl:\"app/views/cluster_single_config.html\",url:\"/cluster/single/:app\",controller:\"SentinelClusterSingleController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/cluster_single.js\"]})}]}}).state(\"dashboard.authority\",{templateUrl:\"app/views/authority.html\",url:\"/authority/:app\",controller:\"AuthorityRuleController\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/authority.js\"]})}]}}).state(\"dashboard.degrade\",{templateUrl:\"app/views/degrade.html\",url:\"/degrade/:app\",controller:\"DegradeCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/degrade.js\"]})}]}}).state(\"dashboard.system\",{templateUrl:\"app/views/system.html\",url:\"/system/:app\",controller:\"SystemCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/system.js\"]})}]}}).state(\"dashboard.machine\",{templateUrl:\"app/views/machine.html\",url:\"/app/:app\",controller:\"MachineCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/machine.js\"]})}]}}).state(\"dashboard.identity\",{templateUrl:\"app/views/identity.html\",url:\"/identity/:app\",controller:\"IdentityCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/identity.js\"]})}]}}).state(\"dashboard.metric\",{templateUrl:\"app/views/metric.html\",url:\"/metric/:app\",controller:\"MetricCtl\",resolve:{loadMyFiles:[\"$ocLazyLoad\",function(e){return e.load({name:\"sentinelDashboardApp\",files:[\"app/scripts/controllers/metric.js\"]})}]}})}]),(app=angular.module(\"sentinelDashboardApp\")).filter(\"range\",[function(){return function(e,r){if(isNaN(r)||r<=0)return[];e=[];for(var t=1;t<=r;t++)e.push(t);return e}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"AuthService\",[\"$http\",function(r){this.login=function(e){return r({url:\"/auth/login\",params:e,method:\"POST\"})},this.logout=function(){return r({url:\"/auth/logout\",method:\"POST\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"AppService\",[\"$http\",function(e){this.getApps=function(){return e({url:\"app/briefinfos.json\",method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"FlowServiceV1\",[\"$http\",function(a){function r(e){return void 0===e||\"\"===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:\"/v1/flow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){e.resource,e.limitApp,e.grade,e.count,e.strategy,e.refResource,e.controlBehavior,e.warmUpPeriodSec,e.maxQueueingTimeMs,e.app,e.ip,e.port;return a({url:\"/v1/flow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,strategy:e.strategy,refResource:e.refResource,controlBehavior:e.controlBehavior,warmUpPeriodSec:e.warmUpPeriodSec,maxQueueingTimeMs:e.maxQueueingTimeMs};return a({url:\"/v1/flow/save.json\",params:r,method:\"PUT\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/v1/flow/delete.json\",params:r,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.count||e.count<0?(alert(\"限流阈值必须大于等于 0\"),!1):void 0===e.strategy||e.strategy<0?(alert(\"无效的流控模式\"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&\"\"!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert(\"无效的流控整形方式\"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert(\"预热时长必须大于 0\"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert(\"排队超时时间必须大于 0\"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert(\"集群限流配置不正确\"),!1):(alert(\"请填写关联资源或入口\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"FlowServiceV2\",[\"$http\",function(a){function r(e){return void 0===e||\"\"===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:\"/v2/flow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){return a({url:\"/v2/flow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/v2/flow/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/v2/flow/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.count||e.count<0?(alert(\"限流阈值必须大于等于 0\"),!1):void 0===e.strategy||e.strategy<0?(alert(\"无效的流控模式\"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&\"\"!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert(\"无效的流控整形方式\"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert(\"预热时长必须大于 0\"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert(\"排队超时时间必须大于 0\"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert(\"集群限流配置不正确\"),!1):(alert(\"请填写关联资源或入口\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"DegradeService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"degrade/rules.json\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,count:e.count,timeWindow:e.timeWindow,grade:e.grade,app:e.app,ip:e.ip,port:e.port};return a({url:\"/degrade/new.json\",params:r,method:\"GET\"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,timeWindow:e.timeWindow};return a({url:\"/degrade/save.json\",params:r,method:\"GET\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/degrade/delete.json\",params:r,method:\"GET\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.grade||e.grade<0?(alert(\"未知的降级策略\"),!1):void 0===e.count||\"\"===e.count||e.count<0?(alert(\"降级阈值不能为空或小于 0\"),!1):void 0===e.timeWindow||\"\"===e.timeWindow||e.timeWindow<=0?(alert(\"降级时间窗口必须大于 0\"),!1):!(1==e.grade&&1<e.count)||(alert(\"异常比率超出范围：[0.0 - 1.0]\"),!1)}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"SystemService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"system/rules.json\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.newRule=function(e){var r={app:e.app,ip:e.ip,port:e.port};return 0==e.grade?r.avgLoad=e.avgLoad:1==e.grade?r.avgRt=e.avgRt:2==e.grade?r.maxThread=e.maxThread:3==e.grade&&(r.qps=e.qps),a({url:\"/system/new.json\",params:r,method:\"GET\"})},this.saveRule=function(e){var r={id:e.id};return 0==e.grade?r.avgLoad=e.avgLoad:1==e.grade?r.avgRt=e.avgRt:2==e.grade?r.maxThread=e.maxThread:3==e.grade&&(r.qps=e.qps),a({url:\"/system/save.json\",params:r,method:\"GET\"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:\"/system/delete.json\",params:r,method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"MachineService\",[\"$http\",\"$httpParamSerializerJQLike\",function(a,o){this.getAppMachines=function(e){return a({url:\"app/\"+e+\"/machines.json\",method:\"GET\"})},this.removeAppMachine=function(e,r,t){return a({url:\"app/\"+e+\"/machine/remove.json\",method:\"POST\",headers:{\"Content-type\":\"application/x-www-form-urlencoded; charset=UTF-8\"},data:o({ip:r,port:t})})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"IdentityService\",[\"$http\",function(a){this.fetchIdentityOfMachine=function(e,r,t){return a({url:\"resource/machineResource.json\",params:{ip:e,port:r,searchKey:t},method:\"GET\"})},this.fetchClusterNodeOfMachine=function(e,r,t){return a({url:\"resource/machineResource.json\",params:{ip:e,port:r,type:\"cluster\",searchKey:t},method:\"GET\"})}}]),(app=angular.module(\"sentinelDashboardApp\")).service(\"MetricService\",[\"$http\",function(l){this.queryAppSortedIdentities=function(e){return l({url:\"/metric/queryTopResourceMetric.json\",params:e,method:\"GET\"})},this.queryByAppAndIdentity=function(e){return l({url:\"/metric/queryByAppAndResource.json\",params:e,method:\"GET\"})},this.queryByMachineAndIdentity=function(e,r,t,a,o){var n={ip:e,port:r,identity:t,startTime:a.getTime(),endTime:o.getTime()};return l({url:\"/metric/queryByAppAndResource.json\",params:n,method:\"GET\"})}}]),angular.module(\"sentinelDashboardApp\").service(\"ParamFlowService\",[\"$http\",function(a){function o(e){return!(!function(e){return\"int\"===e||\"double\"===e||\"float\"===e||\"long\"===e||\"short\"===e}(e.classType)||!function(e){return void 0===e||\"\"===e||isNaN(e)}(e.object))||(!(!function(e){return\"byte\"===e}(e.classType)||!function(e,r,t){return void 0===e||\"\"===e||isNaN(e)||e<r||t<e}(e.object,-128,127))||(void 0===e.object||void 0===e.classType||function(e){return void 0===e||\"\"===e||isNaN(e)||e<0}(e.count)))}this.queryMachineRules=function(e,r,t){return a({url:\"/paramFlow/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.addNewRule=function(e){return a({url:\"/paramFlow/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/paramFlow/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/paramFlow/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){if(!e.resource||\"\"===e.resource)return alert(\"资源名称不能为空\"),!1;if(1!=e.grade)return alert(\"未知的限流模式\"),!1;if(e.count<0)return alert(\"限流阈值必须大于等于 0\"),!1;if(void 0===e.paramIdx||\"\"===e.paramIdx||isNaN(e.paramIdx)||e.paramIdx<0)return alert(\"热点参数索引必须大于等于 0\"),!1;if(void 0!==e.paramFlowItemList)for(var r=0;r<e.paramFlowItemList.length;r++){var t=e.paramFlowItemList[r];if(o(t))return alert(\"热点参数例外项不合法，请检查值和类型是否正确：参数为 \"+t.object+\", 类型为 \"+t.classType+\", 限流阈值为 \"+t.count),!1}return!0}}]),angular.module(\"sentinelDashboardApp\").service(\"AuthorityRuleService\",[\"$http\",function(a){this.queryMachineRules=function(e,r,t){return a({url:\"/authority/rules\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.addNewRule=function(e){return a({url:\"/authority/rule\",data:e,method:\"POST\"})},this.saveRule=function(e){return a({url:\"/authority/rule/\"+e.id,data:e,method:\"PUT\"})},this.deleteRule=function(e){return a({url:\"/authority/rule/\"+e.id,method:\"DELETE\"})},this.checkRuleValid=function(e){return void 0===e.resource||\"\"===e.resource?(alert(\"资源名称不能为空\"),!1):void 0===e.limitApp||\"\"===e.limitApp?(alert(\"流控针对应用不能为空\"),!1):void 0!==e.strategy||(alert(\"必须选择黑白名单模式\"),!1)}}]),angular.module(\"sentinelDashboardApp\").service(\"ClusterStateService\",[\"$http\",function(a){this.fetchClusterUniversalStateSingle=function(e,r,t){return a({url:\"/cluster/state_single\",params:{app:e,ip:r,port:t},method:\"GET\"})},this.fetchClusterUniversalStateOfApp=function(e){return a({url:\"/cluster/state/\"+e,method:\"GET\"})},this.fetchClusterServerStateOfApp=function(e){return a({url:\"/cluster/server_state/\"+e,method:\"GET\"})},this.fetchClusterClientStateOfApp=function(e){return a({url:\"/cluster/client_state/\"+e,method:\"GET\"})},this.modifyClusterConfig=function(e){return a({url:\"/cluster/config/modify_single\",data:e,method:\"POST\"})},this.applyClusterFullAssignOfApp=function(e,r){return a({url:\"/cluster/assign/all_server/\"+e,data:r,method:\"POST\"})},this.applyClusterSingleServerAssignOfApp=function(e,r){return a({url:\"/cluster/assign/single_server/\"+e,data:r,method:\"POST\"})},this.applyClusterServerBatchUnbind=function(e,r){return a({url:\"/cluster/assign/unbind_server/\"+e,data:r,method:\"POST\"})}}]);"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/dist/js/app.vendor.js",
    "content": "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?t(exports,require(\"angular\")):\"function\"==typeof define&&define.amd?define([\"exports\",\"angular\"],t):t((e=e||self)[\"@uirouter/angularjs\"]={},e.angular)}(this,function(g,e){\"use strict\";var t=angular,S=e&&e.module?e:t;function l(n){var e=[].slice.apply(arguments,[1]),r=n.length;return function e(t){return t.length>=r?n.apply(null,t):function(){return e(t.concat([].slice.apply(arguments)))}}(e)}function n(){var n=arguments,r=n.length-1;return function(){for(var e=r,t=n[r].apply(this,arguments);e--;)t=n[e].call(this,t);return t}}function u(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,[].slice.call(arguments).reverse())}var v=function(t){return function(e){return e&&e[t]}},y=l(function(e,t,n){return n&&n[e]===t}),C=function(e){return u.apply(null,e.split(\".\").map(v))},d=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}};function r(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,e)&&r.apply(null,e)}}function i(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n.apply(null,e)||r.apply(null,e)}}var a=function(n){return function(e){return e.reduce(function(e,t){return e&&!!n(t)},!0)}},s=function(n){return function(e){return e.reduce(function(e,t){return e||!!n(t)},!1)}},c=function(t){return function(e){return null!=e&&e.constructor===t||e instanceof t}},o=function(t){return function(e){return t===e}},p=function(e){return function(){return e}};function h(t,n){return function(e){return e[t].apply(e,n)}}function f(n){return function(e){for(var t=0;t<n.length;t++)if(n[t][0](e))return n[t][1](e)}}var m=Object.prototype.toString,w=function(t){return function(e){return typeof e===t}},b=w(\"undefined\"),k=d(b),$=function(e){return null===e},_=i($,b),D=w(\"function\"),x=w(\"number\"),O=w(\"string\"),T=function(e){return null!==e&&\"object\"==typeof e},A=Array.isArray,E=function(e){return\"[object Date]\"===m.call(e)},P=function(e){return\"[object RegExp]\"===m.call(e)};function M(e){if(A(e)&&e.length){var t=e.slice(0,-1),n=e.slice(-1);return!(t.filter(d(O)).length||n.filter(d(D)).length)}return D(e)}var R=r(T,u(v(\"then\"),D)),I=function(n,e){return e.reduce(function(e,t){return e[t]=function(e){return function(){throw new Error(\"No implementation for \"+e+\". The framework specific code did not implement this method.\")}}(n+\".\"+t+\"()\"),e},{})},V={$q:void 0,$injector:void 0},L=\"object\"==typeof self&&self.self===self&&self||\"object\"==typeof global&&global.global===global&&global||void 0,F=L.angular||{},j=F.fromJson||JSON.parse.bind(JSON),H=F.toJson||JSON.stringify.bind(JSON),N=F.forEach||function(t,n,e){if(A(t))return t.forEach(n,e);Object.keys(t).forEach(function(e){return n(t[e],e)})},Y=Object.assign||Oe,U=F.equals||Te;function q(e){return e}function z(){}function B(t,n,r,e,i){void 0===i&&(i=!1);var o=function(e){return t()[e].bind(r())};return(e=e||Object.keys(t())).reduce(function(e,t){return e[t]=i?function(e){return function(){return n[e]=o(e),n[e].apply(null,arguments)}}(t):o(t),e},n)}var W=function(e,t){return Y(Object.create(e),t)},G=l(K);function K(e,t){return-1!==e.indexOf(t)}var Q=l(Z);function Z(e,t){var n=e.indexOf(t);return 0<=n&&e.splice(n,1),e}var X=l(J);function J(e,t){return e.push(t),t}var ee=function(t){return t.slice().forEach(function(e){\"function\"==typeof e&&e(),Q(t,e)})};function te(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=Y.apply(void 0,[{}].concat(t.reverse()));return Y(r,ie(e||{},Object.keys(r)))}var ne=function(e,t){return Y(e,t)};function re(e,t){var n=[];for(var r in e.path){if(e.path[r]!==t.path[r])break;n.push(e.path[r])}return n}function ie(e,t){var n={};for(var r in e)-1!==t.indexOf(r)&&(n[r]=e[r]);return n}function oe(n,e){return Object.keys(n).filter(d(G(e))).reduce(function(e,t){return e[t]=n[t],e},{})}function ae(e,t){return ce(e,v(t))}function se(e,n){var t=A(e),r=t?[]:{},i=t?function(e){return r.push(e)}:function(e,t){return r[t]=e};return N(e,function(e,t){n(e,t)&&i(e,t)}),r}function ue(e,n){var r;return N(e,function(e,t){r||n(e,t)&&(r=e)}),r}var le=ce;function ce(e,n,r){return r=r||(A(e)?[]:{}),N(e,function(e,t){return r[t]=n(e,t)}),r}var de=function(t){return Object.keys(t).map(function(e){return t[e]})},pe=function(e,t){return e&&t},he=function(e,t){return e||t},fe=function(e,t){return e.concat(t)},ge=function(e,t){return A(t)?e.concat(t.reduce(ge,[])):me(e,t)};function me(e,t){return e.push(t),e}var ve=function(e,t){return G(e,t)?e:me(e,t)},ye=function(e){return e.reduce(fe,[])},we=function(e){return e.reduce(ge,[])},be=_e,$e=_e;function _e(n,r){return void 0===r&&(r=\"assert failure\"),function(e){var t=n(e);if(!t)throw new Error(D(r)?r(e):r);return t}}var Se=function(t){return Object.keys(t).map(function(e){return[e,t[e]]})};function Ce(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];if(0===e.length)return[];for(var n=e.reduce(function(e,t){return Math.min(t.length,e)},9007199254740991),r=[],i=function(t){switch(e.length){case 1:r.push([e[0][t]]);break;case 2:r.push([e[0][t],e[1][t]]);break;case 3:r.push([e[0][t],e[1][t],e[2][t]]);break;case 4:r.push([e[0][t],e[1][t],e[2][t],e[3][t]]);break;default:r.push(e.map(function(e){return e[t]}))}},o=0;o<n;o++)i(o);return r}function ke(e,t){var n,r;if(A(t)&&(n=t[0],r=t[1]),!O(n))throw new Error(\"invalid parameters to applyPairs\");return e[n]=r,e}function De(e){return e.length&&e[e.length-1]||void 0}function xe(e,t){return t&&Object.keys(t).forEach(function(e){return delete t[e]}),t||(t={}),Y(t,e)}function Oe(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];if(n)for(var r=Object.keys(n),i=0;i<r.length;i++)e[r[i]]=n[r[i]]}return e}function Te(e,t){if(e===t)return!0;if(null===e||null===t)return!1;if(e!=e&&t!=t)return!0;var n=typeof e;if(n!=typeof t||\"object\"!=n)return!1;var r=[e,t];if(a(A)(r))return function(e,t){return e.length===t.length&&Ce(e,t).reduce(function(e,t){return e&&Te(t[0],t[1])},!0)}(e,t);if(a(E)(r))return e.getTime()===t.getTime();if(a(P)(r))return e.toString()===t.toString();if(a(D)(r))return!0;if([D,A,E,P].map(s).reduce(function(e,t){return e||!!t(r)},!1))return!1;var i={};for(var o in e){if(!Te(e[o],t[o]))return!1;i[o]=!0}for(var o in t)if(!i[o])return!1;return!0}var Ae,Ee=function(e){return e.catch(function(e){return 0})&&e},Pe=function(e){return Ee(V.$q.reject(e))},Me=function(){function t(e){this.text=e,this.glob=e.split(\".\");var t=this.text.split(\".\").map(function(e){return\"**\"===e?\"(?:|(?:\\\\.[^.]*)*)\":\"*\"===e?\"\\\\.[^.]*\":\"\\\\.\"+e}).join(\"\");this.regexp=new RegExp(\"^\"+t+\"$\")}return t.is=function(e){return!!/[!,*]+/.exec(e)},t.fromString=function(e){return t.is(e)?new t(e):null},t.prototype.matches=function(e){return this.regexp.test(\".\"+e)},t}(),Re=function(){function e(e,t){void 0===e&&(e=[]),void 0===t&&(t=null),this._items=e,this._limit=t,this._evictListeners=[],this.onEvict=X(this._evictListeners)}return e.prototype.enqueue=function(e){var t=this._items;return t.push(e),this._limit&&t.length>this._limit&&this.evict(),e},e.prototype.evict=function(){var t=this._items.shift();return this._evictListeners.forEach(function(e){return e(t)}),t},e.prototype.dequeue=function(){if(this.size())return this._items.splice(0,1)[0]},e.prototype.clear=function(){var e=this._items;return this._items=[],e},e.prototype.size=function(){return this._items.length},e.prototype.remove=function(e){var t=this._items.indexOf(e);return-1<t&&this._items.splice(t,1)[0]},e.prototype.peekTail=function(){return this._items[this._items.length-1]},e.prototype.peekHead=function(){if(this.size())return this._items[0]},e}();(Ae=g.RejectType||(g.RejectType={}))[Ae.SUPERSEDED=2]=\"SUPERSEDED\",Ae[Ae.ABORTED=3]=\"ABORTED\",Ae[Ae.INVALID=4]=\"INVALID\",Ae[Ae.IGNORED=5]=\"IGNORED\",Ae[Ae.ERROR=6]=\"ERROR\";var Ie=0,Ve=function(){function r(e,t,n){this.$id=Ie++,this.type=e,this.message=t,this.detail=n}return r.isRejectionPromise=function(e){return e&&\"function\"==typeof e.then&&c(r)(e._transitionRejection)},r.superseded=function(e,t){var n=new r(g.RejectType.SUPERSEDED,\"The transition has been superseded by a different transition\",e);return t&&t.redirected&&(n.redirected=!0),n},r.redirected=function(e){return r.superseded(e,{redirected:!0})},r.invalid=function(e){return new r(g.RejectType.INVALID,\"This transition is invalid\",e)},r.ignored=function(e){return new r(g.RejectType.IGNORED,\"The transition was ignored\",e)},r.aborted=function(e){return new r(g.RejectType.ABORTED,\"The transition has been aborted\",e)},r.errored=function(e){return new r(g.RejectType.ERROR,\"The transition errored\",e)},r.normalize=function(e){return c(r)(e)?e:r.errored(e)},r.prototype.toString=function(){var e,t=(e=this.detail)&&e.toString!==Object.prototype.toString?e.toString():qe(e);return\"Transition Rejection($id: \"+this.$id+\" type: \"+this.type+\", message: \"+this.message+\", detail: \"+t+\")\"},r.prototype.toPromise=function(){return Y(Pe(this),{_transitionRejection:this})},r}();function Le(e,t){return t.length<=e?t:t.substr(0,e-3)+\"...\"}function Fe(e,t){for(;t.length<e;)t+=\" \";return t}function je(e){return e.replace(/^([A-Z])/,function(e){return e.toLowerCase()}).replace(/([A-Z])/g,function(e){return\"-\"+e.toLowerCase()})}function He(e){var t=Ne(e),n=t.match(/^(function [^ ]+\\([^)]*\\))/),r=n?n[1]:t,i=e.name||\"\";return i&&r.match(/function \\(/)?\"function \"+i+r.substr(9):r}function Ne(e){var t=A(e)?e.slice(-1)[0]:e;return t&&t.toString()||\"undefined\"}var Ye=Ve.isRejectionPromise,Ue=f([[b,p(\"undefined\")],[$,p(\"null\")],[R,p(\"[Promise]\")],[Ye,function(e){return e._transitionRejection.toString()}],[function(e){return T(e)&&!A(e)&&e.constructor!==Object&&D(e.toString)},function(e){return e.toString()}],[M,He],[p(!0),q]]);function qe(e){var t=[];function n(e){if(T(e)){if(-1!==t.indexOf(e))return\"[circular ref]\";t.push(e)}return Ue(e)}return b(e)?n(e):JSON.stringify(e,function(e,t){return n(t)}).replace(/\\\\\"/g,'\"')}var ze=function(n){return function(e){if(!e)return[\"\",\"\"];var t=e.indexOf(n);return-1===t?[e,\"\"]:[e.substr(0,t),e.substr(t+1)]}},Be=new RegExp(\"^(?:[a-z]+:)?//[^/]+/\"),We=function(e){return e.replace(/\\/[^/]*$/,\"\")},Ge=ze(\"#\"),Ke=ze(\"?\"),Qe=ze(\"=\"),Ze=function(e){return e?e.replace(/^#/,\"\"):\"\"};function Xe(e){var t=new RegExp(\"(\"+e+\")\",\"g\");return function(e){return e.split(t).filter(q)}}function Je(e,t){return O(De(e))&&O(t)?e.slice(0,-1).concat(De(e)+t):me(e,t)}function et(e){if(!e)return\"ui-view (defunct)\";var t=e.creationContext?e.creationContext.name||\"(root)\":\"(none)\";return\"[ui-view#\"+e.id+\" \"+e.$type+\":\"+e.fqn+\" (\"+e.name+\"@\"+t+\")]\"}function tt(e){return x(e)?g.Category[e]:g.Category[g.Category[e]]}var nt,rt=Function.prototype.bind.call(console.log,console),it=D(console.table)?console.table.bind(console):rt.bind(console);(nt=g.Category||(g.Category={}))[nt.RESOLVE=0]=\"RESOLVE\",nt[nt.TRANSITION=1]=\"TRANSITION\",nt[nt.HOOK=2]=\"HOOK\",nt[nt.UIVIEW=3]=\"UIVIEW\",nt[nt.VIEWCONFIG=4]=\"VIEWCONFIG\";var ot=C(\"$id\"),at=C(\"router.$id\"),st=function(e){return\"Transition #\"+ot(e)+\"-\"+at(e)},ut=function(){function e(){this._enabled={},this.approximateDigests=0}return e.prototype._set=function(t,e){var n=this;e.length||(e=Object.keys(g.Category).map(function(e){return parseInt(e,10)}).filter(function(e){return!isNaN(e)}).map(function(e){return g.Category[e]})),e.map(tt).forEach(function(e){return n._enabled[e]=t})},e.prototype.enable=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._set(!0,e)},e.prototype.disable=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];this._set(!1,e)},e.prototype.enabled=function(e){return!!this._enabled[tt(e)]},e.prototype.traceTransitionStart=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+\": Started  -> \"+qe(e))},e.prototype.traceTransitionIgnored=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+\": Ignored  <> \"+qe(e))},e.prototype.traceHookInvocation=function(e,t,n){if(this.enabled(g.Category.HOOK)){var r=C(\"traceData.hookType\")(n)||\"internal\",i=C(\"traceData.context.state.name\")(n)||C(\"traceData.context\")(n)||\"unknown\",o=He(e.registeredHook.callback);console.log(st(t)+\":   Hook -> \"+r+\" context: \"+i+\", \"+Le(200,o))}},e.prototype.traceHookResult=function(e,t,n){this.enabled(g.Category.HOOK)&&console.log(st(t)+\":   <- Hook returned: \"+Le(200,qe(e)))},e.prototype.traceResolvePath=function(e,t,n){this.enabled(g.Category.RESOLVE)&&console.log(st(n)+\":         Resolving \"+e+\" (\"+t+\")\")},e.prototype.traceResolvableResolved=function(e,t){this.enabled(g.Category.RESOLVE)&&console.log(st(t)+\":               <- Resolved  \"+e+\" to: \"+Le(200,qe(e.data)))},e.prototype.traceError=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+\": <- Rejected \"+qe(t)+\", reason: \"+e)},e.prototype.traceSuccess=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+\": <- Success  \"+qe(t)+\", final state: \"+e.name)},e.prototype.traceUIViewEvent=function(e,t,n){void 0===n&&(n=\"\"),this.enabled(g.Category.UIVIEW)&&console.log(\"ui-view: \"+Fe(30,e)+\" \"+et(t)+n)},e.prototype.traceUIViewConfigUpdated=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent(\"Updating\",e,\" with ViewConfig from context='\"+t+\"'\")},e.prototype.traceUIViewFill=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent(\"Fill\",e,\" with: \"+Le(200,t))},e.prototype.traceViewSync=function(e){if(this.enabled(g.Category.VIEWCONFIG)){var a=\"uiview component fqn\",t=e.map(function(e){var t,n=e.uiView,r=e.viewConfig,i=n&&n.fqn,o=r&&r.viewDecl.$context.name+\": (\"+r.viewDecl.$name+\")\";return(t={})[a]=i,t[\"view config state (view name)\"]=o,t}).sort(function(e,t){return(e[a]||\"\").localeCompare(t[a]||\"\")});it(t)}},e.prototype.traceViewServiceEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log(\"VIEWCONFIG: \"+e+\" \"+function(e){var t=e.viewDecl,n=t.$context.name||\"(root)\";return\"[View#\"+e.$id+\" from '\"+n+\"' state]: target ui-view: '\"+t.$uiViewName+\"@\"+t.$uiViewContextAnchor+\"'\"}(t))},e.prototype.traceViewServiceUIViewEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log(\"VIEWCONFIG: \"+e+\" \"+et(t))},e}(),lt=new ut,ct=function(){function e(e){this.pattern=/.*/,this.inherit=!0,Y(this,e)}return e.prototype.is=function(e,t){return!0},e.prototype.encode=function(e,t){return e},e.prototype.decode=function(e,t){return e},e.prototype.equals=function(e,t){return e==t},e.prototype.$subPattern=function(){var e=this.pattern.toString();return e.substr(1,e.length-2)},e.prototype.toString=function(){return\"{ParamType:\"+this.name+\"}\"},e.prototype.$normalize=function(e){return this.is(e)?e:this.decode(e)},e.prototype.$asArray=function(e,t){if(!e)return this;if(\"auto\"===e&&!t)throw new Error(\"'auto' array mode is for query parameters only\");return new dt(this,e)},e}();function dt(r,i){var o=this;function a(e){return A(e)?e:k(e)?[e]:[]}function s(n,r){return function(e){if(A(e)&&0===e.length)return e;var t=ce(a(e),n);return!0===r?0===se(t,function(e){return!e}).length:function(e){switch(e.length){case 0:return;case 1:return\"auto\"===i?e[0]:e;default:return e}}(t)}}function u(o){return function(e,t){var n=a(e),r=a(t);if(n.length!==r.length)return!1;for(var i=0;i<n.length;i++)if(!o(n[i],r[i]))return!1;return!0}}[\"encode\",\"decode\",\"equals\",\"$normalize\"].forEach(function(e){var t=r[e].bind(r),n=\"equals\"===e?u:s;o[e]=n(t)}),Y(this,{dynamic:r.dynamic,name:r.name,pattern:r.pattern,inherit:r.inherit,raw:r.raw,is:s(r.is.bind(r),!0),$arrayMode:i})}var pt,ht=Object.prototype.hasOwnProperty,ft=function(e){return 0===[\"value\",\"type\",\"squash\",\"array\",\"dynamic\"].filter(ht.bind(e||{})).length};function gt(e,t,n){var r=!1===n.reloadOnSearch&&t===g.DefType.SEARCH||void 0,i=ue([n.dynamic,r],k),o=k(i)?{dynamic:i}:{},a=function(e){function t(){return e.value}e=ft(e)?{value:e}:e,t.__cacheable=!0;var n=M(e.value)?e.value:t;return Y(e,{$$fn:n})}(n&&n.params&&n.params[e]);return Y(o,a)}(pt=g.DefType||(g.DefType={}))[pt.PATH=0]=\"PATH\",pt[pt.SEARCH=1]=\"SEARCH\",pt[pt.CONFIG=2]=\"CONFIG\";var mt,vt=function(){function r(e,t,n,r,i){var o=gt(e,n,i);t=function(e,t,n,r,i){if(e.type&&t&&\"string\"!==t.name)throw new Error(\"Param '\"+r+\"' has two type configurations.\");if(e.type&&t&&\"string\"===t.name&&i.type(e.type))return i.type(e.type);if(t)return t;if(e.type)return e.type instanceof ct?e.type:i.type(e.type);var o=n===g.DefType.CONFIG?\"any\":n===g.DefType.PATH?\"path\":n===g.DefType.SEARCH?\"query\":\"string\";return i.type(o)}(o,t,n,e,r.paramTypes);var a,s,u=(a={array:n===g.DefType.SEARCH&&\"auto\"},s=e.match(/\\[\\]$/)?{array:!0}:{},Y(a,s,o).array);t=u?t.$asArray(u,n===g.DefType.SEARCH):t;var l=void 0!==o.value||n===g.DefType.SEARCH,c=k(o.dynamic)?!!o.dynamic:!!t.dynamic,d=k(o.raw)?!!o.raw:!!t.raw,p=function(e,t,n){var r=e.squash;if(!t||!1===r)return!1;if(!k(r)||null==r)return n;if(!0===r||O(r))return r;throw new Error(\"Invalid squash policy: '\"+r+\"'. Valid policies: false, true, or arbitrary string\")}(o,l,r.defaultSquashPolicy()),h=function(e,t,n,r){var i=[{from:\"\",to:n||t?void 0:\"\"},{from:null,to:n||t?void 0:\"\"}],o=A(e.replace)?e.replace:[];O(r)&&o.push({from:r,to:void 0});var a=ce(o,v(\"from\"));return se(i,function(e){return-1===a.indexOf(e.from)}).concat(o)}(o,u,l,p),f=k(o.inherit)?!!o.inherit:!!t.inherit;Y(this,{id:e,type:t,location:n,isOptional:l,dynamic:c,raw:d,squash:p,replace:h,inherit:f,array:u,config:o})}return r.values=function(e,t){void 0===t&&(t={});for(var n={},r=0,i=e;r<i.length;r++){var o=i[r];n[o.id]=o.value(t[o.id])}return n},r.changed=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),e.filter(function(e){return!e.type.equals(t[e.id],n[e.id])})},r.equals=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),0===r.changed(e,t,n).length},r.validates=function(e,t){return void 0===t&&(t={}),e.map(function(e){return e.validates(t[e.id])}).reduce(pe,!0)},r.prototype.isDefaultValue=function(e){return this.isOptional&&this.type.equals(this.value(),e)},r.prototype.value=function(e){var i=this;return e=function(e){for(var t=0,n=i.replace;t<n.length;t++){var r=n[t];if(r.from===e)return r.to}return e}(e),b(e)?function(){if(i._defaultValueCache)return i._defaultValueCache.defaultValue;if(!V.$injector)throw new Error(\"Injectable functions cannot be called at configuration time\");var e=V.$injector.invoke(i.config.$$fn);if(null!=e&&!i.type.is(e))throw new Error(\"Default value (\"+e+\") for parameter '\"+i.id+\"' is not an instance of ParamType (\"+i.type.name+\")\");return i.config.$$fn.__cacheable&&(i._defaultValueCache={defaultValue:e}),e}():this.type.$normalize(e)},r.prototype.isSearch=function(){return this.location===g.DefType.SEARCH},r.prototype.validates=function(e){if((b(e)||null===e)&&this.isOptional)return!0;var t=this.type.$normalize(e);if(!this.type.is(t))return!1;var n=this.type.encode(t);return!(O(n)&&!this.type.pattern.exec(n))},r.prototype.toString=function(){return\"{Param:\"+this.id+\" \"+this.type+\" squash: '\"+this.squash+\"' optional: \"+this.isOptional+\"}\"},r}(),yt=function(){function e(){this.enqueue=!0,this.typeQueue=[],this.defaultTypes=ie(e.prototype,[\"hash\",\"string\",\"query\",\"path\",\"int\",\"bool\",\"date\",\"json\",\"any\"]);this.types=W(ce(this.defaultTypes,function(e,t){return new ct(Y({name:t},e))}),{})}return e.prototype.dispose=function(){this.types={}},e.prototype.type=function(e,t,n){if(!k(t))return this.types[e];if(this.types.hasOwnProperty(e))throw new Error(\"A type named '\"+e+\"' has already been defined.\");return this.types[e]=new ct(Y({name:e},t)),n&&(this.typeQueue.push({name:e,def:n}),this.enqueue||this._flushTypeQueue()),this},e.prototype._flushTypeQueue=function(){for(;this.typeQueue.length;){var e=this.typeQueue.shift();if(e.pattern)throw new Error(\"You cannot override a type's .pattern at runtime.\");Y(this.types[e.name],V.$injector.invoke(e.def))}},e}();mt=function(e){var t=function(e){return null!=e?e.toString():e},n={encode:t,decode:t,is:c(String),pattern:/.*/,equals:function(e,t){return e==t}};return Y({},n,e)},Y(yt.prototype,{string:mt({}),path:mt({pattern:/[^/]*/}),query:mt({}),hash:mt({inherit:!1}),int:mt({decode:function(e){return parseInt(e,10)},is:function(e){return!_(e)&&this.decode(e.toString())===e},pattern:/-?\\d+/}),bool:mt({encode:function(e){return e?1:0},decode:function(e){return 0!==parseInt(e,10)},is:c(Boolean),pattern:/0|1/}),date:mt({encode:function(e){return this.is(e)?[e.getFullYear(),(\"0\"+(e.getMonth()+1)).slice(-2),(\"0\"+e.getDate()).slice(-2)].join(\"-\"):void 0},decode:function(e){if(this.is(e))return e;var t=this.capture.exec(e);return t?new Date(t[1],t[2]-1,t[3]):void 0},is:function(e){return e instanceof Date&&!isNaN(e.valueOf())},equals:function(n,r){return[\"getFullYear\",\"getMonth\",\"getDate\"].reduce(function(e,t){return e&&n[t]()===r[t]()},!0)},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/}),json:mt({encode:H,decode:j,is:c(Object),equals:U,pattern:/[^/]*/}),any:mt({encode:q,decode:q,is:function(){return!0},equals:U})});var wt=function(){function e(e){void 0===e&&(e={}),Y(this,e)}return e.prototype.$inherit=function(e,t,n){var r,i=re(t,n),o={},a=[];for(var s in i)if(i[s]&&i[s].params&&(r=Object.keys(i[s].params)).length)for(var u in r)0<=a.indexOf(r[u])||(a.push(r[u]),o[r[u]]=this[r[u]]);return Y({},o,e)},e}(),bt=function(){function r(e){if(e instanceof r){var t=e;this.state=t.state,this.paramSchema=t.paramSchema.slice(),this.paramValues=Y({},t.paramValues),this.resolvables=t.resolvables.slice(),this.views=t.views&&t.views.slice()}else{var n=e;this.state=n,this.paramSchema=n.parameters({inherit:!1}),this.paramValues={},this.resolvables=n.resolvables.map(function(e){return e.clone()})}}return r.prototype.clone=function(){return new r(this)},r.prototype.applyRawParams=function(n){return this.paramValues=this.paramSchema.reduce(function(e,t){return ke(e,function(e){return[e.id,e.value(n[e.id])]}(t))},{}),this},r.prototype.parameter=function(e){return ue(this.paramSchema,y(\"id\",e))},r.prototype.equals=function(e,t){var n=this.diff(e,t);return n&&0===n.length},r.prototype.diff=function(e,t){if(this.state!==e.state)return!1;var n=t?t(this):this.paramSchema;return vt.changed(n,this.paramValues,e.paramValues)},r.clone=function(e){return e.clone()},r}(),$t=function(){function r(e,t,n,r){this._stateRegistry=e,this._identifier=t,this._identifier=t,this._params=Y({},n||{}),this._options=Y({},r||{}),this._definition=e.matcher.find(t,this._options.relative)}return r.prototype.name=function(){return this._definition&&this._definition.name||this._identifier},r.prototype.identifier=function(){return this._identifier},r.prototype.params=function(){return this._params},r.prototype.$state=function(){return this._definition},r.prototype.state=function(){return this._definition&&this._definition.self},r.prototype.options=function(){return this._options},r.prototype.exists=function(){return!(!this._definition||!this._definition.self)},r.prototype.valid=function(){return!this.error()},r.prototype.error=function(){var e=this.options().relative;if(this._definition||!e)return this._definition?this._definition.self?void 0:\"State '\"+this.name()+\"' has an invalid definition\":\"No such state '\"+this.name()+\"'\";var t=e.name?e.name:e;return\"Could not resolve '\"+this.name()+\"' from state '\"+t+\"'\"},r.prototype.toString=function(){return\"'\"+this.name()+\"'\"+qe(this.params())},r.prototype.withState=function(e){return new r(this._stateRegistry,e,this._params,this._options)},r.prototype.withParams=function(e,t){void 0===t&&(t=!1);var n=t?e:Y({},this._params,e);return new r(this._stateRegistry,this._identifier,n,this._options)},r.prototype.withOptions=function(e,t){void 0===t&&(t=!1);var n=t?e:Y({},this._options,e);return new r(this._stateRegistry,this._identifier,this._params,n)},r.isDef=function(e){return e&&e.state&&(O(e.state)||O(e.state.name))},r}(),_t=function(){function p(){}return p.makeTargetState=function(e,t){var n=De(t).state;return new $t(e,n,t.map(v(\"paramValues\")).reduce(ne,{}),{})},p.buildPath=function(e){var t=e.params();return e.$state().path.map(function(e){return new bt(e).applyRawParams(t)})},p.buildToPath=function(e,t){var n=p.buildPath(t);return t.options().inherit?p.inheritParams(e,n,Object.keys(t.params())):n},p.applyViewConfigs=function(i,o,t){o.filter(function(e){return G(t,e.state)}).forEach(function(t){var e=de(t.state.views||{}),n=p.subPath(o,function(e){return e===t}),r=e.map(function(e){return i.createViewConfig(n,e)});t.views=r.reduce(fe,[])})},p.inheritParams=function(o,e,a){void 0===a&&(a=[]);var s=o.map(function(e){return e.paramSchema}).reduce(fe,[]).filter(function(e){return!e.inherit}).map(v(\"id\"));return e.map(function(e){var t=Y({},e&&e.paramValues),n=ie(t,a);t=oe(t,a);var r=oe(function(e,t){var n=ue(e,y(\"state\",t));return Y({},n&&n.paramValues)}(o,e.state)||{},s),i=Y(t,r,n);return new bt(e.state).applyRawParams(i)})},p.treeChanges=function(e,r,t){for(var n,i,o,a,s,u,l=Math.min(e.length,r.length),c=0;c<l&&e[c].state!==t&&(n=e[c],i=r[c],n.equals(i,p.nonDynamicParams));)c++;a=(o=e).slice(0,c),s=o.slice(c);var d=a.map(function(e,t){var n=e.clone();return n.paramValues=r[t].paramValues,n});return u=r.slice(c),{from:o,to:d.concat(u),retained:a,retainedWithToParams:d,exiting:s,entering:u}},p.matching=function(e,t,i){var o=!1;return Ce(e,t).reduce(function(e,t){var n=t[0],r=t[1];return(o=o||!n.equals(r,i))?e:e.concat(n)},[])},p.equals=function(e,t,n){return e.length===t.length&&p.matching(e,t,n).length===e.length},p.subPath=function(e,t){var n=ue(e,t),r=e.indexOf(n);return-1===r?void 0:e.slice(0,r+1)},p.nonDynamicParams=function(e){return e.state.parameters({inherit:!1}).filter(function(e){return!e.dynamic})},p.paramValues=function(e){return e.reduce(function(e,t){return Y(e,t.paramValues)},{})},p}(),St={when:{LAZY:\"LAZY\",EAGER:\"EAGER\"},async:{WAIT:\"WAIT\",NOWAIT:\"NOWAIT\",RXWAIT:\"RXWAIT\"}},Ct={when:\"LAZY\",async:\"WAIT\"},kt=function(){function a(e,t,n,r,i){if(this.resolved=!1,this.promise=void 0,e instanceof a)Y(this,e);else if(D(t)){if(_(e))throw new Error(\"new Resolvable(): token argument is required\");if(!D(t))throw new Error(\"new Resolvable(): resolveFn argument must be a function\");this.token=e,this.policy=r,this.resolveFn=t,this.deps=n||[],this.data=i,this.resolved=void 0!==i,this.promise=this.resolved?V.$q.when(this.data):void 0}else if(T(e)&&e.token&&(e.hasOwnProperty(\"resolveFn\")||e.hasOwnProperty(\"data\"))){var o=e;return new a(o.token,o.resolveFn,o.deps,o.policy,o.data)}}return a.prototype.getPolicy=function(e){var t=this.policy||{},n=e&&e.resolvePolicy||{};return{when:t.when||n.when||Ct.when,async:t.async||n.async||Ct.async}},a.prototype.resolve=function(t,n){var r=this,e=V.$q,i=t.findNode(this),o=i&&i.state,a=\"RXWAIT\"===this.getPolicy(o).async?function(e){var t=e.cache(1);return t.take(1).toPromise().then(function(){return t})}:q;return this.promise=e.when().then(function(){return e.all(t.getDependencies(r).map(function(e){return e.get(t,n)}))}).then(function(e){return r.resolveFn.apply(null,e)}).then(a).then(function(e){return r.data=e,r.resolved=!0,r.resolveFn=null,lt.traceResolvableResolved(r,n),r.data})},a.prototype.get=function(e,t){return this.promise||this.resolve(e,t)},a.prototype.toString=function(){return\"Resolvable(token: \"+qe(this.token)+\", requires: [\"+this.deps.map(qe)+\"])\"},a.prototype.clone=function(){return new a(this)},a.fromData=function(e,t){return new a(e,function(){return t},null,null,t)},a}(),Dt=St.when,xt=[Dt.EAGER,Dt.LAZY],Ot=[Dt.EAGER],Tt=\"Native Injector\",At=function(){function e(e){this._path=e}return e.prototype.getTokens=function(){return this._path.reduce(function(e,t){return e.concat(t.resolvables.map(function(e){return e.token}))},[]).reduce(ve,[])},e.prototype.getResolvable=function(t){return De(this._path.map(function(e){return e.resolvables}).reduce(fe,[]).filter(function(e){return e.token===t}))},e.prototype.getPolicy=function(e){var t=this.findNode(e);return e.getPolicy(t.state)},e.prototype.subContext=function(t){return new e(_t.subPath(this._path,function(e){return e.state===t}))},e.prototype.addResolvables=function(e,t){var n=ue(this._path,y(\"state\",t)),r=e.map(function(e){return e.token});n.resolvables=n.resolvables.filter(function(e){return-1===r.indexOf(e.token)}).concat(e)},e.prototype.resolvePath=function(e,s){var u=this;void 0===e&&(e=\"LAZY\");var l=(G(xt,e)?e:\"LAZY\")===St.when.EAGER?Ot:xt;lt.traceResolvePath(this._path,e,s);var c=function(t,n){return function(e){return G(t,u.getPolicy(e)[n])}},t=this._path.reduce(function(e,t){var n=t.resolvables.filter(c(l,\"when\")),r=n.filter(c([\"NOWAIT\"],\"async\")),i=n.filter(d(c([\"NOWAIT\"],\"async\"))),o=u.subContext(t.state),a=function(t){return t.get(o,s).then(function(e){return{token:t.token,value:e}})};return r.forEach(a),e.concat(i.map(a))},[]);return V.$q.all(t)},e.prototype.injector=function(){return this._injector||(this._injector=new Et(this))},e.prototype.findNode=function(t){return ue(this._path,function(e){return G(e.resolvables,t)})},e.prototype.getDependencies=function(t){var r=this,n=this.findNode(t),i=(_t.subPath(this._path,function(e){return e===n})||this._path).reduce(function(e,t){return e.concat(t.resolvables)},[]).filter(function(e){return e!==t});return t.deps.map(function(t){var e=i.filter(function(e){return e.token===t});if(e.length)return De(e);var n=r.injector().getNative(t);if(b(n))throw new Error(\"Could not find Dependency Injection token: \"+qe(t));return new kt(t,function(){return n},[],n)})},e}(),Et=function(){function e(e){this.context=e,this.native=this.get(Tt)||V.$injector}return e.prototype.get=function(e){var t=this.context.getResolvable(e);if(t){if(\"NOWAIT\"===this.context.getPolicy(t).async)return t.get(this.context);if(!t.resolved)throw new Error(\"Resolvable async .get() not complete:\"+qe(t.token));return t.data}return this.getNative(e)},e.prototype.getAsync=function(e){var t=this.context.getResolvable(e);return t?t.get(this.context):V.$q.when(this.native.get(e))},e.prototype.getNative=function(e){return this.native&&this.native.get(e)},e}();function Pt(e){return e.name}function Mt(e){return e.self.$$state=function(){return e},e.self}function Rt(e){return e.parent&&e.parent.data&&(e.data=e.self.data=W(e.parent.data,e.data)),e.data}var It=function(a,s){return function(e){var t=e.self;if(t&&t.url&&t.name&&t.name.match(/\\.\\*\\*$/)){var n={};xe(t,n),n.url+=\"{remainder:any}\",t=n}var r=e.parent,i=function(e){if(!O(e))return!1;var t=\"^\"===e.charAt(0);return{val:t?e.substring(1):e,root:t}}(t.url),o=i?a.compile(i.val,{state:t}):t.url;if(!o)return null;if(!a.isMatcher(o))throw new Error(\"Invalid url '\"+o+\"' in state '\"+e+\"'\");return i&&i.root?o:(r&&r.navigable||s()).url.append(o)}};function Vt(e){return e.parent?e.parent.path.concat(e):[e]}function Lt(e){var t=e.parent?Y({},e.parent.includes):{};return t[e.name]=!0,t}function Ft(e){var t,n,r=function(e){return e.provide||e.token},i=f([[v(\"resolveFn\"),function(e){return new kt(r(e),e.resolveFn,e.deps,e.policy)}],[v(\"useFactory\"),function(e){return new kt(r(e),e.useFactory,e.deps||e.dependencies,e.policy)}],[v(\"useClass\"),function(e){return new kt(r(e),function(){return new e.useClass},[],e.policy)}],[v(\"useValue\"),function(e){return new kt(r(e),function(){return e.useValue},[],e.policy,e.useValue)}],[v(\"useExisting\"),function(e){return new kt(r(e),q,[e.useExisting],e.policy)}]]),o=f([[u(v(\"val\"),O),function(e){return new kt(e.token,q,[e.val],e.policy)}],[u(v(\"val\"),A),function(e){return new kt(e.token,De(e.val),e.val.slice(0,-1),e.policy)}],[u(v(\"val\"),D),function(e){return new kt(e.token,e.val,function(e){var t=V.$injector;return e.$inject||t&&t.annotate(e,t.strictDi)||\"deferred\"}(e.val),e.policy)}]]),a=f([[c(kt),function(e){return e}],[function(e){return!(!e.token||!e.resolveFn)},i],[function(e){return!(!e.provide&&!e.token||!(e.useValue||e.useFactory||e.useExisting||e.useClass))},i],[function(e){return!!(e&&e.val&&(O(e.val)||A(e.val)||D(e.val)))},o],[p(!0),function(e){throw new Error(\"Invalid resolve value: \"+qe(e))}]]),s=e.resolve;return(A(s)?s:(t=s,n=e.resolvePolicy||{},Object.keys(t||{}).map(function(e){return{token:e,val:t[e],deps:void 0,policy:n[e]}}))).map(a)}var jt,Ht,Nt=function(){function e(t,e){this.matcher=t;var n=this,r=function(){return t.find(\"\")},i=function(e){return\"\"===e.name};this.builders={name:[Pt],self:[Mt],parent:[function(e){return i(e)?null:t.find(n.parentName(e))||r()}],data:[Rt],url:[It(e,r)],navigable:[function(t){return function(e){return!t(e)&&e.url?e:e.parent?e.parent.navigable:null}}(i)],params:[function(r){return function(n){var e=n.url&&n.url.parameters({inherit:!1})||[],t=de(le(oe(n.params||{},e.map(v(\"id\"))),function(e,t){return r.fromConfig(t,null,n.self)}));return e.concat(t).map(function(e){return[e.id,e]}).reduce(ke,{})}}(e.paramFactory)],views:[],path:[Vt],includes:[Lt],resolvables:[Ft]}}return e.prototype.builder=function(e,t){var n=this.builders,r=n[e]||[];return O(e)&&!k(t)?1<r.length?r:r[0]:O(e)&&D(t)?(n[e]=r,n[e].push(t),function(){return n[e].splice(n[e].indexOf(t,1))&&null}):void 0},e.prototype.build=function(e){var t=this.matcher,n=this.builders,r=this.parentName(e);if(r&&!t.find(r,void 0,!1))return null;for(var i in n)if(n.hasOwnProperty(i)){var o=n[i].reduce(function(t,n){return function(e){return n(e,t)}},z);e[i]=o(e)}return e},e.prototype.parentName=function(e){var t=e.name||\"\",n=t.split(\".\");if(\"**\"===n.pop()&&n.pop(),n.length){if(e.parent)throw new Error(\"States that specify the 'parent:' property should not have a '.' in their name (\"+t+\")\");return n.join(\".\")}return e.parent?O(e.parent)?e.parent:e.parent.name:\"\"},e.prototype.name=function(e){var t=e.name;if(-1!==t.indexOf(\".\")||!e.parent)return t;var n=O(e.parent)?e.parent:e.parent.name;return n?n+\".\"+t:t},e}(),Yt=function(){function n(e){return n.create(e||{})}return n.create=function(e){e=n.isStateClass(e)?new e:e;var t=W(W(e,n.prototype));return e.$$state=function(){return t},t.self=e,t.__stateObjectCache={nameGlob:Me.fromString(t.name)},t},n.prototype.is=function(e){return this===e||this.self===e||this.fqn()===e},n.prototype.fqn=function(){if(!(this.parent&&this.parent instanceof this.constructor))return this.name;var e=this.parent.fqn();return e?e+\".\"+this.name:this.name},n.prototype.root=function(){return this.parent&&this.parent.root()||this},n.prototype.parameters=function(t){return((t=te(t,{inherit:!0,matchingKeys:null})).inherit&&this.parent&&this.parent.parameters()||[]).concat(de(this.params)).filter(function(e){return!t.matchingKeys||t.matchingKeys.hasOwnProperty(e.id)})},n.prototype.parameter=function(e,t){return void 0===t&&(t={}),this.url&&this.url.parameter(e,t)||ue(de(this.params),y(\"id\",e))||t.inherit&&this.parent&&this.parent.parameter(e)},n.prototype.toString=function(){return this.fqn()},n.isStateClass=function(e){return D(e)&&!0===e.__uiRouterState},n.isState=function(e){return T(e.__stateObjectCache)},n}(),Ut=function(){function e(e){this._states=e}return e.prototype.isRelative=function(e){return 0===(e=e||\"\").indexOf(\".\")||0===e.indexOf(\"^\")},e.prototype.find=function(e,t,n){if(void 0===n&&(n=!0),e||\"\"===e){var r=O(e),i=r?e:e.name;this.isRelative(i)&&(i=this.resolvePath(i,t));var o=this._states[i];if(o&&(r||!(r||o!==e&&o.self!==e)))return o;if(r&&n){var a=de(this._states).filter(function(e){return e.__stateObjectCache.nameGlob&&e.__stateObjectCache.nameGlob.matches(i)});return 1<a.length&&console.log(\"stateMatcher.find: Found multiple matches for \"+i+\" using glob: \",a.map(function(e){return e.name})),a[0]}}},e.prototype.resolvePath=function(e,t){if(!t)throw new Error(\"No reference point given for path '\"+e+\"'\");for(var n=this.find(t),r=e.split(\".\"),i=r.length,o=0,a=n;o<i;o++)if(\"\"!==r[o]||0!==o){if(\"^\"!==r[o])break;if(!a.parent)throw new Error(\"Path '\"+e+\"' not valid for state '\"+n.name+\"'\");a=a.parent}else a=n;var s=r.slice(o).join(\".\");return a.name+(a.name&&s?\".\":\"\")+s},e}(),qt=function(){function e(e,t,n,r){this.router=e,this.states=t,this.builder=n,this.listeners=r,this.queue=[]}return e.prototype.dispose=function(){this.queue=[]},e.prototype.register=function(e){var t=this.queue,n=Yt.create(e),r=n.name;if(!O(r))throw new Error(\"State must have a valid name\");if(this.states.hasOwnProperty(r)||G(t.map(v(\"name\")),r))throw new Error(\"State '\"+r+\"' is already defined\");return t.push(n),this.flush(),n},e.prototype.flush=function(){for(var t=this,e=this.queue,n=this.states,r=this.builder,i=[],o=[],a={},s=function(e){return t.states.hasOwnProperty(e)&&t.states[e]},u=function(){i.length&&t.listeners.forEach(function(e){return e(\"registered\",i.map(function(e){return e.self}))})};0<e.length;){var l=e.shift(),c=l.name,d=r.build(l),p=o.indexOf(l);if(d){var h=s(c);if(h&&h.name===c)throw new Error(\"State '\"+c+\"' is already defined\");var f=s(c+\".**\");f&&this.router.stateRegistry.deregister(f),n[c]=l,this.attachRoute(l),0<=p&&o.splice(p,1),i.push(l)}else{var g=a[c];if(a[c]=e.length,0<=p&&g===e.length)return e.push(l),u(),n;p<0&&o.push(l),e.push(l)}}return u(),n},e.prototype.attachRoute=function(e){if(!e.abstract&&e.url){var t=this.router.urlService.rules;t.rule(t.urlRuleFactory.create(e))}},e}(),zt=function(){function e(e){this.router=e,this.states={},this.listeners=[],this.matcher=new Ut(this.states),this.builder=new Nt(this.matcher,e.urlMatcherFactory),this.stateQueue=new qt(e,this.states,this.builder,this.listeners),this._registerRoot()}return e.prototype._registerRoot=function(){(this._root=this.stateQueue.register({name:\"\",url:\"^\",views:null,params:{\"#\":{value:null,type:\"hash\",dynamic:!0}},abstract:!0})).navigable=null},e.prototype.dispose=function(){var t=this;this.stateQueue.dispose(),this.listeners=[],this.get().forEach(function(e){return t.get(e)&&t.deregister(e)})},e.prototype.onStatesChanged=function(e){return this.listeners.push(e),function(){Q(this.listeners)(e)}.bind(this)},e.prototype.root=function(){return this._root},e.prototype.register=function(e){return this.stateQueue.register(e)},e.prototype._deregisterTree=function(e){var n=this,r=this.get().map(function(e){return e.$$state()}),i=function(t){var e=r.filter(function(e){return-1!==t.indexOf(e.parent)});return 0===e.length?e:e.concat(i(e))},t=i([e]),o=[e].concat(t).reverse();return o.forEach(function(e){var t=n.router.urlService.rules;t.rules().filter(y(\"state\",e)).forEach(function(e){return t.removeRule(e)}),delete n.states[e.name]}),o},e.prototype.deregister=function(e){var t=this.get(e);if(!t)throw new Error(\"Can't deregister state; not found: \"+e);var n=this._deregisterTree(t.$$state());return this.listeners.forEach(function(e){return e(\"deregistered\",n.map(function(e){return e.self}))}),n},e.prototype.get=function(e,t){var n=this;if(0===arguments.length)return Object.keys(this.states).map(function(e){return n.states[e].self});var r=this.matcher.find(e,t);return r&&r.self||null},e.prototype.decorator=function(e,t){return this.builder.builder(e,t)},e}();(jt=g.TransitionHookPhase||(g.TransitionHookPhase={}))[jt.CREATE=0]=\"CREATE\",jt[jt.BEFORE=1]=\"BEFORE\",jt[jt.RUN=2]=\"RUN\",jt[jt.SUCCESS=3]=\"SUCCESS\",jt[jt.ERROR=4]=\"ERROR\",(Ht=g.TransitionHookScope||(g.TransitionHookScope={}))[Ht.TRANSITION=0]=\"TRANSITION\",Ht[Ht.STATE=1]=\"STATE\";var Bt={current:z,transition:null,traceData:{},bind:null},Wt=function(){function o(e,t,n,r){var i=this;this.transition=e,this.stateContext=t,this.registeredHook=n,this.options=r,this.isSuperseded=function(){return i.type.hookPhase===g.TransitionHookPhase.RUN&&!i.options.transition.isActive()},this.options=te(r,Bt),this.type=n.eventType}return o.chain=function(e,t){return e.reduce(function(e,t){return e.then(function(){return t.invokeHook()})},t||V.$q.when())},o.invokeHooks=function(e,t){for(var n=0;n<e.length;n++){var r=e[n].invokeHook();if(R(r)){var i=e.slice(n+1);return o.chain(i,r).then(t)}}return t()},o.runAllHooks=function(e){e.forEach(function(e){return e.invokeHook()})},o.prototype.logError=function(e){this.transition.router.stateService.defaultErrorHandler()(e)},o.prototype.invokeHook=function(){var t=this,n=this.registeredHook;if(!n._deregistered){var e=this.getNotCurrentRejection();if(e)return e;var r=this.options;lt.traceHookInvocation(this,this.transition,r);var i=function(e){return n.eventType.getErrorHandler(t)(e)},o=function(e){return n.eventType.getResultHandler(t)(e)};try{var a=n.callback.call(r.bind,t.transition,t.stateContext);return!this.type.synchronous&&R(a)?a.catch(function(e){return Ve.normalize(e).toPromise()}).then(o,i):o(a)}catch(e){return i(Ve.normalize(e))}finally{n.invokeLimit&&++n.invokeCount>=n.invokeLimit&&n.deregister()}}},o.prototype.handleHookResult=function(e){var t=this,n=this.getNotCurrentRejection();return n||(R(e)?e.then(function(e){return t.handleHookResult(e)}):(lt.traceHookResult(e,this.transition,this.options),!1===e?Ve.aborted(\"Hook aborted transition\").toPromise():c($t)(e)?Ve.redirected(e).toPromise():void 0))},o.prototype.getNotCurrentRejection=function(){var e=this.transition.router;return e._disposed?Ve.aborted(\"UIRouter instance #\"+e.$id+\" has been stopped (disposed)\").toPromise():this.transition._aborted?Ve.aborted().toPromise():this.isSuperseded()?Ve.superseded(this.options.current()).toPromise():void 0},o.prototype.toString=function(){var e=this.options,t=this.registeredHook;return(C(\"traceData.hookType\")(e)||\"internal\")+\" context: \"+(C(\"traceData.context.state.name\")(e)||C(\"traceData.context\")(e)||\"unknown\")+\", \"+Le(200,Ne(t.callback))},o.HANDLE_RESULT=function(t){return function(e){return t.handleHookResult(e)}},o.LOG_REJECTED_RESULT=function(t){return function(e){R(e)&&e.catch(function(e){return t.logError(Ve.normalize(e))})}},o.LOG_ERROR=function(t){return function(e){return t.logError(e)}},o.REJECT_ERROR=function(e){return function(e){return Pe(e)}},o.THROW_ERROR=function(e){return function(e){throw e}},o}();function Gt(e,t,n){var i=O(t)?[t]:t;return!!(D(i)?i:function(e){for(var t=i,n=0;n<t.length;n++){var r=new Me(t[n]);if(r&&r.matches(e.name)||!r&&t[n]===e.name)return!0}return!1})(e,n)}var Kt=function(){function e(e,t,n,r,i,o){void 0===o&&(o={}),this.tranSvc=e,this.eventType=t,this.callback=n,this.matchCriteria=r,this.removeHookFromRegistry=i,this.invokeCount=0,this._deregistered=!1,this.priority=o.priority||0,this.bind=o.bind||null,this.invokeLimit=o.invokeLimit}return e.prototype._matchingNodes=function(e,t,n){if(!0===t)return e;var r=e.filter(function(e){return Gt(e.state,t,n)});return r.length?r:null},e.prototype._getDefaultMatchCriteria=function(){return le(this.tranSvc._pluginapi._getPathTypes(),function(){return!0})},e.prototype._getMatchingNodes=function(o,a){var s=this,u=Y(this._getDefaultMatchCriteria(),this.matchCriteria);return de(this.tranSvc._pluginapi._getPathTypes()).reduce(function(e,t){var n=t.scope===g.TransitionHookScope.STATE,r=o[t.name]||[],i=n?r:[De(r)];return e[t.name]=s._matchingNodes(i,u[t.name],a),e},{})},e.prototype.matches=function(e,t){var n=this._getMatchingNodes(e,t);return de(n).every(q)?n:null},e.prototype.deregister=function(){this.removeHookFromRegistry(this),this._deregistered=!0},e}();function Qt(e,i,o){var a=(e._registeredHooks=e._registeredHooks||{})[o.name]=[],s=Q(a);function t(e,t,n){void 0===n&&(n={});var r=new Kt(i,o,t,e,s,n);return a.push(r),r.deregister.bind(r)}return e[o.name]=t}var Zt=function(){function e(e){this.transition=e}return e.prototype.buildHooksForPhase=function(e){var t=this;return this.transition.router.transitionService._pluginapi._getEvents(e).map(function(e){return t.buildHooks(e)}).reduce(fe,[]).filter(q)},e.prototype.buildHooks=function(o){var a=this.transition,e=a.treeChanges(),t=this.getMatchingHooks(o,e,a);if(!t)return[];var s={transition:a,current:a.options().current};return t.map(function(i){return i.matches(e,a)[o.criteriaMatchPath.name].map(function(e){var t=Y({bind:i.bind,traceData:{hookType:o.name,context:e}},s),n=o.criteriaMatchPath.scope===g.TransitionHookScope.STATE?e.state.self:null,r=new Wt(a,n,i,t);return{hook:i,node:e,transitionHook:r}})}).reduce(fe,[]).sort(function(i){void 0===i&&(i=!1);return function(e,t){var n=i?-1:1,r=(e.node.state.path.length-t.node.state.path.length)*n;return 0!=r?r:t.hook.priority-e.hook.priority}}(o.reverseSort)).map(function(e){return e.transitionHook})},e.prototype.getMatchingHooks=function(t,n,r){var e=t.hookPhase===g.TransitionHookPhase.CREATE,i=this.transition.router.transitionService;return(e?[i]:[this.transition,i]).map(function(e){return e.getHooks(t.name)}).filter(be(A,\"broken event named: \"+t.name)).reduce(fe,[]).filter(function(e){return e.matches(n,r)})},e}();var Xt=v(\"self\"),Jt=function(){function t(e,t,n){var r=this;if(this._deferred=V.$q.defer(),this.promise=this._deferred.promise,this._registeredHooks={},this._hookBuilder=new Zt(this),this.isActive=function(){return r.router.globals.transition===r},this.router=n,!(this._targetState=t).valid())throw new Error(t.error());this._options=Y({current:p(this)},t.options()),this.$id=n.transitionService._transitionCount++;var i=_t.buildToPath(e,t);this._treeChanges=_t.treeChanges(e,i,this._options.reloadState),this.createTransitionHookRegFns();var o=this._hookBuilder.buildHooksForPhase(g.TransitionHookPhase.CREATE);Wt.invokeHooks(o,function(){return null}),this.applyViewConfigs(n)}return t.prototype.onBefore=function(e,t,n){},t.prototype.onStart=function(e,t,n){},t.prototype.onExit=function(e,t,n){},t.prototype.onRetain=function(e,t,n){},t.prototype.onEnter=function(e,t,n){},t.prototype.onFinish=function(e,t,n){},t.prototype.onSuccess=function(e,t,n){},t.prototype.onError=function(e,t,n){},t.prototype.createTransitionHookRegFns=function(){var t=this;this.router.transitionService._pluginapi._getEvents().filter(function(e){return e.hookPhase!==g.TransitionHookPhase.CREATE}).forEach(function(e){return Qt(t,t.router.transitionService,e)})},t.prototype.getHooks=function(e){return this._registeredHooks[e]},t.prototype.applyViewConfigs=function(e){var t=this._treeChanges.entering.map(function(e){return e.state});_t.applyViewConfigs(e.transitionService.$view,this._treeChanges.to,t)},t.prototype.$from=function(){return De(this._treeChanges.from).state},t.prototype.$to=function(){return De(this._treeChanges.to).state},t.prototype.from=function(){return this.$from().self},t.prototype.to=function(){return this.$to().self},t.prototype.targetState=function(){return this._targetState},t.prototype.is=function(e){return e instanceof t?this.is({to:e.$to().name,from:e.$from().name}):!(e.to&&!Gt(this.$to(),e.to,this)||e.from&&!Gt(this.$from(),e.from,this))},t.prototype.params=function(e){return void 0===e&&(e=\"to\"),Object.freeze(this._treeChanges[e].map(v(\"paramValues\")).reduce(ne,{}))},t.prototype.paramsChanged=function(){var e=this.params(\"from\"),n=this.params(\"to\"),t=[].concat(this._treeChanges.to).concat(this._treeChanges.from).map(function(e){return e.paramSchema}).reduce(ge,[]).reduce(ve,[]);return vt.changed(t,e,n).reduce(function(e,t){return e[t.id]=n[t.id],e},{})},t.prototype.injector=function(t,e){void 0===e&&(e=\"to\");var n=this._treeChanges[e];return t&&(n=_t.subPath(n,function(e){return e.state===t||e.state.name===t})),new At(n).injector()},t.prototype.getResolveTokens=function(e){return void 0===e&&(e=\"to\"),new At(this._treeChanges[e]).getTokens()},t.prototype.addResolvable=function(e,t){void 0===t&&(t=\"\"),e=c(kt)(e)?e:new kt(e);var n=\"string\"==typeof t?t:t.name,r=this._treeChanges.to,i=ue(r,function(e){return e.state.name===n});new At(r).addResolvables([e],i.state)},t.prototype.redirectedFrom=function(){return this._options.redirectedFrom||null},t.prototype.originalTransition=function(){var e=this.redirectedFrom();return e&&e.originalTransition()||this},t.prototype.options=function(){return this._options},t.prototype.entering=function(){return ce(this._treeChanges.entering,v(\"state\")).map(Xt)},t.prototype.exiting=function(){return ce(this._treeChanges.exiting,v(\"state\")).map(Xt).reverse()},t.prototype.retained=function(){return ce(this._treeChanges.retained,v(\"state\")).map(Xt)},t.prototype.views=function(e,t){void 0===e&&(e=\"entering\");var n=this._treeChanges[e];return(n=t?n.filter(y(\"state\",t)):n).map(v(\"views\")).filter(q).reduce(fe,[])},t.prototype.treeChanges=function(e){return e?this._treeChanges[e]:this._treeChanges},t.prototype.redirect=function(e){for(var t=1,n=this;null!=(n=n.redirectedFrom());)if(20<++t)throw new Error(\"Too many consecutive Transition redirects (20+)\");var r={redirectedFrom:this,source:\"redirect\"};\"url\"===this.options().source&&!1!==e.options().location&&(r.location=\"replace\");var i=Y({},this.options(),e.options(),r);e=e.withOptions(i,!0);var o,a=this.router.transitionService.create(this._treeChanges.from,e),s=this._treeChanges.entering,u=a._treeChanges.entering;return _t.matching(u,s,_t.nonDynamicParams).filter(d((o=e.options().reloadState,function(e){return o&&e.state.includes[o.name]}))).forEach(function(e,t){e.resolvables=s[t].resolvables}),a},t.prototype._changedParams=function(){var e=this._treeChanges;if(!this._options.reload&&(!e.exiting.length&&!e.entering.length&&e.to.length===e.from.length&&!Ce(e.to,e.from).map(function(e){return e[0].state!==e[1].state}).reduce(he,!1))){var t=e.to.map(function(e){return e.paramSchema}),n=[e.to,e.from].map(function(e){return e.map(function(e){return e.paramValues})});return Ce(t,n[0],n[1]).map(function(e){var t=e[0],n=e[1],r=e[2];return vt.changed(t,n,r)}).reduce(fe,[])}},t.prototype.dynamic=function(){var e=this._changedParams();return!!e&&e.map(function(e){return e.dynamic}).reduce(he,!1)},t.prototype.ignored=function(){return!!this._ignoredReason()},t.prototype._ignoredReason=function(){var e=this.router.globals.transition,r=this._options.reloadState,t=function(e,t){if(e.length!==t.length)return!1;var n=_t.matching(e,t);return e.length===n.filter(function(e){return!r||!e.state.includes[r.name]}).length},n=this.treeChanges(),i=e&&e.treeChanges();return i&&t(i.to,n.to)&&t(i.exiting,n.exiting)?\"SameAsPending\":0===n.exiting.length&&0===n.entering.length&&t(n.from,n.to)?\"SameAsCurrent\":void 0},t.prototype.run=function(){var t=this,n=Wt.runAllHooks,r=function(e){return t._hookBuilder.buildHooksForPhase(e)},e=r(g.TransitionHookPhase.BEFORE);return Wt.invokeHooks(e,function(){var e=t.router.globals;return e.lastStartedTransitionId=t.$id,e.transition=t,e.transitionHistory.enqueue(t),lt.traceTransitionStart(t),V.$q.when(void 0)}).then(function(){var e=r(g.TransitionHookPhase.RUN);return Wt.invokeHooks(e,function(){return V.$q.when(void 0)})}).then(function(){lt.traceSuccess(t.$to(),t),t.success=!0,t._deferred.resolve(t.to()),n(r(g.TransitionHookPhase.SUCCESS))},function(e){lt.traceError(e,t),t.success=!1,t._deferred.reject(e),t._error=e,n(r(g.TransitionHookPhase.ERROR))}),this.promise},t.prototype.valid=function(){return!this.error()||void 0!==this.success},t.prototype.abort=function(){b(this.success)&&(this._aborted=!0)},t.prototype.error=function(){var e=this.$to();if(e.self.abstract)return Ve.invalid(\"Cannot transition to abstract state '\"+e.name+\"'\");var t=e.parameters(),n=this.params(),r=t.filter(function(e){return!e.validates(n[e.id])});if(r.length){var i=r.map(function(e){return\"[\"+e.id+\":\"+qe(n[e.id])+\"]\"}).join(\", \"),o=\"The following parameter values are not valid for state '\"+e.name+\"': \"+i;return Ve.invalid(o)}return!1===this.success?this._error:void 0},t.prototype.toString=function(){var e=this.from(),t=this.to(),n=function(e){return null!==e[\"#\"]&&void 0!==e[\"#\"]?e:oe(e,[\"#\"])};return\"Transition#\"+this.$id+\"( '\"+(T(e)?e.name:e)+\"'\"+qe(n(this._treeChanges.from.map(v(\"paramValues\")).reduce(ne,{})))+\" -> \"+(this.valid()?\"\":\"(X) \")+\"'\"+(T(t)?t.name:t)+\"'\"+qe(n(this.params()))+\" )\"},t.diToken=t}();function en(e,t){var n=[\"\",\"\"],r=e.replace(/[\\\\\\[\\]\\^$*+?.()|{}]/g,\"\\\\$&\");if(!t)return r;switch(t.squash){case!1:n=[\"(\",\")\"+(t.isOptional?\"?\":\"\")];break;case!0:r=r.replace(/\\/$/,\"\"),n=[\"(?:/(\",\")|/)?\"];break;default:n=[\"(\"+t.squash+\"|\",\")?\"]}return r+n[0]+t.type.pattern.source+n[1]}var tn=Xe(\"/\"),nn={state:{params:{}},strict:!0,caseInsensitive:!0},rn=function(){function m(o,a,e,t){var s=this;this._cache={path:[this]},this._children=[],this._params=[],this._segments=[],this._compiled=[],this.config=t=te(t,nn),this.pattern=o;for(var n,r,i,u=/([:*])([\\w\\[\\]]+)|\\{([\\w\\[\\]]+)(?:\\:\\s*((?:[^{}\\\\]+|\\\\.|\\{(?:[^{}\\\\]+|\\\\.)*\\})+))?\\}/g,l=/([:]?)([\\w\\[\\].-]+)|\\{([\\w\\[\\].-]+)(?:\\:\\s*((?:[^{}\\\\]+|\\\\.|\\{(?:[^{}\\\\]+|\\\\.)*\\})+))?\\}/g,c=[],d=0,p=function(e){if(!m.nameValidator.test(e))throw new Error(\"Invalid parameter name '\"+e+\"' in pattern '\"+o+\"'\");if(ue(s._params,y(\"id\",e)))throw new Error(\"Duplicate parameter name '\"+e+\"' in pattern '\"+o+\"'\")},h=function(e,t){var n,r=e[2]||e[3],i=t?e[4]:e[4]||(\"*\"===e[1]?\"[\\\\s\\\\S]*\":null);return{id:r,regexp:i,segment:o.substring(d,e.index),type:i?a.type(i)||(n=i,W(a.type(t?\"query\":\"path\"),{pattern:new RegExp(n,s.config.caseInsensitive?\"i\":void 0)})):null}};(n=u.exec(o))&&!(0<=(r=h(n,!1)).segment.indexOf(\"?\"));)p(r.id),this._params.push(e.fromPath(r.id,r.type,t.state)),this._segments.push(r.segment),c.push([r.segment,De(this._params)]),d=u.lastIndex;var f=(i=o.substring(d)).indexOf(\"?\");if(0<=f){var g=i.substring(f);if(i=i.substring(0,f),0<g.length)for(d=0;n=l.exec(g);)p((r=h(n,!0)).id),this._params.push(e.fromSearch(r.id,r.type,t.state)),d=u.lastIndex}this._segments.push(i),this._compiled=c.map(function(e){return en.apply(null,e)}).concat(en(i))}return m.encodeDashes=function(e){return encodeURIComponent(e).replace(/-/g,function(e){return\"%5C%\"+e.charCodeAt(0).toString(16).toUpperCase()})},m.pathSegmentsAndParams=function(e){return Ce(e._segments,e._params.filter(function(e){return e.location===g.DefType.PATH}).concat(void 0)).reduce(fe,[]).filter(function(e){return\"\"!==e&&k(e)})},m.queryParams=function(e){return e._params.filter(function(e){return e.location===g.DefType.SEARCH})},m.compare=function(e,t){var n=function(e){return e._cache.weights=e._cache.weights||function(e){return e._cache.segments=e._cache.segments||e._cache.path.map(m.pathSegmentsAndParams).reduce(fe,[]).reduce(Je,[]).map(function(e){return O(e)?tn(e):e}).reduce(fe,[])}(e).map(function(e){return\"/\"===e?1:O(e)?2:e instanceof vt?3:void 0})},r=n(e),i=n(t);!function(e,t,n){for(var r=Math.max(e.length,t.length);e.length<r;)e.push(n);for(;t.length<r;)t.push(n)}(r,i,0);var o,a,s=Ce(r,i);for(a=0;a<s.length;a++)if(0!=(o=s[a][0]-s[a][1]))return o;return 0},m.prototype.append=function(e){return this._children.push(e),e._cache={path:this._cache.path.concat(e),parent:this,pattern:null},e},m.prototype.isRoot=function(){return this._cache.path[0]===this},m.prototype.toString=function(){return this.pattern},m.prototype.exec=function(e,r,t,n){var i=this;void 0===r&&(r={}),void 0===n&&(n={});var o=function(e,t,n){return e[t]=e[t]||n()}(this._cache,\"pattern\",function(){return new RegExp([\"^\",ye(i._cache.path.map(v(\"_compiled\"))).join(\"\"),!1===i.config.strict?\"/?\":\"\",\"$\"].join(\"\"),i.config.caseInsensitive?\"i\":void 0)}).exec(e);if(!o)return null;var a,s=this.parameters(),u=s.filter(function(e){return!e.isSearch()}),l=s.filter(function(e){return e.isSearch()}),c=this._cache.path.map(function(e){return e._segments.length-1}).reduce(function(e,t){return e+t}),d={};if(c!==o.length-1)throw new Error(\"Unbalanced capture group in route '\"+this.pattern+\"'\");for(var p=0;p<c;p++){for(var h=u[p],f=o[p+1],g=0;g<h.replace.length;g++)h.replace[g].from===f&&(f=h.replace[g].to);f&&!0===h.array&&(void 0,f=ce(ce((a=function(e){return e.split(\"\").reverse().join(\"\")})(f).split(/-(?!\\\\)/),a),function(e){return e.replace(/\\\\-/g,\"-\")}).reverse()),k(f)&&(f=h.type.decode(f)),d[h.id]=h.value(f)}return l.forEach(function(e){for(var t=r[e.id],n=0;n<e.replace.length;n++)e.replace[n].from===t&&(t=e.replace[n].to);k(t)&&(t=e.type.decode(t)),d[e.id]=e.value(t)}),t&&(d[\"#\"]=t),d},m.prototype.parameters=function(e){return void 0===e&&(e={}),!1===e.inherit?this._params:ye(this._cache.path.map(function(e){return e._params}))},m.prototype.parameter=function(r,e){var i=this;void 0===e&&(e={});var t=this._cache.parent;return function(){for(var e=0,t=i._params;e<t.length;e++){var n=t[e];if(n.id===r)return n}}()||!1!==e.inherit&&t&&t.parameter(r,e)||null},m.prototype.validates=function(t){return t=t||{},this.parameters().filter(function(e){return t.hasOwnProperty(e.id)}).map(function(e){return function(e,t){return!e||e.validates(t)}(e,t[e.id])}).reduce(pe,!0)},m.prototype.format=function(a){void 0===a&&(a={});var e=this._cache.path,t=e.map(m.pathSegmentsAndParams).reduce(fe,[]).map(function(e){return O(e)?e:r(e)}),n=e.map(m.queryParams).reduce(fe,[]).map(r);if(t.concat(n).filter(function(e){return!1===e.isValid}).length)return null;function r(e){var t=e.value(a[e.id]),n=e.validates(t),r=e.isDefaultValue(t),i=!!r&&e.squash,o=e.type.encode(t);return{param:e,value:t,isValid:n,isDefaultValue:r,squash:i,encoded:o}}var i=t.reduce(function(e,t){if(O(t))return e+t;var n=t.squash,r=t.encoded,i=t.param;return!0===n?e.match(/\\/$/)?e.slice(0,-1):e:O(n)?e+n:!1!==n?e:null==r?e:A(r)?e+ce(r,m.encodeDashes).join(\"-\"):i.raw?e+r:e+encodeURIComponent(r)},\"\"),o=n.map(function(e){var t=e.param,n=e.squash,r=e.encoded,i=e.isDefaultValue;if(!(null==r||i&&!1!==n)&&(A(r)||(r=[r]),0!==r.length))return t.raw||(r=ce(r,encodeURIComponent)),r.map(function(e){return t.id+\"=\"+e})}).filter(q).reduce(fe,[]).join(\"&\");return i+(o?\"?\"+o:\"\")+(a[\"#\"]?\"#\"+a[\"#\"]:\"\")},m.nameValidator=/^\\w+([-.]+\\w+)*(?:\\[\\])?$/,m}(),on=function(){return(on=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},an=function(){function e(e){this.router=e}return e.prototype.fromConfig=function(e,t,n){return new vt(e,t,g.DefType.CONFIG,this.router.urlService.config,n)},e.prototype.fromPath=function(e,t,n){return new vt(e,t,g.DefType.PATH,this.router.urlService.config,n)},e.prototype.fromSearch=function(e,t,n){return new vt(e,t,g.DefType.SEARCH,this.router.urlService.config,n)},e}(),sn=function(){function e(e){var r=this;this.router=e,this.paramFactory=new an(this.router),this.caseInsensitive=function(e){return r.router.urlService.config.caseInsensitive(e)},this.defaultSquashPolicy=function(e){return r.router.urlService.config.defaultSquashPolicy(e)},this.strictMode=function(e){return r.router.urlService.config.strictMode(e)},this.type=function(e,t,n){return r.router.urlService.config.type(e,t,n)||r},Y(this,{UrlMatcher:rn,Param:vt})}return e.prototype.compile=function(e,t){var n=this.router.urlService.config,r=t&&!t.state&&t.params;t=r?on({state:{params:r}},t):t;var i={strict:n._isStrictMode,caseInsensitive:n._isCaseInsensitive};return new rn(e,n.paramTypes,this.paramFactory,Y(i,t))},e.prototype.isMatcher=function(n){if(!T(n))return!1;var r=!0;return N(rn.prototype,function(e,t){D(e)&&(r=r&&k(n[t])&&D(n[t]))}),r},e.prototype.$get=function(){var e=this.router.urlService.config;return e.paramTypes.enqueue=!1,e.paramTypes._flushTypeQueue(),this},e}(),un=function(){function e(e){this.router=e}return e.prototype.compile=function(e){return this.router.urlMatcherFactory.compile(e)},e.prototype.create=function(e,t){var n=this,r=Yt.isState,i=f([[O,function(e){return i(n.compile(e))}],[c(rn),function(e){return n.fromUrlMatcher(e,t)}],[r,function(e){return n.fromState(e,n.router)}],[c(RegExp),function(e){return n.fromRegExp(e,t)}],[D,function(e){return new ln(e,t)}]]),o=i(e);if(!o)throw new Error(\"invalid 'what' in when()\");return o},e.prototype.fromUrlMatcher=function(n,t){var e=t;O(t)&&(t=this.router.urlMatcherFactory.compile(t)),c(rn)(t)&&(e=function(e){return t.format(e)});var r={urlMatcher:n,matchPriority:function(t){var e=n.parameters().filter(function(e){return e.isOptional});return e.length?e.filter(function(e){return t[e.id]}).length/e.length:1e-6},type:\"URLMATCHER\"};return Y(new ln(function(e){var t=n.exec(e.path,e.search,e.hash);return n.validates(t)&&t},e),r)},e.prototype.fromState=function(r,i){var e={state:r,type:\"STATE\"};return Y(this.fromUrlMatcher(r.url,function(e){var t=i.stateService,n=i.globals;t.href(r,e)!==t.href(n.current,n.params)&&t.transitionTo(r,e,{inherit:!0,source:\"url\"})}),e)},e.prototype.fromRegExp=function(t,e){if(t.global||t.sticky)throw new Error(\"Rule RegExp must not be global or sticky\");var n=O(e)?function(n){return e.replace(/\\$(\\$|\\d{1,2})/,function(e,t){return n[\"$\"===t?0:Number(t)]})}:e,r={regexp:t,type:\"REGEXP\"};return Y(new ln(function(e){return t.exec(e.path)},n),r)},e.isUrlRule=function(t){return t&&[\"type\",\"match\",\"handler\"].every(function(e){return k(t[e])})},e}(),ln=function(e,t){var n=this;this.match=e,this.type=\"RAW\",this.matchPriority=function(e){return 0-n.$id},this.handler=t||q};var cn,dn=function(){function e(e){var r=this;this.router=e,this.sync=function(e){return r.router.urlService.sync(e)},this.listen=function(e){return r.router.urlService.listen(e)},this.deferIntercept=function(e){return r.router.urlService.deferIntercept(e)},this.match=function(e){return r.router.urlService.match(e)},this.initial=function(e){return r.router.urlService.rules.initial(e)},this.otherwise=function(e){return r.router.urlService.rules.otherwise(e)},this.removeRule=function(e){return r.router.urlService.rules.removeRule(e)},this.rule=function(e){return r.router.urlService.rules.rule(e)},this.rules=function(){return r.router.urlService.rules.rules()},this.sort=function(e){return r.router.urlService.rules.sort(e)},this.when=function(e,t,n){return r.router.urlService.rules.when(e,t,n)},this.urlRuleFactory=new un(e)}return e.prototype.update=function(e){var t=this.router.locationService;e?this.location=t.url():t.url()!==this.location&&t.url(this.location,!0)},e.prototype.push=function(e,t,n){var r=n&&!!n.replace;this.router.urlService.url(e.format(t||{}),r)},e.prototype.href=function(e,t,n){var r=e.format(t);if(null==r)return null;n=n||{absolute:!1};var i=this.router.urlService.config,o=i.html5Mode();if(o||null===r||(r=\"#\"+i.hashPrefix()+r),r=function(e,t,n,r){return\"/\"===r?e:t?We(r)+e:n?r.slice(1)+e:e}(r,o,n.absolute,i.baseHref()),!n.absolute||!r)return r;var a=!o&&r?\"/\":\"\",s=i.port(),u=80===s||443===s?\"\":\":\"+s;return[i.protocol(),\"://\",i.host(),u,a,r].join(\"\")},Object.defineProperty(e.prototype,\"interceptDeferred\",{get:function(){return this.router.urlService.interceptDeferred},enumerable:!0,configurable:!0}),e}(),pn=function(){function u(e){var n=this;this.router=e,this._uiViews=[],this._viewConfigs=[],this._viewConfigFactories={},this._listeners=[],this._pluginapi={_rootViewContext:this._rootViewContext.bind(this),_viewConfigFactory:this._viewConfigFactory.bind(this),_registeredUIView:function(t){return ue(n._uiViews,function(e){return n.router.$id+\".\"+e.id===t})},_registeredUIViews:function(){return n._uiViews},_activeViewConfigs:function(){return n._viewConfigs},_onSync:function(e){return n._listeners.push(e),function(){return Q(n._listeners,e)}}}}return u.normalizeUIViewTarget=function(e,t){void 0===t&&(t=\"\");var n=t.split(\"@\"),r=n[0]||\"$default\",i=O(n[1])?n[1]:\"^\",o=/^(\\^(?:\\.\\^)*)\\.(.*$)/.exec(r);o&&(i=o[1],r=o[2]),\"!\"===r.charAt(0)&&(r=r.substr(1),i=\"\");/^(\\^(?:\\.\\^)*)$/.exec(i)?i=i.split(\".\").reduce(function(e,t){return e.parent},e).name:\".\"===i&&(i=e.name);return{uiViewName:r,uiViewContextAnchor:i}},u.prototype._rootViewContext=function(e){return this._rootContext=e||this._rootContext},u.prototype._viewConfigFactory=function(e,t){this._viewConfigFactories[e]=t},u.prototype.createViewConfig=function(e,t){var n=this._viewConfigFactories[t.$type];if(!n)throw new Error(\"ViewService: No view config factory registered for type \"+t.$type);var r=n(e,t);return A(r)?r:[r]},u.prototype.deactivateViewConfig=function(e){lt.traceViewServiceEvent(\"<- Removing\",e),Q(this._viewConfigs,e)},u.prototype.activateViewConfig=function(e){lt.traceViewServiceEvent(\"-> Registering\",e),this._viewConfigs.push(e)},u.prototype.sync=function(){var n=this,r=this._uiViews.map(function(e){return[e.fqn,e]}).reduce(ke,{});function i(e){for(var t=e.viewDecl.$context,n=0;++n&&t.parent;)t=t.parent;return n}var o=l(function(e,t,n,r){return t*(e(n)-e(r))}),e=this._uiViews.sort(o(function(e){var t=function(e){return e&&e.parent?t(e.parent)+1:1};return 1e4*e.fqn.split(\".\").length+t(e.creationContext)},1)).map(function(e){var t=n._viewConfigs.filter(u.matches(r,e));return 1<t.length&&t.sort(o(i,-1)),{uiView:e,viewConfig:t[0]}}),t=e.map(function(e){return e.viewConfig}),a=this._viewConfigs.filter(function(e){return!G(t,e)}).map(function(e){return{uiView:void 0,viewConfig:e}});e.forEach(function(e){-1!==n._uiViews.indexOf(e.uiView)&&e.uiView.configUpdated(e.viewConfig)});var s=e.concat(a);this._listeners.forEach(function(e){return e(s)}),lt.traceViewSync(s)},u.prototype.registerUIView=function(t){lt.traceViewServiceUIViewEvent(\"-> Registering\",t);var e=this._uiViews;return e.filter(function(e){return e.fqn===t.fqn&&e.$type===t.$type}).length&&lt.traceViewServiceUIViewEvent(\"!!!! duplicate uiView named:\",t),e.push(t),this.sync(),function(){-1!==e.indexOf(t)?(lt.traceViewServiceUIViewEvent(\"<- Deregistering\",t),Q(e)(t)):lt.traceViewServiceUIViewEvent(\"Tried removing non-registered uiView\",t)}},u.prototype.available=function(){return this._uiViews.map(v(\"fqn\"))},u.prototype.active=function(){return this._uiViews.filter(v(\"$config\")).map(v(\"name\"))},u.matches=function(s,u){return function(e){if(u.$type!==e.viewDecl.$type)return!1;var t=e.viewDecl,n=t.$uiViewName.split(\".\"),r=u.fqn.split(\".\");if(!U(n,r.slice(0-n.length)))return!1;var i=1-n.length||void 0,o=r.slice(0,i).join(\".\"),a=s[o].creationContext;return t.$uiViewContextAnchor===(a&&a.name)}},u}(),hn=function(){function e(){this.params=new wt,this.lastStartedTransitionId=-1,this.transitionHistory=new Re([],1),this.successfulTransitions=new Re([],1)}return e.prototype.dispose=function(){this.transitionHistory.clear(),this.successfulTransitions.clear(),this.transition=null},e}();function fn(e){if(!(D(e)||O(e)||c($t)(e)||$t.isDef(e)))throw new Error(\"'handler' must be a string, function, TargetState, or have a state: 'newtarget' property\");return D(e)?e:p(e)}cn=function(e,t){var n=function(e,t){return(t.priority||0)-(e.priority||0)}(e,t);return 0!==n?n:0!==(n=function(e,t){var n={STATE:4,URLMATCHER:4,REGEXP:3,RAW:2,OTHER:1};return(n[e.type]||0)-(n[t.type]||0)}(e,t))?n:0!==(n=function(e,t){return e.urlMatcher&&t.urlMatcher?rn.compare(e.urlMatcher,t.urlMatcher):0}(e,t))?n:function(e,t){var n={STATE:!0,URLMATCHER:!0};return n[e.type]&&n[t.type]?0:(e.$id||0)-(t.$id||0)}(e,t)};var gn=function(){function e(e){this.router=e,this._sortFn=cn,this._rules=[],this._id=0,this.urlRuleFactory=new un(e)}return e.prototype.dispose=function(e){this._rules=[],delete this._otherwiseFn},e.prototype.initial=function(e){var t=fn(e);this.rule(this.urlRuleFactory.create(function(e,t){return 0===t.globals.transitionHistory.size()&&!!/^\\/?$/.exec(e.path)},t))},e.prototype.otherwise=function(e){var t=fn(e);this._otherwiseFn=this.urlRuleFactory.create(p(!0),t),this._sorted=!1},e.prototype.removeRule=function(e){Q(this._rules,e)},e.prototype.rule=function(e){var t=this;if(!un.isUrlRule(e))throw new Error(\"invalid rule\");return e.$id=this._id++,e.priority=e.priority||0,this._rules.push(e),this._sorted=!1,function(){return t.removeRule(e)}},e.prototype.rules=function(){return this.ensureSorted(),this._rules.concat(this._otherwiseFn?[this._otherwiseFn]:[])},e.prototype.sort=function(e){for(var t=this.stableSort(this._rules,this._sortFn=e||this._sortFn),n=0,r=0;r<t.length;r++)t[r]._group=n,r<t.length-1&&0!==this._sortFn(t[r],t[r+1])&&n++;this._rules=t,this._sorted=!0},e.prototype.ensureSorted=function(){this._sorted||this.sort()},e.prototype.stableSort=function(e,r){var t=e.map(function(e,t){return{elem:e,idx:t}});return t.sort(function(e,t){var n=r(e.elem,t.elem);return 0===n?e.idx-t.idx:n}),t.map(function(e){return e.elem})},e.prototype.when=function(e,t,n){var r=this.urlRuleFactory.create(e,t);return k(n&&n.priority)&&(r.priority=n.priority),this.rule(r),r},e}(),mn=function(){function e(e){var t=this;this.router=e,this.paramTypes=new yt,this._isCaseInsensitive=!1,this._isStrictMode=!0,this._defaultSquashPolicy=!1,this.dispose=function(){return t.paramTypes.dispose()},this.baseHref=function(){return t.router.locationConfig.baseHref()},this.hashPrefix=function(e){return t.router.locationConfig.hashPrefix(e)},this.host=function(){return t.router.locationConfig.host()},this.html5Mode=function(){return t.router.locationConfig.html5Mode()},this.port=function(){return t.router.locationConfig.port()},this.protocol=function(){return t.router.locationConfig.protocol()}}return e.prototype.caseInsensitive=function(e){return this._isCaseInsensitive=k(e)?e:this._isCaseInsensitive},e.prototype.defaultSquashPolicy=function(e){if(k(e)&&!0!==e&&!1!==e&&!O(e))throw new Error(\"Invalid squash policy: \"+e+\". Valid policies: false, true, arbitrary-string\");return this._defaultSquashPolicy=k(e)?e:this._defaultSquashPolicy},e.prototype.strictMode=function(e){return this._isStrictMode=k(e)?e:this._isStrictMode},e.prototype.type=function(e,t,n){var r=this.paramTypes.type(e,t,n);return k(t)?this:r},e}(),vn=function(){function e(e){var r=this;this.router=e,this.interceptDeferred=!1,this.rules=new gn(this.router),this.config=new mn(this.router),this.url=function(e,t,n){return r.router.locationService.url(e,t,n)},this.path=function(){return r.router.locationService.path()},this.search=function(){return r.router.locationService.search()},this.hash=function(){return r.router.locationService.hash()},this.onChange=function(e){return r.router.locationService.onChange(e)}}return e.prototype.dispose=function(){this.listen(!1),this.rules.dispose()},e.prototype.parts=function(){return{path:this.path(),search:this.search(),hash:this.hash()}},e.prototype.sync=function(e){if(!e||!e.defaultPrevented){var t=this.router,n=t.urlService,r=t.stateService,i={path:n.path(),search:n.search(),hash:n.hash()},o=this.match(i);f([[O,function(e){return n.url(e,!0)}],[$t.isDef,function(e){return r.go(e.state,e.params,e.options)}],[c($t),function(e){return r.go(e.state(),e.params(),e.options())}]])(o&&o.rule.handler(o.match,i,this.router))}},e.prototype.listen=function(e){var t=this;if(!1!==e)return this._stopListeningFn=this._stopListeningFn||this.router.urlService.onChange(function(e){return t.sync(e)});this._stopListeningFn&&this._stopListeningFn(),delete this._stopListeningFn},e.prototype.deferIntercept=function(e){void 0===e&&(e=!0),this.interceptDeferred=e},e.prototype.match=function(e){var t=this;e=Y({path:\"\",search:{},hash:\"\"},e);for(var n,r,i,o=this.rules.rules(),a=0;a<o.length&&(!n||n.rule._group===o[a]._group);a++){var s=(r=o[a],void 0,(i=r.match(e,t.router))&&{match:i,rule:r,weight:r.matchPriority(i)});n=!n||s&&s.weight>n.weight?s:n}return n},e}(),yn=0,wn=I(\"LocationServices\",[\"url\",\"path\",\"search\",\"hash\",\"onChange\"]),bn=I(\"LocationConfig\",[\"port\",\"protocol\",\"host\",\"baseHref\",\"html5Mode\",\"hashPrefix\"]),$n=function(){function e(e,t){void 0===e&&(e=wn),void 0===t&&(t=bn),this.locationService=e,this.locationConfig=t,this.$id=yn++,this._disposed=!1,this._disposables=[],this.trace=lt,this.viewService=new pn(this),this.globals=new hn,this.transitionService=new Un(this),this.urlMatcherFactory=new sn(this),this.urlRouter=new dn(this),this.urlService=new vn(this),this.stateRegistry=new zt(this),this.stateService=new qn(this),this._plugins={},this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()),this.globals.$current=this.stateRegistry.root(),this.globals.current=this.globals.$current.self,this.disposable(this.globals),this.disposable(this.stateService),this.disposable(this.stateRegistry),this.disposable(this.transitionService),this.disposable(this.urlService),this.disposable(e),this.disposable(t)}return e.prototype.disposable=function(e){this._disposables.push(e)},e.prototype.dispose=function(e){var t=this;e&&D(e.dispose)?e.dispose(this):(this._disposed=!0,this._disposables.slice().forEach(function(e){try{\"function\"==typeof e.dispose&&e.dispose(t),Q(t._disposables,e)}catch(e){}}))},e.prototype.plugin=function(e,t){void 0===t&&(t={});var n=new e(this,t);if(!n.name)throw new Error(\"Required property `name` missing on plugin: \"+n);return this._disposables.push(n),this._plugins[n.name]=n},e.prototype.getPlugin=function(e){return e?this._plugins[e]:de(this._plugins)},e}();function _n(t){t.addResolvable(kt.fromData($n,t.router),\"\"),t.addResolvable(kt.fromData(Jt,t),\"\"),t.addResolvable(kt.fromData(\"$transition$\",t),\"\"),t.addResolvable(kt.fromData(\"$stateParams\",t.params()),\"\"),t.entering().forEach(function(e){t.addResolvable(kt.fromData(\"$state$\",e),e)})}var Sn=G([\"$transition$\",Jt]),Cn=function(e){var t=de(e.treeChanges()).reduce(fe,[]).reduce(ve,[]),n=function(e){return Sn(e.token)?kt.fromData(e.token,null):e};t.forEach(function(e){e.resolvables=e.resolvables.map(n)})},kn=function(t){var e=t.to().redirectTo;if(e){var n=t.router.stateService;return D(e)?V.$q.when(e(t)).then(r):r(e)}function r(e){if(e)return e instanceof $t?e:O(e)?n.target(e,t.params(),t.options()):e.state||e.params?n.target(e.state||t.to(),e.params||t.params(),t.options()):void 0}};function Dn(n){return function(e,t){return(0,t.$$state()[n])(e,t)}}var xn=Dn(\"onExit\"),On=Dn(\"onRetain\"),Tn=Dn(\"onEnter\"),An=function(e){return new At(e.treeChanges().to).resolvePath(\"EAGER\",e).then(z)},En=function(e,t){return new At(e.treeChanges().to).subContext(t.$$state()).resolvePath(\"LAZY\",e).then(z)},Pn=function(e){return new At(e.treeChanges().to).resolvePath(\"LAZY\",e).then(z)},Mn=function(e){var t=V.$q,n=e.views(\"entering\");if(n.length)return t.all(n.map(function(e){return t.when(e.load())})).then(z)},Rn=function(e){var t=e.views(\"entering\"),n=e.views(\"exiting\");if(t.length||n.length){var r=e.router.viewService;n.forEach(function(e){return r.deactivateViewConfig(e)}),t.forEach(function(e){return r.activateViewConfig(e)}),r.sync()}},In=function(e){var t=e.router.globals,n=function(){t.transition===e&&(t.transition=null)};e.onSuccess({},function(){t.successfulTransitions.enqueue(e),t.$current=e.$to(),t.current=t.$current.self,xe(e.params(),t.params)},{priority:1e4}),e.promise.then(n,n)},Vn=function(e){var t=e.options(),n=e.router.stateService,r=e.router.urlRouter;if(\"url\"!==t.source&&t.location&&n.$current.navigable){var i={replace:\"replace\"===t.location};r.push(n.$current.navigable.url,n.params,i)}r.update(!0)},Ln=function(a){var s=a.router;var e=a.entering().filter(function(e){return!!e.$$state().lazyLoad}).map(function(e){return Fn(a,e)});return V.$q.all(e).then(function(){if(\"url\"!==a.originalTransition().options().source){var e=a.targetState();return s.stateService.target(e.identifier(),e.params(),e.options())}var t=s.urlService,n=t.match(t.parts()),r=n&&n.rule;if(r&&\"STATE\"===r.type){var i=r.state,o=n.match;return s.stateService.target(i,o,a.options())}s.urlService.sync()})};function Fn(t,n){var r=n.$$state().lazyLoad,e=r._promise;if(!e){e=r._promise=V.$q.when(r(t,n)).then(function(e){e&&Array.isArray(e.states)&&e.states.forEach(function(e){return t.router.stateRegistry.register(e)});return e}).then(function(e){return delete n.lazyLoad,delete n.$$state().lazyLoad,delete r._promise,e},function(e){return delete r._promise,V.$q.reject(e)})}return e}var jn=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1),this.name=e,this.hookPhase=t,this.hookOrder=n,this.criteriaMatchPath=r,this.reverseSort=i,this.getResultHandler=o,this.getErrorHandler=a,this.synchronous=s};function Hn(e){var t=e._ignoredReason();if(t){lt.traceTransitionIgnored(e);var n=e.router.globals.transition;return\"SameAsCurrent\"===t&&n&&n.abort(),Ve.ignored().toPromise()}}function Nn(e){if(!e.valid())throw new Error(e.error().toString())}var Yn={location:!0,relative:null,inherit:!1,notify:!0,reload:!1,custom:{},current:function(){return null},source:\"unknown\"},Un=function(){function e(e){this._transitionCount=0,this._eventTypes=[],this._registeredHooks={},this._criteriaPaths={},this._router=e,this.$view=e.viewService,this._deregisterHookFns={},this._pluginapi=B(p(this),{},p(this),[\"_definePathType\",\"_defineEvent\",\"_getPathTypes\",\"_getEvents\",\"getHooks\"]),this._defineCorePaths(),this._defineCoreEvents(),this._registerCoreTransitionHooks(),e.globals.successfulTransitions.onEvict(Cn)}return e.prototype.onCreate=function(e,t,n){},e.prototype.onBefore=function(e,t,n){},e.prototype.onStart=function(e,t,n){},e.prototype.onExit=function(e,t,n){},e.prototype.onRetain=function(e,t,n){},e.prototype.onEnter=function(e,t,n){},e.prototype.onFinish=function(e,t,n){},e.prototype.onSuccess=function(e,t,n){},e.prototype.onError=function(e,t,n){},e.prototype.dispose=function(e){de(this._registeredHooks).forEach(function(t){return t.forEach(function(e){e._deregistered=!0,Q(t,e)})})},e.prototype.create=function(e,t){return new Jt(e,t,this._router)},e.prototype._defineCoreEvents=function(){var e=g.TransitionHookPhase,t=Wt,n=this._criteriaPaths;this._defineEvent(\"onCreate\",e.CREATE,0,n.to,!1,t.LOG_REJECTED_RESULT,t.THROW_ERROR,!0),this._defineEvent(\"onBefore\",e.BEFORE,0,n.to),this._defineEvent(\"onStart\",e.RUN,0,n.to),this._defineEvent(\"onExit\",e.RUN,100,n.exiting,!0),this._defineEvent(\"onRetain\",e.RUN,200,n.retained),this._defineEvent(\"onEnter\",e.RUN,300,n.entering),this._defineEvent(\"onFinish\",e.RUN,400,n.to),this._defineEvent(\"onSuccess\",e.SUCCESS,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0),this._defineEvent(\"onError\",e.ERROR,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0)},e.prototype._defineCorePaths=function(){var e=g.TransitionHookScope.STATE,t=g.TransitionHookScope.TRANSITION;this._definePathType(\"to\",t),this._definePathType(\"from\",t),this._definePathType(\"exiting\",e),this._definePathType(\"retained\",e),this._definePathType(\"entering\",e)},e.prototype._defineEvent=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1);var u=new jn(e,t,n,r,i,o,a,s);this._eventTypes.push(u),Qt(this,this,u)},e.prototype._getEvents=function(t){return(k(t)?this._eventTypes.filter(function(e){return e.hookPhase===t}):this._eventTypes.slice()).sort(function(e,t){var n=e.hookPhase-t.hookPhase;return 0==n?e.hookOrder-t.hookOrder:n})},e.prototype._definePathType=function(e,t){this._criteriaPaths[e]={name:e,scope:t}},e.prototype._getPathTypes=function(){return this._criteriaPaths},e.prototype.getHooks=function(e){return this._registeredHooks[e]},e.prototype._registerCoreTransitionHooks=function(){var e=this._deregisterHookFns;e.addCoreResolves=function(e){return e.onCreate({},_n)}(this),e.ignored=function(e){return e.onBefore({},Hn,{priority:-9999})}(this),e.invalid=function(e){return e.onBefore({},Nn,{priority:-1e4})}(this),e.redirectTo=function(e){return e.onStart({to:function(e){return!!e.redirectTo}},kn)}(this),e.onExit=function(e){return e.onExit({exiting:function(e){return!!e.onExit}},xn)}(this),e.onRetain=function(e){return e.onRetain({retained:function(e){return!!e.onRetain}},On)}(this),e.onEnter=function(e){return e.onEnter({entering:function(e){return!!e.onEnter}},Tn)}(this),e.eagerResolve=function(e){return e.onStart({},An,{priority:1e3})}(this),e.lazyResolve=function(e){return e.onEnter({entering:p(!0)},En,{priority:1e3})}(this),e.resolveAll=function(e){return e.onFinish({},Pn,{priority:1e3})}(this),e.loadViews=function(e){return e.onFinish({},Mn)}(this),e.activateViews=function(e){return e.onSuccess({},Rn)}(this),e.updateGlobals=function(e){return e.onCreate({},In)}(this),e.updateUrl=function(e){return e.onSuccess({},Vn,{priority:9999})}(this),e.lazyLoad=function(e){return e.onBefore({entering:function(e){return!!e.lazyLoad}},Ln)}(this)},e}(),qn=function(){function n(e){this.router=e,this.invalidCallbacks=[],this._defaultErrorHandler=function(e){e instanceof Error&&e.stack?(console.error(e),console.error(e.stack)):e instanceof Ve?(console.error(e.toString()),e.detail&&e.detail.stack&&console.error(e.detail.stack)):console.error(e)};var t=Object.keys(n.prototype).filter(d(G([\"current\",\"$current\",\"params\",\"transition\"])));B(p(n.prototype),this,p(this),t)}return Object.defineProperty(n.prototype,\"transition\",{get:function(){return this.router.globals.transition},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"params\",{get:function(){return this.router.globals.params},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"current\",{get:function(){return this.router.globals.current},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,\"$current\",{get:function(){return this.router.globals.$current},enumerable:!0,configurable:!0}),n.prototype.dispose=function(){this.defaultErrorHandler(z),this.invalidCallbacks=[]},n.prototype._handleInvalidTargetState=function(e,n){var r=this,i=_t.makeTargetState(this.router.stateRegistry,e),t=this.router.globals,o=function(){return t.transitionHistory.peekTail()},a=o(),s=new Re(this.invalidCallbacks.slice()),u=new At(e).injector(),l=function(e){if(e instanceof $t){var t=e;return(t=r.target(t.identifier(),t.params(),t.options())).valid()?o()!==a?Ve.superseded().toPromise():r.transitionTo(t.identifier(),t.params(),t.options()):Ve.invalid(t.error()).toPromise()}};return function t(){var e=s.dequeue();return void 0===e?Ve.invalid(n.error()).toPromise():V.$q.when(e(n,i,u)).then(l).then(function(e){return e||t()})}()},n.prototype.onInvalid=function(e){return this.invalidCallbacks.push(e),function(){Q(this.invalidCallbacks)(e)}.bind(this)},n.prototype.reload=function(e){return this.transitionTo(this.current,this.params,{reload:!k(e)||e,inherit:!1,notify:!1})},n.prototype.go=function(e,t,n){var r=te(n,{relative:this.$current,inherit:!0},Yn);return this.transitionTo(e,t,r)},n.prototype.target=function(e,t,n){if(void 0===n&&(n={}),T(n.reload)&&!n.reload.name)throw new Error(\"Invalid reload state object\");var r=this.router.stateRegistry;if(n.reloadState=!0===n.reload?r.root():r.matcher.find(n.reload,n.relative),n.reload&&!n.reloadState)throw new Error(\"No such reload state '\"+(O(n.reload)?n.reload:n.reload.name)+\"'\");return new $t(this.router.stateRegistry,e,t,n)},n.prototype.getCurrentPath=function(){var e=this,t=this.router.globals.successfulTransitions.peekTail();return t?t.treeChanges().to:[new bt(e.router.stateRegistry.root())]},n.prototype.transitionTo=function(e,t,n){var o=this;void 0===t&&(t={}),void 0===n&&(n={});var a=this.router,s=a.globals;n=te(n,Yn);n=Y(n,{current:function(){return s.transition}});var r=this.target(e,t,n),i=this.getCurrentPath();if(!r.exists())return this._handleInvalidTargetState(i,r);if(!r.valid())return Pe(r.error());var u=function(i){return function(e){if(e instanceof Ve){var t=a.globals.lastStartedTransitionId<=i.$id;if(e.type===g.RejectType.IGNORED)return t&&a.urlRouter.update(),V.$q.when(s.current);var n=e.detail;if(e.type===g.RejectType.SUPERSEDED&&e.redirected&&n instanceof $t){var r=i.redirect(n);return r.run().catch(u(r))}if(e.type===g.RejectType.ABORTED)return t&&a.urlRouter.update(),V.$q.reject(e)}return o.defaultErrorHandler()(e),V.$q.reject(e)}},l=this.router.transitionService.create(i,r),c=l.run().catch(u(l));return Ee(c),Y(c,{transition:l})},n.prototype.is=function(e,t,n){n=te(n,{relative:this.$current});var r=this.router.stateRegistry.matcher.find(e,n.relative);if(k(r)){if(this.$current!==r)return!1;if(!t)return!0;var i=r.parameters({inherit:!0,matchingKeys:t});return vt.equals(i,vt.values(i,t),this.params)}},n.prototype.includes=function(e,t,n){n=te(n,{relative:this.$current});var r=O(e)&&Me.fromString(e);if(r){if(!r.matches(this.$current.name))return!1;e=this.$current.name}var i=this.router.stateRegistry.matcher.find(e,n.relative),o=this.$current.includes;if(k(i)){if(!k(o[i.name]))return!1;if(!t)return!0;var a=i.parameters({inherit:!0,matchingKeys:t});return vt.equals(a,vt.values(a,t),this.params)}},n.prototype.href=function(e,t,n){n=te(n,{lossy:!0,inherit:!0,absolute:!1,relative:this.$current}),t=t||{};var r=this.router.stateRegistry.matcher.find(e,n.relative);if(!k(r))return null;n.inherit&&(t=this.params.$inherit(t,this.$current,r));var i=r&&n.lossy?r.navigable:r;return i&&void 0!==i.url&&null!==i.url?this.router.urlRouter.href(i.url,t,{absolute:n.absolute}):null},n.prototype.defaultErrorHandler=function(e){return this._defaultErrorHandler=e||this._defaultErrorHandler},n.prototype.get=function(e,t){var n=this.router.stateRegistry;return 0===arguments.length?n.get():n.get(e,t||this.$current)},n.prototype.lazyLoad=function(e,t){var n=this.get(e);if(!n||!n.lazyLoad)throw new Error(\"Can not lazy load \"+e);var r=this.getCurrentPath(),i=_t.makeTargetState(this.router.stateRegistry,r);return Fn(t=t||this.router.transitionService.create(r,i),n)},n}(),zn={when:function(n){return new Promise(function(e,t){return e(n)})},reject:function(n){return new Promise(function(e,t){t(n)})},defer:function(){var n={};return n.promise=new Promise(function(e,t){n.resolve=e,n.reject=t}),n},all:function(e){if(A(e))return Promise.all(e);if(T(e)){var t=Object.keys(e).map(function(t){return e[t].then(function(e){return{key:t,val:e}})});return zn.all(t).then(function(e){return e.reduce(function(e,t){return e[t.key]=t.val,e},{})})}}},Bn={},Wn=/((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm,Gn=/([^\\s,]+)/g,Kn={get:function(e){return Bn[e]},has:function(e){return null!=Kn.get(e)},invoke:function(e,t,n){var r=Y({},Bn,n||{}),i=Kn.annotate(e),o=be(function(e){return r.hasOwnProperty(e)},function(e){return\"DI can't find injectable: '\"+e+\"'\"}),a=i.filter(o).map(function(e){return r[e]});return D(e)?e.apply(t,a):e.slice(-1)[0].apply(t,a)},annotate:function(e){if(!M(e))throw new Error(\"Not an injectable function: \"+e);if(e&&e.$inject)return e.$inject;if(A(e))return e.slice(0,-1);var t=e.toString().replace(Wn,\"\");return t.slice(t.indexOf(\"(\")+1,t.indexOf(\")\")).match(Gn)||[]}},Qn=function(e,t){var n=t[0],r=t[1];return e.hasOwnProperty(n)?A(e[n])?e[n].push(r):e[n]=[e[n],r]:e[n]=r,e},Zn=function(e){return e.split(\"&\").filter(q).map(Qe).reduce(Qn,{})};function Xn(e){var t=function(e){return e||\"\"},n=Ge(e).map(t),r=n[0],i=n[1],o=Ke(r).map(t);return{path:o[0],search:o[1],hash:i,url:e}}var Jn=function(e){var t=e.path(),n=e.search(),r=e.hash(),i=Object.keys(n).map(function(t){var e=n[t];return(A(e)?e:[e]).map(function(e){return t+\"=\"+e})}).reduce(fe,[]).join(\"&\");return t+(i?\"?\"+i:\"\")+(r?\"#\"+r:\"\")};function er(r,i,o,a){return function(e){var t=e.locationService=new o(e),n=e.locationConfig=new a(e,i);return{name:r,service:t,configuration:n,dispose:function(e){e.dispose(t),e.dispose(n)}}}}var tr,nr,rr,ir=function(){function e(e,t){var n=this;this.fireAfterUpdate=t,this._listeners=[],this._listener=function(t){return n._listeners.forEach(function(e){return e(t)})},this.hash=function(){return Xn(n._get()).hash},this.path=function(){return Xn(n._get()).path},this.search=function(){return Zn(Xn(n._get()).search)},this._location=L.location,this._history=L.history}return e.prototype.url=function(t,e){return void 0===e&&(e=!0),k(t)&&t!==this._get()&&(this._set(null,null,t,e),this.fireAfterUpdate&&this._listeners.forEach(function(e){return e({url:t})})),Jn(this)},e.prototype.onChange=function(e){var t=this;return this._listeners.push(e),function(){return Q(t._listeners,e)}},e.prototype.dispose=function(e){ee(this._listeners)},e}(),or=(tr=function(e,t){return(tr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}tr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ar=function(n){function e(e){var t=n.call(this,e,!1)||this;return L.addEventListener(\"hashchange\",t._listener,!1),t}return or(e,n),e.prototype._get=function(){return Ze(this._location.hash)},e.prototype._set=function(e,t,n,r){this._location.hash=n},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener(\"hashchange\",this._listener)},e}(ir),sr=(nr=function(e,t){return(nr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}nr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ur=function(t){function e(e){return t.call(this,e,!0)||this}return sr(e,t),e.prototype._get=function(){return this._url},e.prototype._set=function(e,t,n,r){this._url=n},e}(ir),lr=(rr=function(e,t){return(rr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}rr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),cr=function(n){function e(e){var t=n.call(this,e,!0)||this;return t._config=e.urlService.config,L.addEventListener(\"popstate\",t._listener,!1),t}return lr(e,n),e.prototype._getBasePrefix=function(){return We(this._config.baseHref())},e.prototype._get=function(){var e=this._location,t=e.pathname,n=e.hash,r=e.search;r=Ke(r)[1],n=Ge(n)[1];var i=this._getBasePrefix(),o=t===this._config.baseHref(),a=t.substr(0,i.length)===i;return(t=o?\"/\":a?t.substring(i.length):t)+(r?\"?\"+r:\"\")+(n?\"#\"+n:\"\")},e.prototype._set=function(e,t,n,r){var i=this._getBasePrefix(),o=n&&\"/\"!==n[0]?\"/\":\"\",a=\"\"===n||\"/\"===n?this._config.baseHref():i+o+n;r?this._history.replaceState(e,t,a):this._history.pushState(e,t,a)},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener(\"popstate\",this._listener)},e}(ir),dr=function(){var t=this;this.dispose=z,this._baseHref=\"\",this._port=80,this._protocol=\"http\",this._host=\"localhost\",this._hashPrefix=\"\",this.port=function(){return t._port},this.protocol=function(){return t._protocol},this.host=function(){return t._host},this.baseHref=function(){return t._baseHref},this.html5Mode=function(){return!1},this.hashPrefix=function(e){return k(e)?t._hashPrefix=e:t._hashPrefix}},pr=function(){function e(e,t){void 0===t&&(t=!1),this._isHtml5=t,this._baseHref=void 0,this._hashPrefix=\"\"}return e.prototype.port=function(){return location.port?Number(location.port):\"https\"===this.protocol()?443:80},e.prototype.protocol=function(){return location.protocol.replace(/:/g,\"\")},e.prototype.host=function(){return location.hostname},e.prototype.html5Mode=function(){return this._isHtml5},e.prototype.hashPrefix=function(e){return k(e)?this._hashPrefix=e:this._hashPrefix},e.prototype.baseHref=function(e){return k(e)&&(this._baseHref=e),b(this._baseHref)&&(this._baseHref=this.getBaseHref()),this._baseHref},e.prototype.getBaseHref=function(){var e=document.getElementsByTagName(\"base\")[0];return e&&e.href?e.href.replace(/^([^/:]*:)?\\/\\/[^/]*/,\"\"):this._isHtml5?\"/\":location.pathname||\"/\"},e.prototype.dispose=function(){},e}();function hr(e){return V.$injector=Kn,{name:\"vanilla.services\",$q:V.$q=zn,$injector:Kn,dispose:function(){return null}}}var fr=er(\"vanilla.hashBangLocation\",!1,ar,pr),gr=er(\"vanilla.pushStateLocation\",!0,cr,pr),mr=er(\"vanilla.memoryLocation\",!1,ur,dr),vr=function(){function e(){}return e.prototype.dispose=function(e){},e}(),yr=Object.freeze({root:L,fromJson:j,toJson:H,forEach:N,extend:Y,equals:U,identity:q,noop:z,createProxyFunctions:B,inherit:W,inArray:G,_inArray:K,removeFrom:Q,_removeFrom:Z,pushTo:X,_pushTo:J,deregAll:ee,defaults:te,mergeR:ne,ancestors:re,pick:ie,omit:oe,pluck:ae,filter:se,find:ue,mapObj:le,map:ce,values:de,allTrueR:pe,anyTrueR:he,unnestR:fe,flattenR:ge,pushR:me,uniqR:ve,unnest:ye,flatten:we,assertPredicate:be,assertMap:$e,assertFn:_e,pairs:Se,arrayTuples:Ce,applyPairs:ke,tail:De,copy:xe,_extend:Oe,silenceUncaughtInPromise:Ee,silentRejection:Pe,makeStub:I,services:V,Glob:Me,curry:l,compose:n,pipe:u,prop:v,propEq:y,parse:C,not:d,and:r,or:i,all:a,any:s,is:c,eq:o,val:p,invoke:h,pattern:f,isUndefined:b,isDefined:k,isNull:$,isNullOrUndefined:_,isFunction:D,isNumber:x,isString:O,isObject:T,isArray:A,isDate:E,isRegExp:P,isInjectable:M,isPromise:R,Queue:Re,maxLength:Le,padString:Fe,kebobString:je,functionToString:He,fnToString:Ne,stringify:qe,beforeAfterSubstr:ze,hostRegex:Be,stripLastPathElement:We,splitHash:Ge,splitQuery:Ke,splitEqual:Qe,trimHashVal:Ze,splitOnDelim:Xe,joinNeighborsR:Je,get Category(){return g.Category},Trace:ut,trace:lt,get DefType(){return g.DefType},Param:vt,ParamTypes:yt,StateParams:wt,ParamType:ct,PathNode:bt,PathUtils:_t,resolvePolicies:St,defaultResolvePolicy:Ct,Resolvable:kt,NATIVE_INJECTOR_TOKEN:Tt,ResolveContext:At,resolvablesBuilder:Ft,StateBuilder:Nt,StateObject:Yt,StateMatcher:Ut,StateQueueManager:qt,StateRegistry:zt,StateService:qn,TargetState:$t,get TransitionHookPhase(){return g.TransitionHookPhase},get TransitionHookScope(){return g.TransitionHookScope},HookBuilder:Zt,matchState:Gt,RegisteredHook:Kt,makeEvent:Qt,get RejectType(){return g.RejectType},Rejection:Ve,Transition:Jt,TransitionHook:Wt,TransitionEventType:jn,defaultTransOpts:Yn,TransitionService:Un,UrlRules:gn,UrlConfig:mn,UrlMatcher:rn,ParamFactory:an,UrlMatcherFactory:sn,UrlRouter:dn,UrlRuleFactory:un,BaseUrlRule:ln,UrlService:vn,ViewService:pn,UIRouterGlobals:hn,UIRouter:$n,$q:zn,$injector:Kn,BaseLocationServices:ir,HashLocationService:ar,MemoryLocationService:ur,PushStateLocationService:cr,MemoryLocationConfig:dr,BrowserLocationConfig:pr,keyValsToObjectR:Qn,getParams:Zn,parseUrl:Xn,buildUrl:Jn,locationPluginFactory:er,servicesPlugin:hr,hashLocationPlugin:fr,pushStateLocationPlugin:gr,memoryLocationPlugin:mr,UIRouterPluginBase:vr});function wr(){var n=null;return function(e,t){return n=n||V.$injector.get(\"$templateFactory\"),[new Sr(e,t,n)]}}var br=function(e,n){return e.reduce(function(e,t){return e||k(n[t])},!1)};function $r(r){if(!r.parent)return{};var i=[\"component\",\"bindings\",\"componentProvider\"],o=[\"templateProvider\",\"templateUrl\",\"template\",\"notify\",\"async\"].concat([\"controller\",\"controllerProvider\",\"controllerAs\",\"resolveAs\"]),e=i.concat(o);if(k(r.views)&&br(e,r))throw new Error(\"State '\"+r.name+\"' has a 'views' object. It cannot also have \\\"view properties\\\" at the state level.  Move the following properties into a view (in the 'views' object):  \"+e.filter(function(e){return k(r[e])}).join(\", \"));var a={},t=r.views||{$default:ie(r,e)};return N(t,function(e,t){if(t=t||\"$default\",O(e)&&(e={component:e}),e=Y({},e),br(i,e)&&br(o,e))throw new Error(\"Cannot combine: \"+i.join(\"|\")+\" with: \"+o.join(\"|\")+\" in stateview: '\"+t+\"@\"+r.name+\"'\");e.resolveAs=e.resolveAs||\"$resolve\",e.$type=\"ng1\",e.$context=r,e.$name=t;var n=pn.normalizeUIViewTarget(e.$context,e.$name);e.$uiViewName=n.uiViewName,e.$uiViewContextAnchor=n.uiViewContextAnchor,a[t]=e}),a}var _r=0,Sr=function(){function e(e,t,n){var r=this;this.path=e,this.viewDecl=t,this.factory=n,this.$id=_r++,this.loaded=!1,this.getTemplate=function(e,t){return r.component?r.factory.makeComponentTemplate(e,t,r.component,r.viewDecl.bindings):r.template}}return e.prototype.load=function(){var t=this,e=V.$q,n=new At(this.path),r=this.path.reduce(function(e,t){return Y(e,t.paramValues)},{}),i={template:e.when(this.factory.fromConfig(this.viewDecl,r,n)),controller:e.when(this.getController(n))};return e.all(i).then(function(e){return lt.traceViewServiceEvent(\"Loaded\",t),t.controller=e.controller,Y(t,e.template),t})},e.prototype.getController=function(e){var t=this.viewDecl.controllerProvider;if(!M(t))return this.viewDecl.controller;var n=V.$injector.annotate(t),r=A(t)?De(t):t;return new kt(\"\",r,n).get(e)},e}(),Cr=function(){function e(){var r=this;this._useHttp=S.version.minor<3,this.$get=[\"$http\",\"$templateCache\",\"$injector\",function(e,t,n){return r.$templateRequest=n.has&&n.has(\"$templateRequest\")&&n.get(\"$templateRequest\"),r.$http=e,r.$templateCache=t,r}]}return e.prototype.useHttpService=function(e){this._useHttp=e},e.prototype.fromConfig=function(e,t,n){var r=function(e){return V.$q.when(e).then(function(e){return{template:e}})},i=function(e){return V.$q.when(e).then(function(e){return{component:e}})};return k(e.template)?r(this.fromString(e.template,t)):k(e.templateUrl)?r(this.fromUrl(e.templateUrl,t)):k(e.templateProvider)?r(this.fromProvider(e.templateProvider,t,n)):k(e.component)?i(e.component):k(e.componentProvider)?i(this.fromComponentProvider(e.componentProvider,t,n)):r(\"<ui-view></ui-view>\")},e.prototype.fromString=function(e,t){return D(e)?e(t):e},e.prototype.fromUrl=function(e,t){return D(e)&&(e=e(t)),null==e?null:this._useHttp?this.$http.get(e,{cache:this.$templateCache,headers:{Accept:\"text/html\"}}).then(function(e){return e.data}):this.$templateRequest(e)},e.prototype.fromProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt(\"\",i,r).get(n)},e.prototype.fromComponentProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt(\"\",i,r).get(n)},e.prototype.makeComponentTemplate=function(u,l,e,c){c=c||{};var d=3<=S.version.minor?\"::\":\"\",p=function(e){var t=je(e);return/^(x|data)-/.exec(t)?\"x-\"+t:t},t=function(e){var t=V.$injector.get(e+\"Directive\");if(!t||!t.length)throw new Error(\"Unable to find component named '\"+e+\"'\");return t.map(kr).reduce(fe,[])}(e).map(function(e){var t=e.name,n=e.type,r=p(t);if(u.attr(r)&&!c[t])return r+\"='\"+u.attr(r)+\"'\";var i=c[t]||t;if(\"@\"===n)return r+\"='{{\"+d+\"$resolve.\"+i+\"}}'\";if(\"&\"!==n)return r+\"='\"+d+\"$resolve.\"+i+\"'\";var o=l.getResolvable(i),a=o&&o.data,s=a&&V.$injector.annotate(a)||[];return r+\"='$resolve.\"+i+(A(a)?\"[\"+(a.length-1)+\"]\":\"\")+\"(\"+s.join(\",\")+\")'\"}).join(\" \"),n=p(e);return\"<\"+n+\" \"+t+\"></\"+n+\">\"},e}();var kr=function(e){return T(e.bindToController)?Dr(e.bindToController):Dr(e.scope)},Dr=function(t){return Object.keys(t||{}).map(function(e){return[e,/^([=<@&])[?]?(.*)/.exec(t[e])]}).filter(function(e){return k(e)&&A(e[1])}).map(function(e){return{name:e[1][2]||e[0],type:e[1][1]}})},xr=function(){function n(e,t){this.stateRegistry=e,this.stateService=t,B(p(n.prototype),this,p(this))}return n.prototype.decorator=function(e,t){return this.stateRegistry.decorator(e,t)||this},n.prototype.state=function(e,t){return T(e)?t=e:t.name=e,this.stateRegistry.register(t),this},n.prototype.onInvalid=function(e){return this.stateService.onInvalid(e)},n}(),Or=function(n){return function(e,t){var i=e[n],o=\"onExit\"===n?\"from\":\"to\";return i?function(e,t){var n=new At(e.treeChanges(o)).subContext(t.$$state()),r=Y(zr(n),{$state$:t,$transition$:e});return V.$injector.invoke(i,this,r)}:void 0}},Tr=function(){function e(e){this._urlListeners=[],this.$locationProvider=e;var t=p(e);B(t,this,t,[\"hashPrefix\"])}return e.monkeyPatchPathParameterType=function(e){var t=e.urlMatcherFactory.type(\"path\");t.encode=function(e){return null!=e?e.toString().replace(/(~|\\/)/g,function(e){return{\"~\":\"~~\",\"/\":\"~2F\"}[e]}):e},t.decode=function(e){return null!=e?e.toString().replace(/(~~|~2F)/g,function(e){return{\"~~\":\"~\",\"~2F\":\"/\"}[e]}):e}},e.prototype.dispose=function(){},e.prototype.onChange=function(e){var t=this;return this._urlListeners.push(e),function(){return Q(t._urlListeners)(e)}},e.prototype.html5Mode=function(){var e=this.$locationProvider.html5Mode();return(e=T(e)?e.enabled:e)&&this.$sniffer.history},e.prototype.baseHref=function(){return this._baseHref||(this._baseHref=this.$browser.baseHref()||this.$window.location.pathname)},e.prototype.url=function(e,t,n){return void 0===t&&(t=!1),k(e)&&this.$location.url(e),t&&this.$location.replace(),n&&this.$location.state(n),this.$location.url()},e.prototype._runtimeServices=function(e,t,n,r,i){var o=this;this.$location=t,this.$sniffer=n,this.$browser=r,this.$window=i,e.$on(\"$locationChangeSuccess\",function(t){return o._urlListeners.forEach(function(e){return e(t)})});var a=p(t);B(a,this,a,[\"replace\",\"path\",\"search\",\"hash\"]),B(a,this,a,[\"port\",\"protocol\",\"host\"])},e}(),Ar=function(){function n(e){this.router=e}return n.injectableHandler=function(t,n){return function(e){return V.$injector.invoke(n,null,{$match:e,$stateParams:t.globals.params})}},n.prototype.$get=function(){var e=this.router.urlService;return this.router.urlRouter.update(!0),e.interceptDeferred||e.listen(),this.router.urlRouter},n.prototype.rule=function(e){var t=this;if(!D(e))throw new Error(\"'rule' must be a function\");var n=new ln(function(){return e(V.$injector,t.router.locationService)},q);return this.router.urlService.rules.rule(n),this},n.prototype.otherwise=function(e){var t=this,n=this.router.urlService.rules;if(O(e))n.otherwise(e);else{if(!D(e))throw new Error(\"'rule' must be a string or function\");n.otherwise(function(){return e(V.$injector,t.router.locationService)})}return this},n.prototype.when=function(e,t){return(A(t)||D(t))&&(t=n.injectableHandler(this.router,t)),this.router.urlService.rules.when(e,t),this},n.prototype.deferIntercept=function(e){this.router.urlService.deferIntercept(e)},n}();S.module(\"ui.router.angular1\",[]);var Er=S.module(\"ui.router.init\",[\"ng\"]),Pr=S.module(\"ui.router.util\",[\"ui.router.init\"]),Mr=S.module(\"ui.router.router\",[\"ui.router.util\"]),Rr=S.module(\"ui.router.state\",[\"ui.router.router\",\"ui.router.util\",\"ui.router.angular1\"]),Ir=S.module(\"ui.router\",[\"ui.router.init\",\"ui.router.state\",\"ui.router.angular1\"]),Vr=(S.module(\"ui.router.compat\",[\"ui.router\"]),null);function Lr(e){(Vr=this.router=new $n).stateProvider=new xr(Vr.stateRegistry,Vr.stateService),Vr.stateRegistry.decorator(\"views\",$r),Vr.stateRegistry.decorator(\"onExit\",Or(\"onExit\")),Vr.stateRegistry.decorator(\"onRetain\",Or(\"onRetain\")),Vr.stateRegistry.decorator(\"onEnter\",Or(\"onEnter\")),Vr.viewService._pluginapi._viewConfigFactory(\"ng1\",wr());var s=Vr.locationService=Vr.locationConfig=new Tr(e);function t(e,t,n,r,i,o,a){return s._runtimeServices(i,e,r,t,n),delete Vr.router,delete Vr.$get,Vr}return Tr.monkeyPatchPathParameterType(Vr),((Vr.router=Vr).$get=t).$inject=[\"$location\",\"$browser\",\"$window\",\"$sniffer\",\"$rootScope\",\"$http\",\"$templateCache\"],Vr}Lr.$inject=[\"$locationProvider\"];var Fr=function(n){return[\"$uiRouterProvider\",function(e){var t=e.router[n];return t.$get=function(){return t},t}]};function jr(t,e,n){if(V.$injector=t,V.$q=e,!t.hasOwnProperty(\"strictDi\"))try{t.invoke(function(e){})}catch(e){t.strictDi=!!/strict mode/.exec(e&&e.toString())}n.stateRegistry.get().map(function(e){return e.$$state().resolvables}).reduce(fe,[]).filter(function(e){return\"deferred\"===e.deps}).forEach(function(e){return e.deps=t.annotate(e.resolveFn,t.strictDi)})}jr.$inject=[\"$injector\",\"$q\",\"$uiRouter\"];function Hr(e){e.$watch(function(){lt.approximateDigests++})}Hr.$inject=[\"$rootScope\"],Er.provider(\"$uiRouter\",Lr),Mr.provider(\"$urlRouter\",[\"$uiRouterProvider\",function(e){return e.urlRouterProvider=new Ar(e)}]),Pr.provider(\"$urlService\",Fr(\"urlService\")),Pr.provider(\"$urlMatcherFactory\",[\"$uiRouterProvider\",function(){return Vr.urlMatcherFactory}]),Pr.provider(\"$templateFactory\",function(){return new Cr}),Rr.provider(\"$stateRegistry\",Fr(\"stateRegistry\")),Rr.provider(\"$uiRouterGlobals\",Fr(\"globals\")),Rr.provider(\"$transitions\",Fr(\"transitionService\")),Rr.provider(\"$state\",[\"$uiRouterProvider\",function(){return Y(Vr.stateProvider,{$get:function(){return Vr.stateService}})}]),Rr.factory(\"$stateParams\",[\"$uiRouter\",function(e){return e.globals.params}]),Ir.factory(\"$view\",function(){return Vr.viewService}),Ir.service(\"$trace\",function(){return lt}),Ir.run(Hr),Pr.run([\"$urlMatcherFactory\",function(e){}]),Rr.run([\"$state\",function(e){}]),Mr.run([\"$urlRouter\",function(e){}]),Er.run(jr);var Nr,Yr,Ur,qr,zr=function(n){return n.getTokens().filter(O).map(function(e){var t=n.getResolvable(e);return[e,\"NOWAIT\"===n.getPolicy(t).async?t.promise:t.data]}).reduce(ke,{})};function Br(e){var t,n=e.match(/^\\s*({[^}]*})\\s*$/);if(n&&(e=\"(\"+n[1]+\")\"),!(t=e.replace(/\\n/g,\" \").match(/^\\s*([^(]*?)\\s*(\\((.*)\\))?\\s*$/))||4!==t.length)throw new Error(\"Invalid state ref '\"+e+\"'\");return{state:t[1]||null,paramExpr:t[3]||null}}function Wr(e){var t=e.parent().inheritedData(\"$uiView\"),n=C(\"$cfg.path\")(t);return n?De(n).state.name:void 0}function Gr(e,t,n){var r=n.uiState||e.current.name,i=Y(function(e,t){return{relative:Wr(e)||t.$current,inherit:!0,source:\"sref\"}}(t,e),n.uiStateOpts||{}),o=e.href(r,n.uiStateParams,i);return{uiState:r,uiStateParams:n.uiStateParams,uiStateOpts:i,href:o}}function Kr(e){var t=\"[object SVGAnimatedString]\"===Object.prototype.toString.call(e.prop(\"href\")),n=\"FORM\"===e[0].nodeName;return{attr:n?\"action\":t?\"xlink:href\":\"href\",isAnchor:\"A\"===e.prop(\"tagName\").toUpperCase(),clickable:!n}}function Qr(o,a,s,u,l){return function(e){var t=e.which||e.button,n=l();if(!(1<t||e.ctrlKey||e.metaKey||e.shiftKey||o.attr(\"target\"))){var r=s(function(){o.attr(\"disabled\")||a.go(n.uiState,n.uiStateParams,n.uiStateOpts)});e.preventDefault();var i=u.isAnchor&&!n.href?1:0;e.preventDefault=function(){i--<=0&&s.cancel(r)}}}}function Zr(i,e,o,t){var a;t&&(a=t.events),A(a)||(a=[\"click\"]);for(var n=i.on?\"on\":\"bind\",r=0,s=a;r<s.length;r++){var u=s[r];i[n](u,o)}e.$on(\"$destroy\",function(){for(var e=i.off?\"off\":\"unbind\",t=0,n=a;t<n.length;t++){var r=n[t];i[e](r,o)}})}function Xr(r){var e=function(e,t,n){return r.is(e,t,n)};return e.$stateful=!0,e}function Jr(r){var e=function(e,t,n){return r.includes(e,t,n)};return e.$stateful=!0,e}function ei(g,m,v,e,y,t){var w=C(\"viewDecl.controllerAs\"),b=C(\"viewDecl.resolveAs\");return{restrict:\"ECA\",priority:-400,compile:function(e){var f=e.html();return e.empty(),function(t,n){var e=n.data(\"$uiView\");if(!e)return n.html(f),void g(n.contents())(t);var r=e.$cfg||{viewDecl:{},getTemplate:z},i=r.path&&new At(r.path);n.html(r.getTemplate(n,i)||f),lt.traceUIViewFill(e.$uiView,n.html());var o=g(n.contents()),a=r.controller,s=w(r),u=b(r),l=i&&zr(i);if(t[u]=l,a){var c=m(a,Y({},l,{$scope:t,$element:n}));s&&(t[s]=c,t[s][u]=l),n.data(\"$ngControllerController\",c),n.children().data(\"$ngControllerController\",c),ri(y,v,c,t,r)}if(O(r.component))var d=je(r.component),p=new RegExp(\"^(x-|data-)?\"+d+\"$\",\"i\"),h=t.$watch(function(){var e=[].slice.call(n[0].children).filter(function(e){return e&&e.tagName&&p.exec(e.tagName)});return e&&S.element(e).data(\"$\"+r.component+\"Controller\")},function(e){e&&(ri(y,v,e,t,r),h())});o(t)}}}}Nr=[\"$uiRouter\",\"$timeout\",function(p,h){var f=p.stateService;return{restrict:\"A\",require:[\"?^uiSrefActive\",\"?^uiSrefActiveEq\"],link:function(e,t,n,r){var i,o=Kr(t),a=r[1]||r[0],s=null,u={},l=function(){return Gr(f,t,u)},c=Br(n.uiSref);function d(){var e=l();s&&s(),a&&(s=a.$$addStateInfo(e.uiState,e.uiStateParams)),null!=e.href&&n.$set(o.attr,e.href)}u.uiState=c.state,u.uiStateOpts=n.uiSrefOpts?e.$eval(n.uiSrefOpts):{},c.paramExpr&&(e.$watch(c.paramExpr,function(e){u.uiStateParams=Y({},e),d()},!0),u.uiStateParams=Y({},e.$eval(c.paramExpr))),d(),e.$on(\"$destroy\",p.stateRegistry.onStatesChanged(d)),e.$on(\"$destroy\",p.transitionService.onSuccess({},d)),o.clickable&&(i=Qr(t,f,h,o,l),Zr(t,e,i,u.uiStateOpts))}}}],Yr=[\"$uiRouter\",\"$timeout\",function(h,f){var g=h.stateService;return{restrict:\"A\",require:[\"?^uiSrefActive\",\"?^uiSrefActiveEq\"],link:function(n,e,r,t){var i,o=Kr(e),a=t[1]||t[0],s=null,u={},l=function(){return Gr(g,e,u)},c=[\"uiState\",\"uiStateParams\",\"uiStateOpts\"],d=c.reduce(function(e,t){return e[t]=z,e},{});function p(){var e=l();s&&s(),a&&(s=a.$$addStateInfo(e.uiState,e.uiStateParams)),null!=e.href&&r.$set(o.attr,e.href)}c.forEach(function(t){u[t]=r[t]?n.$eval(r[t]):null,r.$observe(t,function(e){d[t](),d[t]=n.$watch(e,function(e){u[t]=e,p()},!0)})}),p(),n.$on(\"$destroy\",h.stateRegistry.onStatesChanged(p)),n.$on(\"$destroy\",h.transitionService.onSuccess({},p)),o.clickable&&(i=Qr(e,g,f,o,l),Zr(e,n,i,u.uiStateOpts))}}}],Ur=[\"$state\",\"$stateParams\",\"$interpolate\",\"$uiRouter\",function(f,e,g,m){return{restrict:\"A\",controller:[\"$scope\",\"$element\",\"$attrs\",function(s,u,e){var l,r,t,n,i,c=[];l=g(e.uiSrefActiveEq||\"\",!1)(s);try{r=s.$eval(e.uiSrefActive)}catch(e){}function o(e){e.promise.then(h,z)}function a(){d(r)}function d(e){T(e)&&(c=[],N(e,function(e,t){var n=function(e,t){var n=Br(e);p(n.state,s.$eval(n.paramExpr),t)};O(e)?n(e,t):A(e)&&N(e,function(e){n(e,t)})}))}function p(e,t,n){var r={state:f.get(e,Wr(u))||{name:e},params:t,activeClass:n};return c.push(r),function(){Q(c)(r)}}function h(){var t=function(e){return e.split(/\\s/).filter(q)},e=function(e){return e.map(function(e){return e.activeClass}).map(t).reduce(fe,[])},n=e(c).concat(t(l)).reduce(ve,[]),r=e(c.filter(function(e){return f.includes(e.state.name,e.params)})),i=!!c.filter(function(e){return f.is(e.state.name,e.params)}).length?t(l):[],o=r.concat(i).reduce(ve,[]),a=n.filter(function(e){return!G(o,e)});s.$evalAsync(function(){o.forEach(function(e){return u.addClass(e)}),a.forEach(function(e){return u.removeClass(e)})})}d(r=r||g(e.uiSrefActive||\"\",!1)(s)),this.$$addStateInfo=function(e,t){if(!(T(r)&&0<c.length)){var n=p(e,t,r);return h(),n}},s.$on(\"$destroy\",(t=m.stateRegistry.onStatesChanged(a),n=m.transitionService.onStart({},o),i=s.$on(\"$stateChangeSuccess\",h),function(){t(),n(),i()})),m.globals.transition&&o(m.globals.transition),h()}]}}],S.module(\"ui.router.state\").directive(\"uiSref\",Nr).directive(\"uiSrefActive\",Ur).directive(\"uiSrefActiveEq\",Ur).directive(\"uiState\",Yr),Xr.$inject=[\"$state\"],Jr.$inject=[\"$state\"],S.module(\"ui.router.state\").filter(\"isState\",Xr).filter(\"includedByState\",Jr),qr=[\"$view\",\"$animate\",\"$uiViewScroll\",\"$interpolate\",\"$q\",function(i,o,y,w,b){var $={$cfg:{viewDecl:{$context:i._pluginapi._rootViewContext()}},$uiView:{}},_={count:0,restrict:\"ECA\",terminal:!0,priority:400,transclude:\"element\",compile:function(e,t,v){return function(a,s,e){var u,l,c,d,t,p=e.onload||\"\",h=e.autoscroll,f={enter:function(e,t,n){2<S.version.minor?o.enter(e,null,t).then(n):o.enter(e,null,t,n)},leave:function(e,t){2<S.version.minor?o.leave(e).then(t):o.leave(e,t)}},n=s.inheritedData(\"$uiView\")||$,g=w(e.uiView||e.name||\"\")(a)||\"$default\",m={$type:\"ng1\",id:_.count++,name:g,fqn:n.$uiView.fqn?n.$uiView.fqn+\".\"+g:g,config:null,configUpdated:function(e){if(e&&!(e instanceof Sr))return;if(function(e,t){return e===t}(d,e))return;lt.traceUIViewConfigUpdated(m,e&&e.viewDecl&&e.viewDecl.$context),r(d=e)},get creationContext(){var e=C(\"$cfg.viewDecl.$context\")(n),t=C(\"$uiView.creationContext\")(n);return e||t}};function r(e){var t=a.$new(),n=b.defer(),r=b.defer(),i={$cfg:e,$uiView:m},o={$animEnter:n.promise,$animLeave:r.promise,$$animLeave:r};t.$emit(\"$viewContentLoading\",g),l=v(t,function(e){e.data(\"$uiViewAnim\",o),e.data(\"$uiView\",i),f.enter(e,s,function(){n.resolve(),c&&c.$emit(\"$viewContentAnimationEnded\"),(k(h)&&!h||a.$eval(h))&&y(e)}),function(){if(u&&(lt.traceUIViewEvent(\"Removing (previous) el\",u.data(\"$uiView\")),u.remove(),u=null),c&&(lt.traceUIViewEvent(\"Destroying scope\",m),c.$destroy(),c=null),l){var e=l.data(\"$uiViewAnim\");lt.traceUIViewEvent(\"Animate out\",e),f.leave(l,function(){e.$$animLeave.resolve(),u=null}),u=l,l=null}}()}),(c=t).$emit(\"$viewContentLoaded\",e||d),c.$eval(p)}lt.traceUIViewEvent(\"Linking\",m),s.data(\"$uiView\",{$uiView:m}),r(),t=i.registerUIView(m),a.$on(\"$destroy\",function(){lt.traceUIViewEvent(\"Destroying/Unregistering\",m),t()})}}};return _}],ei.$inject=[\"$compile\",\"$controller\",\"$transitions\",\"$view\",\"$q\",\"$timeout\"];var ti=\"function\"==typeof S.module(\"ui.router\").component,ni=0;function ri(r,e,l,t,n){!D(l.$onInit)||n.viewDecl.component&&ti||l.$onInit();var c=De(n.path).state.self,i={bind:l};if(D(l.uiOnParamsChanged)){var d=new At(n.path).getResolvable(\"$transition$\").data;t.$on(\"$destroy\",e.onSuccess({},function(e){if(e!==d&&-1===e.exiting().indexOf(c)){var n=e.params(\"to\"),r=e.params(\"from\"),t=function(e){return e.paramSchema},i=e.treeChanges(\"to\").map(t).reduce(fe,[]),o=e.treeChanges(\"from\").map(t).reduce(fe,[]),a=i.filter(function(e){var t=o.indexOf(e);return-1===t||!o[t].type.equals(n[e.id],r[e.id])});if(a.length){var s=a.map(function(e){return e.id}),u=se(n,function(e,t){return-1!==s.indexOf(t)});l.uiOnParamsChanged(u,e)}}},i))}if(D(l.uiCanExit)){var o=ni++,a=\"_uiCanExitIds\",s=function(e){return!!e&&(e[a]&&!0===e[a][o]||s(e.redirectedFrom()))},u={exiting:c.name};t.$on(\"$destroy\",e.onBefore(u,function(e){var t,n=e[a]=e[a]||{};return s(e)||(t=r.when(l.uiCanExit(e))).then(function(e){return n[o]=!1!==e}),t},i))}}S.module(\"ui.router.state\").directive(\"uiView\",qr),S.module(\"ui.router.state\").directive(\"uiView\",ei),S.module(\"ui.router.state\").provider(\"$uiViewScroll\",function(){var n=!1;this.useAnchorScroll=function(){n=!0},this.$get=[\"$anchorScroll\",\"$timeout\",function(e,t){return n?e:function(e){return t(function(){e[0].scrollIntoView()},0,!1)}}]});g.default=\"ui.router\",g.core=yr,g.watchDigests=Hr,g.getLocals=zr,g.getNg1ViewConfigFactory=wr,g.ng1ViewsBuilder=$r,g.Ng1ViewConfig=Sr,g.StateProvider=xr,g.UrlRouterProvider=Ar,g.root=L,g.fromJson=j,g.toJson=H,g.forEach=N,g.extend=Y,g.equals=U,g.identity=q,g.noop=z,g.createProxyFunctions=B,g.inherit=W,g.inArray=G,g._inArray=K,g.removeFrom=Q,g._removeFrom=Z,g.pushTo=X,g._pushTo=J,g.deregAll=ee,g.defaults=te,g.mergeR=ne,g.ancestors=re,g.pick=ie,g.omit=oe,g.pluck=ae,g.filter=se,g.find=ue,g.mapObj=le,g.map=ce,g.values=de,g.allTrueR=pe,g.anyTrueR=he,g.unnestR=fe,g.flattenR=ge,g.pushR=me,g.uniqR=ve,g.unnest=ye,g.flatten=we,g.assertPredicate=be,g.assertMap=$e,g.assertFn=_e,g.pairs=Se,g.arrayTuples=Ce,g.applyPairs=ke,g.tail=De,g.copy=xe,g._extend=Oe,g.silenceUncaughtInPromise=Ee,g.silentRejection=Pe,g.makeStub=I,g.services=V,g.Glob=Me,g.curry=l,g.compose=n,g.pipe=u,g.prop=v,g.propEq=y,g.parse=C,g.not=d,g.and=r,g.or=i,g.all=a,g.any=s,g.is=c,g.eq=o,g.val=p,g.invoke=h,g.pattern=f,g.isUndefined=b,g.isDefined=k,g.isNull=$,g.isNullOrUndefined=_,g.isFunction=D,g.isNumber=x,g.isString=O,g.isObject=T,g.isArray=A,g.isDate=E,g.isRegExp=P,g.isInjectable=M,g.isPromise=R,g.Queue=Re,g.maxLength=Le,g.padString=Fe,g.kebobString=je,g.functionToString=He,g.fnToString=Ne,g.stringify=qe,g.beforeAfterSubstr=ze,g.hostRegex=Be,g.stripLastPathElement=We,g.splitHash=Ge,g.splitQuery=Ke,g.splitEqual=Qe,g.trimHashVal=Ze,g.splitOnDelim=Xe,g.joinNeighborsR=Je,g.Trace=ut,g.trace=lt,g.Param=vt,g.ParamTypes=yt,g.StateParams=wt,g.ParamType=ct,g.PathNode=bt,g.PathUtils=_t,g.resolvePolicies=St,g.defaultResolvePolicy=Ct,g.Resolvable=kt,g.NATIVE_INJECTOR_TOKEN=Tt,g.ResolveContext=At,g.resolvablesBuilder=Ft,g.StateBuilder=Nt,g.StateObject=Yt,g.StateMatcher=Ut,g.StateQueueManager=qt,g.StateRegistry=zt,g.StateService=qn,g.TargetState=$t,g.HookBuilder=Zt,g.matchState=Gt,g.RegisteredHook=Kt,g.makeEvent=Qt,g.Rejection=Ve,g.Transition=Jt,g.TransitionHook=Wt,g.TransitionEventType=jn,g.defaultTransOpts=Yn,g.TransitionService=Un,g.UrlRules=gn,g.UrlConfig=mn,g.UrlMatcher=rn,g.ParamFactory=an,g.UrlMatcherFactory=sn,g.UrlRouter=dn,g.UrlRuleFactory=un,g.BaseUrlRule=ln,g.UrlService=vn,g.ViewService=pn,g.UIRouterGlobals=hn,g.UIRouter=$n,g.$q=zn,g.$injector=Kn,g.BaseLocationServices=ir,g.HashLocationService=ar,g.MemoryLocationService=ur,g.PushStateLocationService=cr,g.MemoryLocationConfig=dr,g.BrowserLocationConfig=pr,g.keyValsToObjectR=Qn,g.getParams=Zn,g.parseUrl=Xn,g.buildUrl=Jn,g.locationPluginFactory=er,g.servicesPlugin=hr,g.hashLocationPlugin=fr,g.pushStateLocationPlugin=gr,g.memoryLocationPlugin=mr,g.UIRouterPluginBase=vr,Object.defineProperty(g,\"__esModule\",{value:!0})}),function(b,l){\"use strict\";var $=[\"ng\",\"oc.lazyLoad\"],_={},S=[],C=[],k=[],D=[],x=b.noop,O={},T=[];b.module(\"oc.lazyLoad\",[\"ng\"]).provider(\"$ocLazyLoad\",[\"$controllerProvider\",\"$provide\",\"$compileProvider\",\"$filterProvider\",\"$injector\",\"$animateProvider\",function(e,t,n,r,i,o){function c(e,t,n){if(t){var r,i,o,a=[];for(r=t.length-1;0<=r;r--)if(i=t[r],b.isString(i)||(i=h(i)),i&&-1===T.indexOf(i)&&(!f[i]||-1!==k.indexOf(i))){var s=-1===$.indexOf(i);if(o=E(i),s&&($.push(i),c(e,o.requires,n)),0<o._runBlocks.length)for(O[i]=[];0<o._runBlocks.length;)O[i].push(o._runBlocks.shift());b.isDefined(O[i])&&(s||n.rerun)&&(a=a.concat(O[i])),d(e,o._invokeQueue,i,n.reconfig),d(e,o._configBlocks,i,n.reconfig),x(s?\"ocLazyLoad.moduleLoaded\":\"ocLazyLoad.moduleReloaded\",i),t.pop(),T.push(i)}var u=e.getInstanceInjector();b.forEach(a,function(e){u.invoke(e)})}}function p(e,n){function i(e){return b.isArray(e)?u(e.toString()):b.isObject(e)?u(w(e)):b.isDefined(e)&&null!==e?u(e.toString()):e}var r=e[2][0],o=e[1],a=!1;b.isUndefined(_[n])&&(_[n]={}),b.isUndefined(_[n][o])&&(_[n][o]={});var s=function(e,t){_[n][o].hasOwnProperty(e)||(_[n][o][e]=[]),function(e,t){var n,r=!0;return t.length&&(n=i(e),b.forEach(t,function(e){r=r&&i(e)!==n})),r}(t,_[n][o][e])&&(a=!0,_[n][o][e].push(t),x(\"ocLazyLoad.componentLoaded\",[n,o,e]))};if(b.isString(r))s(r,e[2][1]);else{if(!b.isObject(r))return!1;b.forEach(r,function(e,t){b.isString(e)?s(e,r[1]):s(t,e)})}return a}function d(e,t,n,r){var i,o,a,s;if(t)for(i=0,o=t.length;i<o;i++)if(a=t[i],b.isArray(a)){if(null!==e){if(!e.hasOwnProperty(a[0]))throw new Error(\"unsupported provider \"+a[0]);s=e[a[0]]}var u=p(a,n);if(\"invoke\"!==a[1])u&&b.isDefined(s)&&s[a[1]].apply(s,a[2]);else{var l=function(e){var t=S.indexOf(n+\"-\"+e);(-1===t||r)&&(-1===t&&S.push(n+\"-\"+e),b.isDefined(s)&&s[a[1]].apply(s,a[2]))};if(b.isFunction(a[2][0]))l(a[2][0]);else if(b.isArray(a[2][0]))for(var c=0,d=a[2][0].length;c<d;c++)b.isFunction(a[2][0][c])&&l(a[2][0][c])}}}function h(e){var t=null;return b.isString(e)?t=e:b.isObject(e)&&e.hasOwnProperty(\"name\")&&b.isString(e.name)&&(t=e.name),t}function a(e){if(!b.isString(e))return!1;try{return E(e)}catch(e){if(/No module/.test(e)||-1<e.message.indexOf(\"$injector:nomod\"))return!1}}var f={},s={$controllerProvider:e,$compileProvider:n,$filterProvider:r,$provide:t,$injector:i,$animateProvider:o},g=!1,m=!1,v=[],y={};v.push=function(e){-1===this.indexOf(e)&&Array.prototype.push.apply(this,arguments)},this.config=function(e){b.isDefined(e.modules)&&(b.isArray(e.modules)?b.forEach(e.modules,function(e){f[e.name]=e}):f[e.modules.name]=e.modules),b.isDefined(e.debug)&&(g=e.debug),b.isDefined(e.events)&&(m=e.events)},this._init=function(r){if(0===C.length){var t=[r],i=[\"ng:app\",\"ng-app\",\"x-ng-app\",\"data-ng-app\"],o=/\\sng[:\\-]app(:\\s*([\\w\\d_]+);?)?\\s/,n=function(e){return e&&t.push(e)};b.forEach(i,function(e){i[e]=!0,n(document.getElementById(e)),e=e.replace(\":\",\"\\\\:\"),void 0!==r[0]&&r[0].querySelectorAll&&(b.forEach(r[0].querySelectorAll(\".\"+e),n),b.forEach(r[0].querySelectorAll(\".\"+e+\"\\\\:\"),n),b.forEach(r[0].querySelectorAll(\"[\"+e+\"]\"),n))}),b.forEach(t,function(e){if(0===C.length){var t=\" \"+r.className+\" \",n=o.exec(t);n?C.push((n[2]||\"\").replace(/\\s+/g,\",\")):b.forEach(e.attributes,function(e){0===C.length&&i[e.name]&&C.push(e.value)})}})}0!==C.length||(l.jasmine||l.mocha)&&b.isDefined(b.mock)||console.error(\"No module found during bootstrap, unable to init ocLazyLoad. You should always use the ng-app directive or angular.boostrap when you use ocLazyLoad.\");b.forEach(C,function(e){!function e(t){if(-1===$.indexOf(t)){$.push(t);var n=b.module(t);d(null,n._invokeQueue,t),d(null,n._configBlocks,t),b.forEach(n.requires,e)}}(e)}),C=[],D.pop()};var w=function(t){try{return JSON.stringify(t)}catch(e){var n=[];return JSON.stringify(t,function(e,t){if(b.isObject(t)&&null!==t){if(-1!==n.indexOf(t))return;n.push(t)}return t})}},u=function(e){var t,n,r=0;if(0==e.length)return r;for(t=0,n=e.length;t<n;t++)r=(r<<5)-r+e.charCodeAt(t),r|=0;return r};this.$get=[\"$log\",\"$rootElement\",\"$rootScope\",\"$cacheFactory\",\"$q\",function(n,e,r,t,u){function l(e){var t=u.defer();return n.error(e.message),t.reject(e),t.promise}var i,o=t(\"ocLazyLoad\");return g||((n={}).error=b.noop,n.warn=b.noop,n.info=b.noop),s.getInstanceInjector=function(){return i||(i=e.data(\"$injector\")||b.injector())},{_broadcast:x=function(e,t){m&&r.$broadcast(e,t),g&&n.info(e,t)},_$log:n,_getFilesCache:function(){return o},toggleWatch:function(e){e?D.push(!0):D.pop()},getModuleConfig:function(e){if(!b.isString(e))throw new Error(\"You need to give the name of the module to get\");return f[e]?b.copy(f[e]):null},setModuleConfig:function(e){if(!b.isObject(e))throw new Error(\"You need to give the module config object to set\");return f[e.name]=e},getModules:function(){return $},isLoaded:function(e){var t,n;if(b.isString(e)&&(e=[e]),b.isArray(e)){var r,i;for(r=0,i=e.length;r<i;r++)if(t=e[r],n=void 0,(n=-1<$.indexOf(t))||(n=!!a(t)),!n)return!1;return!0}throw new Error(\"You need to define the module(s) name(s)\")},_getModuleName:h,_getModule:function(t){try{return E(t)}catch(e){throw(/No module/.test(e)||-1<e.message.indexOf(\"$injector:nomod\"))&&(e.message='The module \"'+w(t)+'\" that you are trying to load does not exist. '+e.message),e}},moduleExists:a,_loadDependencies:function(r,i){var e,t,o,a=[],s=this;if(null===(r=s._getModuleName(r)))return u.when();try{e=s._getModule(r)}catch(e){return l(e)}return t=s.getRequires(e),b.forEach(t,function(t){if(b.isString(t)){var e=s.getModuleConfig(t);if(null===e)return void v.push(t);(t=e).name=void 0}if(s.moduleExists(t.name))return 0!==(o=t.files.filter(function(e){return s.getModuleConfig(t.name).files.indexOf(e)<0})).length&&s._$log.warn('Module \"',r,'\" attempted to redefine configuration for dependency. \"',t.name,'\"\\n Additional Files Loaded:',o),b.isDefined(s.filesLoader)?void a.push(s.filesLoader(t,i).then(function(){return s._loadDependencies(t)})):l(new Error(\"Error: New dependencies need to be loaded from external files (\"+t.files+\"), but no loader has been defined.\"));if(b.isArray(t)){var n=[];b.forEach(t,function(e){var t=s.getModuleConfig(e);null===t?n.push(e):t.files&&(n=n.concat(t.files))}),0<n.length&&(t={files:n})}else b.isObject(t)&&t.hasOwnProperty(\"name\")&&t.name&&(s.setModuleConfig(t),v.push(t.name));if(b.isDefined(t.files)&&0!==t.files.length){if(!b.isDefined(s.filesLoader))return l(new Error('Error: the module \"'+t.name+'\" is defined in external files ('+t.files+\"), but no loader has been defined.\"));a.push(s.filesLoader(t,i).then(function(){return s._loadDependencies(t)}))}}),u.all(a)},inject:function(e){var n=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],t=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],r=this,i=u.defer();if(b.isDefined(e)&&null!==e){if(b.isArray(e)){var o=[];return b.forEach(e,function(e){o.push(r.inject(e,n,t))}),u.all(o)}r._addToLoadList(r._getModuleName(e),!0,t)}if(0<C.length){var a=C.slice();!function e(t){v.push(t),y[t]=i.promise,r._loadDependencies(t,n).then(function(){try{T=[],c(s,v,n)}catch(e){return r._$log.error(e.message),void i.reject(e)}0<C.length?e(C.shift()):i.resolve(a)},function(e){i.reject(e)})}(C.shift())}else{if(n&&n.name&&y[n.name])return y[n.name];i.resolve()}return i.promise},getRequires:function(e){var t=[];return b.forEach(e.requires,function(e){-1===$.indexOf(e)&&t.push(e)}),t},_invokeQueue:d,_registerInvokeList:p,_register:c,_addToLoadList:A,_unregister:function(e){b.isDefined(e)&&b.isArray(e)&&b.forEach(e,function(e){_[e]=void 0})}}}],this._init(b.element(l.document))}]);var r=b.bootstrap;b.bootstrap=function(e,t,n){return $=[\"ng\",\"oc.lazyLoad\"],_={},S=[],C=[],k=[],D=[],x=b.noop,O={},T=[],b.forEach(t.slice(),function(e){A(e,!0,!0)}),r(e,t,n)};var A=function(e,t,n){(0<D.length||t)&&b.isString(e)&&-1===C.indexOf(e)&&(C.push(e),n&&k.push(e))},E=b.module;b.module=function(e,t,n){return A(e,!1,!0),E(e,t,n)},\"undefined\"!=typeof module&&\"undefined\"!=typeof exports&&module.exports===exports&&(module.exports=\"oc.lazyLoad\")}(angular,window),function(l){\"use strict\";l.module(\"oc.lazyLoad\").directive(\"ocLazyLoad\",[\"$ocLazyLoad\",\"$compile\",\"$animate\",\"$parse\",\"$timeout\",function(o,a,s,u,e){return{restrict:\"A\",terminal:!0,priority:1e3,compile:function(e,t){var i=e[0].innerHTML;return e.html(\"\"),function(t,n,e){var r=u(e.ocLazyLoad);t.$watch(function(){return r(t)||e.ocLazyLoad},function(e){l.isDefined(e)&&o.load(e).then(function(){s.enter(i,n),a(n.contents())(t)})},!0)}}}}])}(angular),function(S){\"use strict\";S.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",\"$window\",\"$interval\",function(v,y,w,b){var $=!1,_=w.document.getElementsByTagName(\"head\")[0]||w.document.getElementsByTagName(\"body\")[0];return v.buildElement=function(e,t,n){var r,i,o=y.defer(),a=v._getFilesCache(),s=function(e){var t=(new Date).getTime();return 0<=e.indexOf(\"?\")?\"&\"===e.substring(0,e.length-1)?e+\"_dc=\"+t:e+\"&_dc=\"+t:e+\"?_dc=\"+t};switch(S.isUndefined(a.get(t))&&a.put(t,o.promise),e){case\"css\":(r=w.document.createElement(\"link\")).type=\"text/css\",r.rel=\"stylesheet\",r.href=!1===n.cache?s(t):t;break;case\"js\":(r=w.document.createElement(\"script\")).src=!1===n.cache?s(t):t;break;default:a.remove(t),o.reject(new Error('Requested type \"'+e+'\" is not known. Could not inject \"'+t+'\"'))}r.onload=r.onreadystatechange=function(e){r.readyState&&!/^c|loade/.test(r.readyState)||i||(r.onload=r.onreadystatechange=null,i=1,v._broadcast(\"ocLazyLoad.fileLoaded\",t),o.resolve(r))},r.onerror=function(){a.remove(t),o.reject(new Error(\"Unable to load \"+t))},r.async=n.serie?0:1;var u=_.lastChild;if(n.insertBefore){var l=S.element(S.isDefined(window.jQuery)?n.insertBefore:document.querySelector(n.insertBefore));l&&0<l.length&&(u=l[0])}if(u.parentNode.insertBefore(r,u),\"css\"==e){var c=w.navigator.userAgent.toLowerCase();if(-1<c.indexOf(\"phantomjs/1.9\"))$=!0;else if(/iP(hone|od|ad)/.test(w.navigator.platform)){var d=w.navigator.appVersion.match(/OS (\\d+)_(\\d+)_?(\\d+)?/),p=parseFloat([parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3]||0,10)].join(\".\"));$=p<6}else if(-1<c.indexOf(\"android\")){var h=parseFloat(c.slice(c.indexOf(\"android\")+8));$=h<4.4}else if(-1<c.indexOf(\"safari\")){var f=c.match(/version\\/([\\.\\d]+)/i);$=f&&f[1]&&parseFloat(f[1])<6}if($)var g=1e3,m=b(function(){try{r.sheet.cssRules,b.cancel(m),r.onload()}catch(e){--g<=0&&r.onerror()}},20)}return o.promise},v}])}])}(angular),function(m){\"use strict\";m.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(f,g){return f.filesLoader=function(e){var r=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],i=[],o=[],a=[],s=[],u=null,l=f._getFilesCache();f.toggleWatch(!0),m.extend(r,e);var t=function(e){var t,n=null;if(m.isObject(e)&&(n=e.type,e=e.path),u=l.get(e),m.isUndefined(u)||!1===r.cache){if(null!==(t=/^(css|less|html|htm|js)?(?=!)/.exec(e))&&(n=t[1],e=e.substr(t[1].length+1,e.length)),!n)if(null!==(t=/[.](css|less|html|htm|js)?((\\?|#).*)?$/.exec(e)))n=t[1];else{if(f.jsLoader.hasOwnProperty(\"ocLazyLoadLoader\")||!f.jsLoader.hasOwnProperty(\"requirejs\"))return void f._$log.error(\"File type could not be determined. \"+e);n=\"js\"}\"css\"!==n&&\"less\"!==n||-1!==i.indexOf(e)?\"html\"!==n&&\"htm\"!==n||-1!==o.indexOf(e)?\"js\"===n||-1===a.indexOf(e)?a.push(e):f._$log.error(\"File type is not valid. \"+e):o.push(e):i.push(e)}else u&&s.push(u)};if(r.serie?t(r.files.shift()):m.forEach(r.files,function(e){t(e)}),0<i.length){var n=g.defer();f.cssLoader(i,function(e){m.isDefined(e)&&f.cssLoader.hasOwnProperty(\"ocLazyLoadLoader\")?(f._$log.error(e),n.reject(e)):n.resolve()},r),s.push(n.promise)}if(0<o.length){var c=g.defer();f.templatesLoader(o,function(e){m.isDefined(e)&&f.templatesLoader.hasOwnProperty(\"ocLazyLoadLoader\")?(f._$log.error(e),c.reject(e)):c.resolve()},r),s.push(c.promise)}if(0<a.length){var d=g.defer();f.jsLoader(a,function(e){m.isDefined(e)&&(f.jsLoader.hasOwnProperty(\"ocLazyLoadLoader\")||f.jsLoader.hasOwnProperty(\"requirejs\"))?(f._$log.error(e),d.reject(e)):d.resolve()},r),s.push(d.promise)}if(0!==s.length)return r.serie&&0<r.files.length?g.all(s).then(function(){return f.filesLoader(e,r)}):g.all(s).finally(function(e){return f.toggleWatch(!1),e});var p=g.defer(),h=\"Error: no file to load has been found, if you're trying to load an existing module you should use the 'inject' method instead of 'load'.\";return f._$log.error(h),p.reject(h),p.promise},f.load=function(e){var t,n=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],r=this,i=null,o=[],a=g.defer(),s=m.copy(e),u=m.copy(n);if(m.isArray(s))return m.forEach(s,function(e){o.push(r.load(e,u))}),g.all(o).then(function(e){a.resolve(e)},function(e){a.reject(e)}),a.promise;if(m.isString(s)?(i=r.getModuleConfig(s))||(i={files:[s]}):m.isObject(s)&&(i=m.isDefined(s.path)&&m.isDefined(s.type)?{files:[s]}:r.setModuleConfig(s)),null===i)return t='Module \"'+(r._getModuleName(s)||\"unknown\")+'\" is not configured, cannot load.',f._$log.error(t),a.reject(new Error(t)),a.promise;m.isDefined(i.template)&&(m.isUndefined(i.files)&&(i.files=[]),m.isString(i.template)?i.files.push(i.template):m.isArray(i.template)&&i.files.concat(i.template));var l=m.extend({},u,i);return m.isUndefined(i.files)&&m.isDefined(i.name)&&f.moduleExists(i.name)?f.inject(i.name,l,!0):(f.filesLoader(i,l).then(function(){f.inject(null,l).then(function(e){a.resolve(e)},function(e){a.reject(e)})},function(e){a.reject(e)}),a.promise)},f}])}])}(angular),function(a){\"use strict\";a.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(i,o){return i.cssLoader=function(e,t,n){var r=[];a.forEach(e,function(e){r.push(i.buildElement(\"css\",e,n))}),o.all(r).then(function(){t()},function(e){t(e)})},i.cssLoader.ocLazyLoadLoader=!0,i}])}])}(angular),function(a){\"use strict\";a.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$q\",function(i,o){return i.jsLoader=function(e,t,n){var r=[];a.forEach(e,function(e){r.push(i.buildElement(\"js\",e,n))}),o.all(r).then(function(){t()},function(e){t(e)})},i.jsLoader.ocLazyLoadLoader=!0,i}])}])}(angular),function(c){\"use strict\";c.module(\"oc.lazyLoad\").config([\"$provide\",function(e){e.decorator(\"$ocLazyLoad\",[\"$delegate\",\"$templateCache\",\"$q\",\"$http\",function(n,s,u,l){return n.templatesLoader=function(e,t,i){var o=[],a=n._getFilesCache();return c.forEach(e,function(n){var r=u.defer();o.push(r.promise),l.get(n,i).then(function(e){var t=e.data;c.isString(t)&&0<t.length&&c.forEach(c.element(t),function(e){\"SCRIPT\"===e.nodeName&&\"text/ng-template\"===e.type&&s.put(e.id,e.innerHTML)}),c.isUndefined(a.get(n))&&a.put(n,!0),r.resolve()}).catch(function(e){r.reject(new Error('Unable to load template file \"'+n+'\": '+e.data))})}),u.all(o).then(function(){t()},function(e){t(e)})},n.templatesLoader.ocLazyLoadLoader=!0,n}])}])}(angular),Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){var n;if(null==this)throw new TypeError('\"this\" is null or not defined');var r=Object(this),i=r.length>>>0;if(0==i)return-1;var o=+t||0;if(Math.abs(o)===1/0&&(o=0),i<=o)return-1;for(n=Math.max(0<=o?o:i-Math.abs(o),0);n<i;){if(n in r&&r[n]===e)return n;n++}return-1}),function(){\"use strict\";angular.module(\"angular-loading-bar\",[\"cfp.loadingBarInterceptor\"]),angular.module(\"chieffancypants.loadingBar\",[\"cfp.loadingBarInterceptor\"]),angular.module(\"cfp.loadingBarInterceptor\",[\"cfp.loadingBar\"]).config([\"$httpProvider\",function(h){var e=[\"$q\",\"$cacheFactory\",\"$timeout\",\"$rootScope\",\"$log\",\"cfpLoadingBar\",function(t,o,n,r,i,a){function s(){n.cancel(l),a.complete(),c=d=0}function u(e){var t,n=o.get(\"$http\"),r=h.defaults;!e.cache&&!r.cache||!1===e.cache||\"GET\"!==e.method&&\"JSONP\"!==e.method||(t=angular.isObject(e.cache)?e.cache:angular.isObject(r.cache)?r.cache:n);var i=void 0!==t&&void 0!==t.get(e.url);return void 0!==e.cached&&i!==e.cached?e.cached:e.cached=i}var l,c=0,d=0,p=a.latencyThreshold;return{request:function(e){return e.ignoreLoadingBar||u(e)||(r.$broadcast(\"cfpLoadingBar:loading\",{url:e.url}),0===c&&(l=n(function(){a.start()},p)),c++,a.set(d/c)),e},response:function(e){return e&&e.config?e.config.ignoreLoadingBar||u(e.config)||(d++,r.$broadcast(\"cfpLoadingBar:loaded\",{url:e.config.url,result:e}),c<=d?s():a.set(d/c)):i.error(\"Broken interceptor detected: Config object not supplied in response:\\n https://github.com/chieffancypants/angular-loading-bar/pull/50\"),e},responseError:function(e){return e&&e.config?e.config.ignoreLoadingBar||u(e.config)||(d++,r.$broadcast(\"cfpLoadingBar:loaded\",{url:e.config.url,result:e}),c<=d?s():a.set(d/c)):i.error(\"Broken interceptor detected: Config object not supplied in rejection:\\n https://github.com/chieffancypants/angular-loading-bar/pull/50\"),t.reject(e)}}}];h.interceptors.push(e)}]),angular.module(\"cfp.loadingBar\",[]).provider(\"cfpLoadingBar\",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector=\"body\",this.spinnerTemplate='<div id=\"loading-bar-spinner\"><div class=\"spinner-icon\"></div></div>',this.loadingBarTemplate='<div id=\"loading-bar\"><div class=\"bar\"><div class=\"peg\"></div></div></div>',this.$get=[\"$injector\",\"$document\",\"$timeout\",\"$rootScope\",function(i,o,a,s){function u(e){if(m){var t=100*e+\"%\";f.css(\"width\",t),v=e,y&&(a.cancel(c),c=a(function(){n()},250))}}function n(){if(!(1<=r())){var e,t=r();e=0<=t&&t<.25?(3*Math.random()+3)/100:.25<=t&&t<.65?3*Math.random()/100:.65<=t&&t<.9?2*Math.random()/100:.9<=t&&t<.99?.005:0,u(r()+e)}}function r(){return v}function t(){v=0,m=!1}var l,c,d,p=this.parentSelector,h=angular.element(this.loadingBarTemplate),f=h.find(\"div\").eq(0),g=angular.element(this.spinnerTemplate),m=!1,v=0,y=this.autoIncrement,w=this.includeSpinner,b=this.includeBar,$=this.startSize;return{start:function(){if(l||(l=i.get(\"$animate\")),a.cancel(d),!m){var e=o[0],t=e.querySelector?e.querySelector(p):o.find(p)[0];t||(t=e.getElementsByTagName(\"body\")[0]);var n=angular.element(t),r=t.lastChild&&angular.element(t.lastChild);s.$broadcast(\"cfpLoadingBar:started\"),m=!0,b&&l.enter(h,n,r),w&&l.enter(g,n,h),u($)}},set:u,status:r,inc:n,complete:function(){l||(l=i.get(\"$animate\")),s.$broadcast(\"cfpLoadingBar:completed\"),u(1),a.cancel(d),d=a(function(){var e=l.leave(h,t);e&&e.then&&e.then(t),l.leave(g)},500)},autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(),angular.module(\"ui.bootstrap\",[\"ui.bootstrap.tpls\",\"ui.bootstrap.transition\",\"ui.bootstrap.collapse\",\"ui.bootstrap.accordion\",\"ui.bootstrap.alert\",\"ui.bootstrap.bindHtml\",\"ui.bootstrap.buttons\",\"ui.bootstrap.carousel\",\"ui.bootstrap.dateparser\",\"ui.bootstrap.position\",\"ui.bootstrap.datepicker\",\"ui.bootstrap.dropdown\",\"ui.bootstrap.modal\",\"ui.bootstrap.pagination\",\"ui.bootstrap.tooltip\",\"ui.bootstrap.popover\",\"ui.bootstrap.progressbar\",\"ui.bootstrap.rating\",\"ui.bootstrap.tabs\",\"ui.bootstrap.timepicker\",\"ui.bootstrap.typeahead\"]),angular.module(\"ui.bootstrap.tpls\",[\"template/accordion/accordion-group.html\",\"template/accordion/accordion.html\",\"template/alert/alert.html\",\"template/carousel/carousel.html\",\"template/carousel/slide.html\",\"template/datepicker/datepicker.html\",\"template/datepicker/day.html\",\"template/datepicker/month.html\",\"template/datepicker/popup.html\",\"template/datepicker/year.html\",\"template/modal/backdrop.html\",\"template/modal/window.html\",\"template/pagination/pager.html\",\"template/pagination/pagination.html\",\"template/tooltip/tooltip-html-unsafe-popup.html\",\"template/tooltip/tooltip-popup.html\",\"template/popover/popover.html\",\"template/progressbar/bar.html\",\"template/progressbar/progress.html\",\"template/progressbar/progressbar.html\",\"template/rating/rating.html\",\"template/tabs/tab.html\",\"template/tabs/tabset.html\",\"template/timepicker/timepicker.html\",\"template/typeahead/typeahead-match.html\",\"template/typeahead/typeahead-popup.html\"]),angular.module(\"ui.bootstrap.transition\",[]).factory(\"$transition\",[\"$q\",\"$timeout\",\"$rootScope\",function(a,s,u){function e(e){for(var t in e)if(void 0!==n.style[t])return e[t]}var l=function(e,t,n){n=n||{};var r=a.defer(),i=l[n.animation?\"animationEndEventName\":\"transitionEndEventName\"],o=function(){u.$apply(function(){e.unbind(i,o),r.resolve(e)})};return i&&e.bind(i,o),s(function(){angular.isString(t)?e.addClass(t):angular.isFunction(t)?t(e):angular.isObject(t)&&e.css(t),i||r.resolve(e)}),r.promise.cancel=function(){i&&e.unbind(i,o),r.reject(\"Transition cancelled\")},r.promise},n=document.createElement(\"trans\");return l.transitionEndEventName=e({WebkitTransition:\"webkitTransitionEnd\",MozTransition:\"transitionend\",OTransition:\"oTransitionEnd\",transition:\"transitionend\"}),l.animationEndEventName=e({WebkitTransition:\"webkitAnimationEnd\",MozTransition:\"animationend\",OTransition:\"oAnimationEnd\",transition:\"animationend\"}),l}]),angular.module(\"ui.bootstrap.collapse\",[\"ui.bootstrap.transition\"]).directive(\"collapse\",[\"$transition\",function(u){return{link:function(e,r,t){function n(e){function t(){a===n&&(a=void 0)}var n=u(r,e);return a&&a.cancel(),(a=n).then(t,t),n}function i(){r.removeClass(\"collapsing\"),r.addClass(\"collapse in\"),r.css({height:\"auto\"})}function o(){r.removeClass(\"collapsing\"),r.addClass(\"collapse\")}var a,s=!0;e.$watch(t.collapse,function(e){e?s?(s=!1,o(),r.css({height:0})):(r.css({height:r[0].scrollHeight+\"px\"}),r[0].offsetWidth,r.removeClass(\"collapse in\").addClass(\"collapsing\"),n({height:0}).then(o)):s?(s=!1,i()):(r.removeClass(\"collapse\").addClass(\"collapsing\"),n({height:r[0].scrollHeight+\"px\"}).then(i))})}}}]),angular.module(\"ui.bootstrap.accordion\",[\"ui.bootstrap.collapse\"]).constant(\"accordionConfig\",{closeOthers:!0}).controller(\"AccordionController\",[\"$scope\",\"$attrs\",\"accordionConfig\",function(e,n,r){this.groups=[],this.closeOthers=function(t){(angular.isDefined(n.closeOthers)?e.$eval(n.closeOthers):r.closeOthers)&&angular.forEach(this.groups,function(e){e!==t&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on(\"$destroy\",function(){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);-1!==t&&this.groups.splice(t,1)}}]).directive(\"accordion\",function(){return{restrict:\"EA\",controller:\"AccordionController\",transclude:!0,replace:!1,templateUrl:\"template/accordion/accordion.html\"}}).directive(\"accordionGroup\",function(){return{require:\"^accordion\",restrict:\"EA\",transclude:!0,replace:!0,templateUrl:\"template/accordion/accordion-group.html\",scope:{heading:\"@\",isOpen:\"=?\",isDisabled:\"=?\"},controller:function(){this.setHeading=function(e){this.heading=e}},link:function(t,e,n,r){r.addGroup(t),t.$watch(\"isOpen\",function(e){e&&r.closeOthers(t)}),t.toggleOpen=function(){t.isDisabled||(t.isOpen=!t.isOpen)}}}}).directive(\"accordionHeading\",function(){return{restrict:\"EA\",transclude:!0,template:\"\",replace:!0,require:\"^accordionGroup\",link:function(e,t,n,r,i){r.setHeading(i(e,function(){}))}}}).directive(\"accordionTransclude\",function(){return{require:\"^accordionGroup\",link:function(e,t,n,r){e.$watch(function(){return r[n.accordionTransclude]},function(e){e&&(t.html(\"\"),t.append(e))})}}}),angular.module(\"ui.bootstrap.alert\",[]).controller(\"AlertController\",[\"$scope\",\"$attrs\",function(e,t){e.closeable=\"close\"in t,this.close=e.close}]).directive(\"alert\",function(){return{restrict:\"EA\",controller:\"AlertController\",templateUrl:\"template/alert/alert.html\",transclude:!0,replace:!0,scope:{type:\"@\",close:\"&\"}}}).directive(\"dismissOnTimeout\",[\"$timeout\",function(i){return{require:\"alert\",link:function(e,t,n,r){i(function(){r.close()},parseInt(n.dismissOnTimeout,10))}}}]),angular.module(\"ui.bootstrap.bindHtml\",[]).directive(\"bindHtmlUnsafe\",function(){return function(e,t,n){t.addClass(\"ng-binding\").data(\"$binding\",n.bindHtmlUnsafe),e.$watch(n.bindHtmlUnsafe,function(e){t.html(e||\"\")})}}),angular.module(\"ui.bootstrap.buttons\",[]).constant(\"buttonConfig\",{activeClass:\"active\",toggleEvent:\"click\"}).controller(\"ButtonsController\",[\"buttonConfig\",function(e){this.activeClass=e.activeClass||\"active\",this.toggleEvent=e.toggleEvent||\"click\"}]).directive(\"btnRadio\",function(){return{require:[\"btnRadio\",\"ngModel\"],controller:\"ButtonsController\",link:function(t,n,r,e){var i=e[0],o=e[1];o.$render=function(){n.toggleClass(i.activeClass,angular.equals(o.$modelValue,t.$eval(r.btnRadio)))},n.bind(i.toggleEvent,function(){var e=n.hasClass(i.activeClass);(!e||angular.isDefined(r.uncheckable))&&t.$apply(function(){o.$setViewValue(e?null:t.$eval(r.btnRadio)),o.$render()})})}}}).directive(\"btnCheckbox\",function(){return{require:[\"btnCheckbox\",\"ngModel\"],controller:\"ButtonsController\",link:function(r,e,t,n){function i(){return o(t.btnCheckboxTrue,!0)}function o(e,t){var n=r.$eval(e);return angular.isDefined(n)?n:t}var a=n[0],s=n[1];s.$render=function(){e.toggleClass(a.activeClass,angular.equals(s.$modelValue,i()))},e.bind(a.toggleEvent,function(){r.$apply(function(){s.$setViewValue(e.hasClass(a.activeClass)?o(t.btnCheckboxFalse,!1):i()),s.$render()})})}}}),angular.module(\"ui.bootstrap.carousel\",[\"ui.bootstrap.transition\"]).controller(\"CarouselController\",[\"$scope\",\"$timeout\",\"$interval\",\"$transition\",function(a,t,n,s){function u(){r();var e=+a.interval;!isNaN(e)&&0<e&&(o=n(i,e))}function r(){o&&(n.cancel(o),o=null)}function i(){var e=+a.interval;l&&!isNaN(e)&&0<e?a.next():a.pause()}var o,l,c=this,d=c.slides=a.slides=[],p=-1;c.currentSlide=null;var h=!1;c.select=a.select=function(n,r){function e(){var e,t;h||(c.currentSlide&&angular.isString(r)&&!a.noTransition&&n.$element?(n.$element.addClass(r),n.$element[0].offsetWidth,angular.forEach(d,function(e){angular.extend(e,{direction:\"\",entering:!1,leaving:!1,active:!1})}),angular.extend(n,{direction:r,active:!0,entering:!0}),angular.extend(c.currentSlide||{},{direction:r,leaving:!0}),a.$currentTransition=s(n.$element,{}),e=n,t=c.currentSlide,a.$currentTransition.then(function(){i(e,t)},function(){i(e,t)})):i(n,c.currentSlide),c.currentSlide=n,p=o,u())}function i(e,t){angular.extend(e,{direction:\"\",active:!0,leaving:!1,entering:!1}),angular.extend(t||{},{direction:\"\",active:!1,leaving:!1,entering:!1}),a.$currentTransition=null}var o=d.indexOf(n);void 0===r&&(r=p<o?\"next\":\"prev\"),n&&n!==c.currentSlide&&(a.$currentTransition?(a.$currentTransition.cancel(),t(e)):e())},a.$on(\"$destroy\",function(){h=!0}),c.indexOfSlide=function(e){return d.indexOf(e)},a.next=function(){var e=(p+1)%d.length;return a.$currentTransition?void 0:c.select(d[e],\"next\")},a.prev=function(){var e=p-1<0?d.length-1:p-1;return a.$currentTransition?void 0:c.select(d[e],\"prev\")},a.isActive=function(e){return c.currentSlide===e},a.$watch(\"interval\",u),a.$on(\"$destroy\",r),a.play=function(){l||(l=!0,u())},a.pause=function(){a.noPause||(l=!1,r())},c.addSlide=function(e,t){e.$element=t,d.push(e),1===d.length||e.active?(c.select(d[d.length-1]),1==d.length&&a.play()):e.active=!1},c.removeSlide=function(e){var t=d.indexOf(e);d.splice(t,1),0<d.length&&e.active?c.select(t>=d.length?d[t-1]:d[t]):t<p&&p--}}]).directive(\"carousel\",[function(){return{restrict:\"EA\",transclude:!0,replace:!0,controller:\"CarouselController\",require:\"carousel\",templateUrl:\"template/carousel/carousel.html\",scope:{interval:\"=\",noTransition:\"=\",noPause:\"=\"}}}]).directive(\"slide\",function(){return{require:\"^carousel\",restrict:\"EA\",transclude:!0,replace:!0,templateUrl:\"template/carousel/slide.html\",scope:{active:\"=?\"},link:function(t,e,n,r){r.addSlide(t,e),t.$on(\"$destroy\",function(){r.removeSlide(t)}),t.$watch(\"active\",function(e){e&&r.select(t)})}}}),angular.module(\"ui.bootstrap.dateparser\",[]).service(\"dateParser\",[\"$locale\",\"orderByFilter\",function(d,p){this.parsers={};var h={yyyy:{regex:\"\\\\d{4}\",apply:function(e){this.year=+e}},yy:{regex:\"\\\\d{2}\",apply:function(e){this.year=+e+2e3}},y:{regex:\"\\\\d{1,4}\",apply:function(e){this.year=+e}},MMMM:{regex:d.DATETIME_FORMATS.MONTH.join(\"|\"),apply:function(e){this.month=d.DATETIME_FORMATS.MONTH.indexOf(e)}},MMM:{regex:d.DATETIME_FORMATS.SHORTMONTH.join(\"|\"),apply:function(e){this.month=d.DATETIME_FORMATS.SHORTMONTH.indexOf(e)}},MM:{regex:\"0[1-9]|1[0-2]\",apply:function(e){this.month=e-1}},M:{regex:\"[1-9]|1[0-2]\",apply:function(e){this.month=e-1}},dd:{regex:\"[0-2][0-9]{1}|3[0-1]{1}\",apply:function(e){this.date=+e}},d:{regex:\"[1-2]?[0-9]{1}|3[0-1]{1}\",apply:function(e){this.date=+e}},EEEE:{regex:d.DATETIME_FORMATS.DAY.join(\"|\")},EEE:{regex:d.DATETIME_FORMATS.SHORTDAY.join(\"|\")}};this.parse=function(e,t){if(!angular.isString(e)||!t)return e;t=d.DATETIME_FORMATS[t]||t,this.parsers[t]||(this.parsers[t]=function(o){var a=[],s=o.split(\"\");return angular.forEach(h,function(e,t){var n=o.indexOf(t);if(-1<n){o=o.split(\"\"),s[n]=\"(\"+e.regex+\")\",o[n]=\"$\";for(var r=n+1,i=n+t.length;r<i;r++)s[r]=\"\",o[r]=\"$\";o=o.join(\"\"),a.push({index:n,apply:e.apply})}}),{regex:new RegExp(\"^\"+s.join(\"\")+\"$\"),map:p(a,\"index\")}}(t));var n=this.parsers[t],r=n.regex,i=n.map,o=e.match(r);if(o&&o.length){for(var a,s={year:1900,month:0,date:1,hours:0},u=1,l=o.length;u<l;u++){var c=i[u-1];c.apply&&c.apply.call(s,o[u])}return function(e,t,n){return 1===t&&28<n?29===n&&(e%4==0&&e%100!=0||e%400==0):3!==t&&5!==t&&8!==t&&10!==t||n<31}(s.year,s.month,s.date)&&(a=new Date(s.year,s.month,s.date,s.hours)),a}}}]),angular.module(\"ui.bootstrap.position\",[]).factory(\"$position\",[\"$document\",\"$window\",function(o,n){function a(e){return\"static\"===(function(e,t){return e.currentStyle?e.currentStyle[t]:n.getComputedStyle?n.getComputedStyle(e)[t]:e.style[t]}(e,\"position\")||\"static\")}return{position:function(e){var t=this.offset(e),n={top:0,left:0},r=function(e){for(var t=o[0],n=e.offsetParent||t;n&&n!==t&&a(n);)n=n.offsetParent;return n||t}(e[0]);r!=o[0]&&((n=this.offset(angular.element(r))).top+=r.clientTop-r.scrollTop,n.left+=r.clientLeft-r.scrollLeft);var i=e[0].getBoundingClientRect();return{width:i.width||e.prop(\"offsetWidth\"),height:i.height||e.prop(\"offsetHeight\"),top:t.top-n.top,left:t.left-n.left}},offset:function(e){var t=e[0].getBoundingClientRect();return{width:t.width||e.prop(\"offsetWidth\"),height:t.height||e.prop(\"offsetHeight\"),top:t.top+(n.pageYOffset||o[0].documentElement.scrollTop),left:t.left+(n.pageXOffset||o[0].documentElement.scrollLeft)}},positionElements:function(e,t,n,r){var i,o,a,s,u=n.split(\"-\"),l=u[0],c=u[1]||\"center\";i=r?this.offset(e):this.position(e),o=t.prop(\"offsetWidth\"),a=t.prop(\"offsetHeight\");var d={center:function(){return i.left+i.width/2-o/2},left:function(){return i.left},right:function(){return i.left+i.width}},p={center:function(){return i.top+i.height/2-a/2},top:function(){return i.top},bottom:function(){return i.top+i.height}};switch(l){case\"right\":s={top:p[c](),left:d[l]()};break;case\"left\":s={top:p[c](),left:i.left-o};break;case\"bottom\":s={top:p[l](),left:d[c]()};break;default:s={top:i.top-a,left:d[c]()}}return s}}}]),angular.module(\"ui.bootstrap.datepicker\",[\"ui.bootstrap.dateparser\",\"ui.bootstrap.position\"]).constant(\"datepickerConfig\",{formatDay:\"dd\",formatMonth:\"MMMM\",formatYear:\"yyyy\",formatDayHeader:\"EEE\",formatDayTitle:\"MMMM yyyy\",formatMonthTitle:\"yyyy\",datepickerMode:\"day\",minMode:\"day\",maxMode:\"year\",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null}).controller(\"DatepickerController\",[\"$scope\",\"$attrs\",\"$parse\",\"$interpolate\",\"$timeout\",\"$log\",\"dateFilter\",\"datepickerConfig\",function(n,r,e,i,t,o,a,s){var u=this,l={$setViewValue:angular.noop};this.modes=[\"day\",\"month\",\"year\"],angular.forEach([\"formatDay\",\"formatMonth\",\"formatYear\",\"formatDayHeader\",\"formatDayTitle\",\"formatMonthTitle\",\"minMode\",\"maxMode\",\"showWeeks\",\"startingDay\",\"yearRange\"],function(e,t){u[e]=angular.isDefined(r[e])?t<8?i(r[e])(n.$parent):n.$parent.$eval(r[e]):s[e]}),angular.forEach([\"minDate\",\"maxDate\"],function(t){r[t]?n.$parent.$watch(e(r[t]),function(e){u[t]=e?new Date(e):null,u.refreshView()}):u[t]=s[t]?new Date(s[t]):null}),n.datepickerMode=n.datepickerMode||s.datepickerMode,n.uniqueId=\"datepicker-\"+n.$id+\"-\"+Math.floor(1e4*Math.random()),this.activeDate=angular.isDefined(r.initDate)?n.$parent.$eval(r.initDate):new Date,n.isActive=function(e){return 0===u.compare(e.date,u.activeDate)&&(n.activeDateId=e.uid,!0)},this.init=function(e){(l=e).$render=function(){u.render()}},this.render=function(){if(l.$modelValue){var e=new Date(l.$modelValue),t=!isNaN(e);t?this.activeDate=e:o.error('Datepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'),l.$setValidity(\"date\",t)}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var e=l.$modelValue?new Date(l.$modelValue):null;l.$setValidity(\"date-disabled\",!e||this.element&&!this.isDisabled(e))}},this.createDateObject=function(e,t){var n=l.$modelValue?new Date(l.$modelValue):null;return{date:e,label:a(e,t),selected:n&&0===this.compare(e,n),disabled:this.isDisabled(e),current:0===this.compare(e,new Date)}},this.isDisabled=function(e){return this.minDate&&this.compare(e,this.minDate)<0||this.maxDate&&0<this.compare(e,this.maxDate)||r.dateDisabled&&n.dateDisabled({date:e,mode:n.datepickerMode})},this.split=function(e,t){for(var n=[];0<e.length;)n.push(e.splice(0,t));return n},n.select=function(e){if(n.datepickerMode===u.minMode){var t=l.$modelValue?new Date(l.$modelValue):new Date(0,0,0,0,0,0,0);t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),l.$setViewValue(t),l.$render()}else u.activeDate=e,n.datepickerMode=u.modes[u.modes.indexOf(n.datepickerMode)-1]},n.move=function(e){var t=u.activeDate.getFullYear()+e*(u.step.years||0),n=u.activeDate.getMonth()+e*(u.step.months||0);u.activeDate.setFullYear(t,n,1),u.refreshView()},n.toggleMode=function(e){e=e||1,n.datepickerMode===u.maxMode&&1===e||n.datepickerMode===u.minMode&&-1===e||(n.datepickerMode=u.modes[u.modes.indexOf(n.datepickerMode)+e])},n.keys={13:\"enter\",32:\"space\",33:\"pageup\",34:\"pagedown\",35:\"end\",36:\"home\",37:\"left\",38:\"up\",39:\"right\",40:\"down\"};var c=function(){t(function(){u.element[0].focus()},0,!1)};n.$on(\"datepicker.focus\",c),n.keydown=function(e){var t=n.keys[e.which];if(t&&!e.shiftKey&&!e.altKey)if(e.preventDefault(),e.stopPropagation(),\"enter\"===t||\"space\"===t){if(u.isDisabled(u.activeDate))return;n.select(u.activeDate),c()}else!e.ctrlKey||\"up\"!==t&&\"down\"!==t?(u.handleKeyDown(t,e),u.refreshView()):(n.toggleMode(\"up\"===t?1:-1),c())}}]).directive(\"datepicker\",function(){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/datepicker.html\",scope:{datepickerMode:\"=?\",dateDisabled:\"&\"},require:[\"datepicker\",\"?^ngModel\"],controller:\"DatepickerController\",link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o)}}}).directive(\"daypicker\",[\"dateFilter\",function(h){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/day.html\",require:\"^datepicker\",link:function(d,e,t,p){function r(e,t){return 1!==t||e%4!=0||e%100==0&&e%400!=0?n[t]:29}d.showWeeks=p.showWeeks,p.step={months:1},p.element=e;var n=[31,28,31,30,31,30,31,31,30,31,30,31];p._refreshView=function(){var e=p.activeDate.getFullYear(),t=p.activeDate.getMonth(),n=new Date(e,t,1),r=p.startingDay-n.getDay(),i=0<r?7-r:-r,o=new Date(n);0<i&&o.setDate(1-i);for(var a=function(e,t){var n=new Array(t),r=new Date(e),i=0;for(r.setHours(12);i<t;)n[i++]=new Date(r),r.setDate(r.getDate()+1);return n}(o,42),s=0;s<42;s++)a[s]=angular.extend(p.createDateObject(a[s],p.formatDay),{secondary:a[s].getMonth()!==t,uid:d.uniqueId+\"-\"+s});d.labels=new Array(7);for(var u=0;u<7;u++)d.labels[u]={abbr:h(a[u].date,p.formatDayHeader),full:h(a[u].date,\"EEEE\")};if(d.title=h(p.activeDate,p.formatDayTitle),d.rows=p.split(a,7),d.showWeeks){d.weekNumbers=[];for(var l=function(e){var t=new Date(e);t.setDate(t.getDate()+4-(t.getDay()||7));var n=t.getTime();return t.setMonth(0),t.setDate(1),Math.floor(Math.round((n-t)/864e5)/7)+1}(d.rows[0][0].date),c=d.rows.length;d.weekNumbers.push(l++)<c;);}},p.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth(),e.getDate())-new Date(t.getFullYear(),t.getMonth(),t.getDate())},p.handleKeyDown=function(e){var t=p.activeDate.getDate();if(\"left\"===e)t-=1;else if(\"up\"===e)t-=7;else if(\"right\"===e)t+=1;else if(\"down\"===e)t+=7;else if(\"pageup\"===e||\"pagedown\"===e){var n=p.activeDate.getMonth()+(\"pageup\"===e?-1:1);p.activeDate.setMonth(n,1),t=Math.min(r(p.activeDate.getFullYear(),p.activeDate.getMonth()),t)}else\"home\"===e?t=1:\"end\"===e&&(t=r(p.activeDate.getFullYear(),p.activeDate.getMonth()));p.activeDate.setDate(t)},p.refreshView()}}}]).directive(\"monthpicker\",[\"dateFilter\",function(o){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/month.html\",require:\"^datepicker\",link:function(r,e,t,i){i.step={years:1},i.element=e,i._refreshView=function(){for(var e=new Array(12),t=i.activeDate.getFullYear(),n=0;n<12;n++)e[n]=angular.extend(i.createDateObject(new Date(t,n,1),i.formatMonth),{uid:r.uniqueId+\"-\"+n});r.title=o(i.activeDate,i.formatMonthTitle),r.rows=i.split(e,3)},i.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth())-new Date(t.getFullYear(),t.getMonth())},i.handleKeyDown=function(e){var t=i.activeDate.getMonth();if(\"left\"===e)t-=1;else if(\"up\"===e)t-=3;else if(\"right\"===e)t+=1;else if(\"down\"===e)t+=3;else if(\"pageup\"===e||\"pagedown\"===e){var n=i.activeDate.getFullYear()+(\"pageup\"===e?-1:1);i.activeDate.setFullYear(n)}else\"home\"===e?t=0:\"end\"===e&&(t=11);i.activeDate.setMonth(t)},i.refreshView()}}}]).directive(\"yearpicker\",[\"dateFilter\",function(){return{restrict:\"EA\",replace:!0,templateUrl:\"template/datepicker/year.html\",require:\"^datepicker\",link:function(r,e,t,i){function o(e){return parseInt((e-1)/a,10)*a+1}var a=i.yearRange;i.step={years:a},i.element=e,i._refreshView=function(){for(var e=new Array(a),t=0,n=o(i.activeDate.getFullYear());t<a;t++)e[t]=angular.extend(i.createDateObject(new Date(n+t,0,1),i.formatYear),{uid:r.uniqueId+\"-\"+t});r.title=[e[0].label,e[a-1].label].join(\" - \"),r.rows=i.split(e,5)},i.compare=function(e,t){return e.getFullYear()-t.getFullYear()},i.handleKeyDown=function(e){var t=i.activeDate.getFullYear();\"left\"===e?t-=1:\"up\"===e?t-=5:\"right\"===e?t+=1:\"down\"===e?t+=5:\"pageup\"===e||\"pagedown\"===e?t+=(\"pageup\"===e?-1:1)*i.step.years:\"home\"===e?t=o(i.activeDate.getFullYear()):\"end\"===e&&(t=o(i.activeDate.getFullYear())+a-1),i.activeDate.setFullYear(t)},i.refreshView()}}}]).constant(\"datepickerPopupConfig\",{datepickerPopup:\"yyyy-MM-dd\",currentText:\"Today\",clearText:\"Clear\",closeText:\"Done\",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0}).directive(\"datepickerPopup\",[\"$compile\",\"$parse\",\"$document\",\"$position\",\"dateFilter\",\"dateParser\",\"datepickerPopupConfig\",function(f,g,m,v,y,w,b){return{restrict:\"EA\",require:\"ngModel\",scope:{isOpen:\"=?\",currentText:\"@\",clearText:\"@\",closeText:\"@\",dateDisabled:\"&\"},link:function(r,t,i,n){function o(e){return e.replace(/([A-Z])/g,function(e){return\"-\"+e.toLowerCase()})}function a(e){if(!e)return n.$setValidity(\"date\",!0),null;if(angular.isDate(e)&&!isNaN(e))return n.$setValidity(\"date\",!0),e;if(angular.isString(e)){var t=w.parse(e,s)||new Date(e);return isNaN(t)?void n.$setValidity(\"date\",!1):(n.$setValidity(\"date\",!0),t)}n.$setValidity(\"date\",!1)}var s,u=angular.isDefined(i.closeOnDateSelection)?r.$parent.$eval(i.closeOnDateSelection):b.closeOnDateSelection,l=angular.isDefined(i.datepickerAppendToBody)?r.$parent.$eval(i.datepickerAppendToBody):b.appendToBody;r.showButtonBar=angular.isDefined(i.showButtonBar)?r.$parent.$eval(i.showButtonBar):b.showButtonBar,r.getText=function(e){return r[e+\"Text\"]||b[e+\"Text\"]},i.$observe(\"datepickerPopup\",function(e){s=e||b.datepickerPopup,n.$render()});var e=angular.element(\"<div datepicker-popup-wrap><div datepicker></div></div>\");e.attr({\"ng-model\":\"date\",\"ng-change\":\"dateSelection()\"});var c=angular.element(e.children()[0]);i.datepickerOptions&&angular.forEach(r.$parent.$eval(i.datepickerOptions),function(e,t){c.attr(o(t),e)}),r.watchData={},angular.forEach([\"minDate\",\"maxDate\",\"datepickerMode\"],function(t){if(i[t]){var e=g(i[t]);if(r.$parent.$watch(e,function(e){r.watchData[t]=e}),c.attr(o(t),\"watchData.\"+t),\"datepickerMode\"===t){var n=e.assign;r.$watch(\"watchData.\"+t,function(e,t){e!==t&&n(r.$parent,e)})}}}),i.dateDisabled&&c.attr(\"date-disabled\",\"dateDisabled({ date: date, mode: mode })\"),n.$parsers.unshift(a),r.dateSelection=function(e){angular.isDefined(e)&&(r.date=e),n.$setViewValue(r.date),n.$render(),u&&(r.isOpen=!1,t[0].focus())},t.bind(\"input change keyup\",function(){r.$apply(function(){r.date=n.$modelValue})}),n.$render=function(){var e=n.$viewValue?y(n.$viewValue,s):\"\";t.val(e),r.date=a(n.$modelValue)};var d=function(e){r.isOpen&&e.target!==t[0]&&r.$apply(function(){r.isOpen=!1})},p=function(e){r.keydown(e)};t.bind(\"keydown\",p),r.keydown=function(e){27===e.which?(e.preventDefault(),e.stopPropagation(),r.close()):40!==e.which||r.isOpen||(r.isOpen=!0)},r.$watch(\"isOpen\",function(e){e?(r.$broadcast(\"datepicker.focus\"),r.position=l?v.offset(t):v.position(t),r.position.top=r.position.top+t.prop(\"offsetHeight\"),m.bind(\"click\",d)):m.unbind(\"click\",d)}),r.select=function(e){if(\"today\"===e){var t=new Date;angular.isDate(n.$modelValue)?(e=new Date(n.$modelValue)).setFullYear(t.getFullYear(),t.getMonth(),t.getDate()):e=new Date(t.setHours(0,0,0,0))}r.dateSelection(e)},r.close=function(){r.isOpen=!1,t[0].focus()};var h=f(e)(r);e.remove(),l?m.find(\"body\").append(h):t.after(h),r.$on(\"$destroy\",function(){h.remove(),t.unbind(\"keydown\",p),m.unbind(\"click\",d)})}}}]).directive(\"datepickerPopupWrap\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,templateUrl:\"template/datepicker/popup.html\",link:function(e,t){t.bind(\"click\",function(e){e.preventDefault(),e.stopPropagation()})}}}),angular.module(\"ui.bootstrap.dropdown\",[]).constant(\"dropdownConfig\",{openClass:\"open\"}).service(\"dropdownService\",[\"$document\",function(t){var n=null;this.open=function(e){n||(t.bind(\"click\",r),t.bind(\"keydown\",i)),n&&n!==e&&(n.isOpen=!1),n=e},this.close=function(e){n===e&&(n=null,t.unbind(\"click\",r),t.unbind(\"keydown\",i))};var r=function(e){if(n){var t=n.getToggleElement();e&&t&&t[0].contains(e.target)||n.$apply(function(){n.isOpen=!1})}},i=function(e){27===e.which&&(n.focusToggleElement(),r())}}]).controller(\"DropdownController\",[\"$scope\",\"$attrs\",\"$parse\",\"dropdownConfig\",\"dropdownService\",\"$animate\",function(n,t,r,e,i,o){var a,s=this,u=n.$new(),l=e.openClass,c=angular.noop,d=t.onToggle?r(t.onToggle):angular.noop;this.init=function(e){s.$element=e,t.isOpen&&(a=r(t.isOpen),c=a.assign,n.$watch(a,function(e){u.isOpen=!!e}))},this.toggle=function(e){return u.isOpen=arguments.length?!!e:!u.isOpen},this.isOpen=function(){return u.isOpen},u.getToggleElement=function(){return s.toggleElement},u.focusToggleElement=function(){s.toggleElement&&s.toggleElement[0].focus()},u.$watch(\"isOpen\",function(e,t){o[e?\"addClass\":\"removeClass\"](s.$element,l),e?(u.focusToggleElement(),i.open(u)):i.close(u),c(n,e),angular.isDefined(e)&&e!==t&&d(n,{open:!!e})}),n.$on(\"$locationChangeSuccess\",function(){u.isOpen=!1}),n.$on(\"$destroy\",function(){u.$destroy()})}]).directive(\"dropdown\",function(){return{controller:\"DropdownController\",link:function(e,t,n,r){r.init(t)}}}).directive(\"dropdownToggle\",function(){return{require:\"?^dropdown\",link:function(t,n,r,i){if(i){i.toggleElement=n;var e=function(e){e.preventDefault(),n.hasClass(\"disabled\")||r.disabled||t.$apply(function(){i.toggle()})};n.bind(\"click\",e),n.attr({\"aria-haspopup\":!0,\"aria-expanded\":!1}),t.$watch(i.isOpen,function(e){n.attr(\"aria-expanded\",!!e)}),t.$on(\"$destroy\",function(){n.unbind(\"click\",e)})}}}}),angular.module(\"ui.bootstrap.modal\",[\"ui.bootstrap.transition\"]).factory(\"$$stackedMap\",function(){return{createNew:function(){var r=[];return{add:function(e,t){r.push({key:e,value:t})},get:function(e){for(var t=0;t<r.length;t++)if(e==r[t].key)return r[t]},keys:function(){for(var e=[],t=0;t<r.length;t++)e.push(r[t].key);return e},top:function(){return r[r.length-1]},remove:function(e){for(var t=-1,n=0;n<r.length;n++)if(e==r[n].key){t=n;break}return r.splice(t,1)[0]},removeTop:function(){return r.splice(r.length-1,1)[0]},length:function(){return r.length}}}}}).directive(\"modalBackdrop\",[\"$timeout\",function(r){return{restrict:\"EA\",replace:!0,templateUrl:\"template/modal/backdrop.html\",link:function(e,t,n){e.backdropClass=n.backdropClass||\"\",e.animate=!1,r(function(){e.animate=!0})}}}]).directive(\"modalWindow\",[\"$modalStack\",\"$timeout\",function(r,i){return{restrict:\"EA\",scope:{index:\"@\",animate:\"=\"},replace:!0,transclude:!0,templateUrl:function(e,t){return t.templateUrl||\"template/modal/window.html\"},link:function(e,t,n){t.addClass(n.windowClass||\"\"),e.size=n.size,i(function(){e.animate=!0,t[0].querySelectorAll(\"[autofocus]\").length||t[0].focus()}),e.close=function(e){var t=r.getTop();t&&t.value.backdrop&&\"static\"!=t.value.backdrop&&e.target===e.currentTarget&&(e.preventDefault(),e.stopPropagation(),r.dismiss(t.key,\"backdrop click\"))}}}}]).directive(\"modalTransclude\",function(){return{link:function(e,t,n,r,i){i(e.$parent,function(e){t.empty(),t.append(e)})}}}).factory(\"$modalStack\",[\"$transition\",\"$timeout\",\"$document\",\"$compile\",\"$rootScope\",\"$$stackedMap\",function(s,u,l,c,d,e){function p(){for(var e=-1,t=m.keys(),n=0;n<t.length;n++)m.get(t[n]).value.backdrop&&(e=n);return e}function r(e){var t=l.find(\"body\").eq(0),n=m.get(e).value;m.remove(e),i(n.modalDomEl,n.modalScope,300,function(){n.modalScope.$destroy(),t.toggleClass(g,0<m.length()),function(){if(h&&-1==p()){var e=f;i(h,f,150,function(){e.$destroy(),e=null}),f=h=void 0}}()})}function i(e,t,n,r){function i(){i.done||(i.done=!0,e.remove(),r&&r())}t.animate=!1;var o=s.transitionEndEventName;if(o){var a=u(i,n);e.bind(o,function(){u.cancel(a),i(),t.$apply()})}else u(i)}var h,f,g=\"modal-open\",m=e.createNew(),n={};return d.$watch(p,function(e){f&&(f.index=e)}),l.bind(\"keydown\",function(e){var t;27===e.which&&((t=m.top())&&t.value.keyboard&&(e.preventDefault(),d.$apply(function(){n.dismiss(t.key,\"escape key press\")})))}),n.open=function(e,t){m.add(e,{deferred:t.deferred,modalScope:t.scope,backdrop:t.backdrop,keyboard:t.keyboard});var n=l.find(\"body\").eq(0),r=p();if(0<=r&&!h){(f=d.$new(!0)).index=r;var i=angular.element(\"<div modal-backdrop></div>\");i.attr(\"backdrop-class\",t.backdropClass),h=c(i)(f),n.append(h)}var o=angular.element(\"<div modal-window></div>\");o.attr({\"template-url\":t.windowTemplateUrl,\"window-class\":t.windowClass,size:t.size,index:m.length()-1,animate:\"animate\"}).html(t.content);var a=c(o)(t.scope);m.top().value.modalDomEl=a,n.append(a),n.addClass(g)},n.close=function(e,t){var n=m.get(e);n&&(n.value.deferred.resolve(t),r(e))},n.dismiss=function(e,t){var n=m.get(e);n&&(n.value.deferred.reject(t),r(e))},n.dismissAll=function(e){for(var t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},n.getTop=function(){return m.top()},n}]).provider(\"$modal\",function(){var p={options:{backdrop:!0,keyboard:!0},$get:[\"$injector\",\"$rootScope\",\"$q\",\"$http\",\"$templateCache\",\"$controller\",\"$modalStack\",function(n,u,r,i,l,c,d){var e={};return e.open=function(o){var a=r.defer(),e=r.defer(),s={result:a.promise,opened:e.promise,close:function(e){d.close(s,e)},dismiss:function(e){d.dismiss(s,e)}};if((o=angular.extend({},p.options,o)).resolve=o.resolve||{},!o.template&&!o.templateUrl)throw new Error(\"One of template or templateUrl options is required.\");var t=r.all([function(e){return e.template?r.when(e.template):i.get(angular.isFunction(e.templateUrl)?e.templateUrl():e.templateUrl,{cache:l}).then(function(e){return e.data})}(o)].concat(function(e){var t=[];return angular.forEach(e,function(e){(angular.isFunction(e)||angular.isArray(e))&&t.push(r.when(n.invoke(e)))}),t}(o.resolve)));return t.then(function(n){var e=(o.scope||u).$new();e.$close=s.close,e.$dismiss=s.dismiss;var t,r={},i=1;o.controller&&(r.$scope=e,r.$modalInstance=s,angular.forEach(o.resolve,function(e,t){r[t]=n[i++]}),t=c(o.controller,r),o.controllerAs&&(e[o.controllerAs]=t)),d.open(s,{scope:e,deferred:a,content:n[0],backdrop:o.backdrop,keyboard:o.keyboard,backdropClass:o.backdropClass,windowClass:o.windowClass,windowTemplateUrl:o.windowTemplateUrl,size:o.size})},function(e){a.reject(e)}),t.then(function(){e.resolve(!0)},function(){e.reject(!1)}),s},e}]};return p}),angular.module(\"ui.bootstrap.pagination\",[]).controller(\"PaginationController\",[\"$scope\",\"$attrs\",\"$parse\",function(n,r,i){var o=this,a={$setViewValue:angular.noop},t=r.numPages?i(r.numPages).assign:angular.noop;this.init=function(e,t){a=e,this.config=t,a.$render=function(){o.render()},r.itemsPerPage?n.$parent.$watch(i(r.itemsPerPage),function(e){o.itemsPerPage=parseInt(e,10),n.totalPages=o.calculateTotalPages()}):this.itemsPerPage=t.itemsPerPage},this.calculateTotalPages=function(){var e=this.itemsPerPage<1?1:Math.ceil(n.totalItems/this.itemsPerPage);return Math.max(e||0,1)},this.render=function(){n.page=parseInt(a.$viewValue,10)||1},n.selectPage=function(e){n.page!==e&&0<e&&e<=n.totalPages&&(a.$setViewValue(e),a.$render())},n.getText=function(e){return n[e+\"Text\"]||o.config[e+\"Text\"]},n.noPrevious=function(){return 1===n.page},n.noNext=function(){return n.page===n.totalPages},n.$watch(\"totalItems\",function(){n.totalPages=o.calculateTotalPages()}),n.$watch(\"totalPages\",function(e){t(n.$parent,e),n.page>e?n.selectPage(e):a.$render()})}]).constant(\"paginationConfig\",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:\"First\",previousText:\"Previous\",nextText:\"Next\",lastText:\"Last\",rotate:!0}).directive(\"pagination\",[\"$parse\",\"paginationConfig\",function(s,u){return{restrict:\"EA\",scope:{totalItems:\"=\",firstText:\"@\",previousText:\"@\",nextText:\"@\",lastText:\"@\"},require:[\"pagination\",\"?ngModel\"],controller:\"PaginationController\",templateUrl:\"template/pagination/pagination.html\",replace:!0,link:function(e,t,n,r){function c(e,t,n){return{number:e,text:t,active:n}}var i=r[0],o=r[1];if(o){var d=angular.isDefined(n.maxSize)?e.$parent.$eval(n.maxSize):u.maxSize,p=angular.isDefined(n.rotate)?e.$parent.$eval(n.rotate):u.rotate;e.boundaryLinks=angular.isDefined(n.boundaryLinks)?e.$parent.$eval(n.boundaryLinks):u.boundaryLinks,e.directionLinks=angular.isDefined(n.directionLinks)?e.$parent.$eval(n.directionLinks):u.directionLinks,i.init(o,u),n.maxSize&&e.$parent.$watch(s(n.maxSize),function(e){d=parseInt(e,10),i.render()});var a=i.render;i.render=function(){a(),0<e.page&&e.page<=e.totalPages&&(e.pages=function(e,t){var n=[],r=1,i=t,o=angular.isDefined(d)&&d<t;o&&(p?t<(i=(r=Math.max(e-Math.floor(d/2),1))+d-1)&&(r=(i=t)-d+1):(r=(Math.ceil(e/d)-1)*d+1,i=Math.min(r+d-1,t)));for(var a=r;a<=i;a++){var s=c(a,a,a===e);n.push(s)}if(o&&!p){if(1<r){var u=c(r-1,\"...\",!1);n.unshift(u)}if(i<t){var l=c(i+1,\"...\",!1);n.push(l)}}return n}(e.page,e.totalPages))}}}}}]).constant(\"pagerConfig\",{itemsPerPage:10,previousText:\"« Previous\",nextText:\"Next »\",align:!0}).directive(\"pager\",[\"pagerConfig\",function(a){return{restrict:\"EA\",scope:{totalItems:\"=\",previousText:\"@\",nextText:\"@\"},require:[\"pager\",\"?ngModel\"],controller:\"PaginationController\",templateUrl:\"template/pagination/pager.html\",replace:!0,link:function(e,t,n,r){var i=r[0],o=r[1];o&&(e.align=angular.isDefined(n.align)?e.$parent.$eval(n.align):a.align,i.init(o,a))}}}]),angular.module(\"ui.bootstrap.tooltip\",[\"ui.bootstrap.position\",\"ui.bootstrap.bindHtml\"]).provider(\"$tooltip\",function(){var s={placement:\"top\",animation:!0,popupDelay:0},u={mouseenter:\"mouseleave\",click:\"click\",focus:\"blur\"},l={};this.options=function(e){angular.extend(l,e)},this.setTriggers=function(e){angular.extend(u,e)},this.$get=[\"$window\",\"$compile\",\"$timeout\",\"$document\",\"$position\",\"$interpolate\",function(e,o,x,O,T,a){return function(S,C,n){function k(e){var t=e||D.trigger||n;return{show:t,hide:u[t]||t}}var D=angular.extend({},s,l),e=function(e){return e.replace(/[A-Z]/g,function(e,t){return(t?\"-\":\"\")+e.toLowerCase()})}(S),t=a.startSymbol(),r=a.endSymbol(),i=\"<div \"+e+'-popup title=\"'+t+\"title\"+r+'\" content=\"'+t+\"content\"+r+'\" placement=\"'+t+\"placement\"+r+'\" animation=\"animation\" is-open=\"isOpen\"></div>';return{restrict:\"EA\",compile:function(){var _=o(i);return function(e,t,n){function r(){m.isOpen?o():i()}function i(){(!g||e.$eval(n[C+\"Enable\"]))&&(function(){var e=n[C+\"Placement\"];m.placement=angular.isDefined(e)?e:D.placement}(),function(){var e=n[C+\"PopupDelay\"],t=parseInt(e,10);m.popupDelay=isNaN(t)?D.popupDelay:t}(),m.popupDelay?p||(p=x(a,m.popupDelay,!1)).then(function(e){e()}):a()())}function o(){e.$apply(function(){s()})}function a(){return p=null,d&&(x.cancel(d),d=null),m.content?(l&&u(),c=m.$new(),(l=_(c,function(e){h?O.find(\"body\").append(e):t.after(e)})).css({top:0,left:0,display:\"block\"}),m.$digest(),v(),m.isOpen=!0,m.$digest(),v):angular.noop}function s(){m.isOpen=!1,x.cancel(p),p=null,m.animation?d||(d=x(u,500)):u()}function u(){d=null,l&&(l.remove(),l=null),c&&(c.$destroy(),c=null)}var l,c,d,p,h=!!angular.isDefined(D.appendToBody)&&D.appendToBody,f=k(void 0),g=angular.isDefined(n[C+\"Enable\"]),m=e.$new(!0),v=function(){var e=T.positionElements(t,l,m.placement,h);e.top+=\"px\",e.left+=\"px\",l.css(e)};m.isOpen=!1,n.$observe(S,function(e){!(m.content=e)&&m.isOpen&&s()}),n.$observe(C+\"Title\",function(e){m.title=e});var y,w=function(){t.unbind(f.show,i),t.unbind(f.hide,o)};y=n[C+\"Trigger\"],w(),(f=k(y)).show===f.hide?t.bind(f.show,r):(t.bind(f.show,i),t.bind(f.hide,o));var b=e.$eval(n[C+\"Animation\"]);m.animation=angular.isDefined(b)?!!b:D.animation;var $=e.$eval(n[C+\"AppendToBody\"]);(h=angular.isDefined($)?$:h)&&e.$on(\"$locationChangeSuccess\",function(){m.isOpen&&s()}),e.$on(\"$destroy\",function(){x.cancel(d),x.cancel(p),w(),u(),m=null})}}}}}]}).directive(\"tooltipPopup\",function(){return{restrict:\"EA\",replace:!0,scope:{content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/tooltip/tooltip-popup.html\"}}).directive(\"tooltip\",[\"$tooltip\",function(e){return e(\"tooltip\",\"tooltip\",\"mouseenter\")}]).directive(\"tooltipHtmlUnsafePopup\",function(){return{restrict:\"EA\",replace:!0,scope:{content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/tooltip/tooltip-html-unsafe-popup.html\"}}).directive(\"tooltipHtmlUnsafe\",[\"$tooltip\",function(e){return e(\"tooltipHtmlUnsafe\",\"tooltip\",\"mouseenter\")}]),angular.module(\"ui.bootstrap.popover\",[\"ui.bootstrap.tooltip\"]).directive(\"popoverPopup\",function(){return{restrict:\"EA\",replace:!0,scope:{title:\"@\",content:\"@\",placement:\"@\",animation:\"&\",isOpen:\"&\"},templateUrl:\"template/popover/popover.html\"}}).directive(\"popover\",[\"$tooltip\",function(e){return e(\"popover\",\"popover\",\"click\")}]),angular.module(\"ui.bootstrap.progressbar\",[]).constant(\"progressConfig\",{animate:!0,max:100}).controller(\"ProgressController\",[\"$scope\",\"$attrs\",\"progressConfig\",function(n,e,t){var r=this,i=angular.isDefined(e.animate)?n.$parent.$eval(e.animate):t.animate;this.bars=[],n.max=angular.isDefined(e.max)?n.$parent.$eval(e.max):t.max,this.addBar=function(t,e){i||e.css({transition:\"none\"}),this.bars.push(t),t.$watch(\"value\",function(e){t.percent=+(100*e/n.max).toFixed(2)}),t.$on(\"$destroy\",function(){e=null,r.removeBar(t)})},this.removeBar=function(e){this.bars.splice(this.bars.indexOf(e),1)}}]).directive(\"progress\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,controller:\"ProgressController\",require:\"progress\",scope:{},templateUrl:\"template/progressbar/progress.html\"}}).directive(\"bar\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,require:\"^progress\",scope:{value:\"=\",type:\"@\"},templateUrl:\"template/progressbar/bar.html\",link:function(e,t,n,r){r.addBar(e,t)}}}).directive(\"progressbar\",function(){return{restrict:\"EA\",replace:!0,transclude:!0,controller:\"ProgressController\",scope:{value:\"=\",type:\"@\"},templateUrl:\"template/progressbar/progressbar.html\",link:function(e,t,n,r){r.addBar(e,angular.element(t.children()[0]))}}}),angular.module(\"ui.bootstrap.rating\",[]).constant(\"ratingConfig\",{max:5,stateOn:null,stateOff:null}).controller(\"RatingController\",[\"$scope\",\"$attrs\",\"ratingConfig\",function(n,r,i){var o={$setViewValue:angular.noop};this.init=function(e){(o=e).$render=this.render,this.stateOn=angular.isDefined(r.stateOn)?n.$parent.$eval(r.stateOn):i.stateOn,this.stateOff=angular.isDefined(r.stateOff)?n.$parent.$eval(r.stateOff):i.stateOff;var t=angular.isDefined(r.ratingStates)?n.$parent.$eval(r.ratingStates):new Array(angular.isDefined(r.max)?n.$parent.$eval(r.max):i.max);n.range=this.buildTemplateObjects(t)},this.buildTemplateObjects=function(e){for(var t=0,n=e.length;t<n;t++)e[t]=angular.extend({index:t},{stateOn:this.stateOn,stateOff:this.stateOff},e[t]);return e},n.rate=function(e){!n.readonly&&0<=e&&e<=n.range.length&&(o.$setViewValue(e),o.$render())},n.enter=function(e){n.readonly||(n.value=e),n.onHover({value:e})},n.reset=function(){n.value=o.$viewValue,n.onLeave()},n.onKeydown=function(e){/(37|38|39|40)/.test(e.which)&&(e.preventDefault(),e.stopPropagation(),n.rate(n.value+(38===e.which||39===e.which?1:-1)))},this.render=function(){n.value=o.$viewValue}}]).directive(\"rating\",function(){return{restrict:\"EA\",require:[\"rating\",\"ngModel\"],scope:{readonly:\"=?\",onHover:\"&\",onLeave:\"&\"},controller:\"RatingController\",templateUrl:\"template/rating/rating.html\",replace:!0,link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o)}}}),angular.module(\"ui.bootstrap.tabs\",[]).controller(\"TabsetController\",[\"$scope\",function(e){var r,i=this,o=i.tabs=e.tabs=[];i.select=function(t){angular.forEach(o,function(e){e.active&&e!==t&&(e.active=!1,e.onDeselect())}),t.active=!0,t.onSelect()},i.addTab=function(e){o.push(e),1===o.length?e.active=!0:e.active&&i.select(e)},i.removeTab=function(e){var t=o.indexOf(e);if(e.active&&1<o.length&&!r){var n=t==o.length-1?t-1:t+1;i.select(o[n])}o.splice(t,1)},e.$on(\"$destroy\",function(){r=!0})}]).directive(\"tabset\",function(){return{restrict:\"EA\",transclude:!0,replace:!0,scope:{type:\"@\"},controller:\"TabsetController\",templateUrl:\"template/tabs/tabset.html\",link:function(e,t,n){e.vertical=!!angular.isDefined(n.vertical)&&e.$parent.$eval(n.vertical),e.justified=!!angular.isDefined(n.justified)&&e.$parent.$eval(n.justified)}}}).directive(\"tab\",[\"$parse\",function(o){return{require:\"^tabset\",restrict:\"EA\",replace:!0,templateUrl:\"template/tabs/tab.html\",transclude:!0,scope:{active:\"=?\",heading:\"@\",onSelect:\"&select\",onDeselect:\"&deselect\"},controller:function(){},compile:function(e,t,i){return function(t,e,n,r){t.$watch(\"active\",function(e){e&&r.select(t)}),t.disabled=!1,n.disabled&&t.$parent.$watch(o(n.disabled),function(e){t.disabled=!!e}),t.select=function(){t.disabled||(t.active=!0)},r.addTab(t),t.$on(\"$destroy\",function(){r.removeTab(t)}),t.$transcludeFn=i}}}}]).directive(\"tabHeadingTransclude\",[function(){return{restrict:\"A\",require:\"^tab\",link:function(e,t){e.$watch(\"headingElement\",function(e){e&&(t.html(\"\"),t.append(e))})}}}]).directive(\"tabContentTransclude\",function(){return{restrict:\"A\",require:\"^tabset\",link:function(e,t,n){var r=e.$eval(n.tabContentTransclude);r.$transcludeFn(r.$parent,function(e){angular.forEach(e,function(e){!function(e){return e.tagName&&(e.hasAttribute(\"tab-heading\")||e.hasAttribute(\"data-tab-heading\")||\"tab-heading\"===e.tagName.toLowerCase()||\"data-tab-heading\"===e.tagName.toLowerCase())}(e)?t.append(e):r.headingElement=e})})}}}),angular.module(\"ui.bootstrap.timepicker\",[]).constant(\"timepickerConfig\",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0}).controller(\"TimepickerController\",[\"$scope\",\"$attrs\",\"$parse\",\"$log\",\"$locale\",\"timepickerConfig\",function(i,o,e,t,n,a){function r(){var e=parseInt(i.hours,10);return(i.showMeridian?0<e&&e<13:0<=e&&e<24)?(i.showMeridian&&(12===e&&(e=0),i.meridian===g[1]&&(e+=12)),e):void 0}function s(){var e=parseInt(i.minutes,10);return 0<=e&&e<60?e:void 0}function u(e){return angular.isDefined(e)&&e.toString().length<2?\"0\"+e:e}function l(e){c(),f.$setViewValue(new Date(h)),d(e)}function c(){f.$setValidity(\"time\",!0),i.invalidHours=!1,i.invalidMinutes=!1}function d(e){var t=h.getHours(),n=h.getMinutes();i.showMeridian&&(t=0===t||12===t?12:t%12),i.hours=\"h\"===e?t:u(t),i.minutes=\"m\"===e?n:u(n),i.meridian=h.getHours()<12?g[0]:g[1]}function p(e){var t=new Date(h.getTime()+6e4*e);h.setHours(t.getHours(),t.getMinutes()),l()}var h=new Date,f={$setViewValue:angular.noop},g=angular.isDefined(o.meridians)?i.$parent.$eval(o.meridians):a.meridians||n.DATETIME_FORMATS.AMPMS;this.init=function(e,t){(f=e).$render=this.render;var n=t.eq(0),r=t.eq(1);(angular.isDefined(o.mousewheel)?i.$parent.$eval(o.mousewheel):a.mousewheel)&&this.setupMousewheelEvents(n,r),i.readonlyInput=angular.isDefined(o.readonlyInput)?i.$parent.$eval(o.readonlyInput):a.readonlyInput,this.setupInputEvents(n,r)};var m=a.hourStep;o.hourStep&&i.$parent.$watch(e(o.hourStep),function(e){m=parseInt(e,10)});var v=a.minuteStep;o.minuteStep&&i.$parent.$watch(e(o.minuteStep),function(e){v=parseInt(e,10)}),i.showMeridian=a.showMeridian,o.showMeridian&&i.$parent.$watch(e(o.showMeridian),function(e){if(i.showMeridian=!!e,f.$error.time){var t=r(),n=s();angular.isDefined(t)&&angular.isDefined(n)&&(h.setHours(t),l())}else d()}),this.setupMousewheelEvents=function(e,t){var n=function(e){e.originalEvent&&(e=e.originalEvent);var t=e.wheelDelta?e.wheelDelta:-e.deltaY;return e.detail||0<t};e.bind(\"mousewheel wheel\",function(e){i.$apply(n(e)?i.incrementHours():i.decrementHours()),e.preventDefault()}),t.bind(\"mousewheel wheel\",function(e){i.$apply(n(e)?i.incrementMinutes():i.decrementMinutes()),e.preventDefault()})},this.setupInputEvents=function(e,t){if(i.readonlyInput)return i.updateHours=angular.noop,void(i.updateMinutes=angular.noop);var n=function(e,t){f.$setViewValue(null),f.$setValidity(\"time\",!1),angular.isDefined(e)&&(i.invalidHours=e),angular.isDefined(t)&&(i.invalidMinutes=t)};i.updateHours=function(){var e=r();angular.isDefined(e)?(h.setHours(e),l(\"h\")):n(!0)},e.bind(\"blur\",function(){!i.invalidHours&&i.hours<10&&i.$apply(function(){i.hours=u(i.hours)})}),i.updateMinutes=function(){var e=s();angular.isDefined(e)?(h.setMinutes(e),l(\"m\")):n(void 0,!0)},t.bind(\"blur\",function(){!i.invalidMinutes&&i.minutes<10&&i.$apply(function(){i.minutes=u(i.minutes)})})},this.render=function(){var e=f.$modelValue?new Date(f.$modelValue):null;isNaN(e)?(f.$setValidity(\"time\",!1),t.error('Timepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(e&&(h=e),c(),d())},i.incrementHours=function(){p(60*m)},i.decrementHours=function(){p(60*-m)},i.incrementMinutes=function(){p(v)},i.decrementMinutes=function(){p(-v)},i.toggleMeridian=function(){p(720*(h.getHours()<12?1:-1))}}]).directive(\"timepicker\",function(){return{restrict:\"EA\",require:[\"timepicker\",\"?^ngModel\"],controller:\"TimepickerController\",replace:!0,scope:{},templateUrl:\"template/timepicker/timepicker.html\",link:function(e,t,n,r){var i=r[0],o=r[1];o&&i.init(o,t.find(\"input\"))}}}),angular.module(\"ui.bootstrap.typeahead\",[\"ui.bootstrap.position\",\"ui.bootstrap.bindHtml\"]).factory(\"typeaheadParser\",[\"$parse\",function(n){var r=/^\\s*([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?\\s+for\\s+(?:([\\$\\w][\\$\\w\\d]*))\\s+in\\s+([\\s\\S]+?)$/;return{parse:function(e){var t=e.match(r);if(!t)throw new Error('Expected typeahead specification in form of \"_modelValue_ (as _label_)? for _item_ in _collection_\" but got \"'+e+'\".');return{itemName:t[3],source:n(t[4]),viewMapper:n(t[2]||t[1]),modelMapper:n(t[1])}}}}]).directive(\"typeahead\",[\"$compile\",\"$parse\",\"$q\",\"$timeout\",\"$document\",\"$position\",\"typeaheadParser\",function(k,D,x,O,T,A,E){var P=[9,13,27,38,40];return{require:\"ngModel\",link:function(o,a,e,s){var u,t=o.$eval(e.typeaheadMinLength)||1,n=o.$eval(e.typeaheadWaitMs)||0,r=!1!==o.$eval(e.typeaheadEditable),l=D(e.typeaheadLoading).assign||angular.noop,i=D(e.typeaheadOnSelect),c=e.typeaheadInputFormatter?D(e.typeaheadInputFormatter):void 0,d=!!e.typeaheadAppendToBody&&o.$eval(e.typeaheadAppendToBody),p=!1!==o.$eval(e.typeaheadFocusFirst),h=D(e.ngModel).assign,f=E.parse(e.typeahead),g=o.$new();o.$on(\"$destroy\",function(){g.$destroy()});var m=\"typeahead-\"+g.$id+\"-\"+Math.floor(1e4*Math.random());a.attr({\"aria-autocomplete\":\"list\",\"aria-expanded\":!1,\"aria-owns\":m});var v=angular.element(\"<div typeahead-popup></div>\");v.attr({id:m,matches:\"matches\",active:\"activeIdx\",select:\"select(activeIdx)\",query:\"query\",position:\"position\"}),angular.isDefined(e.typeaheadTemplateUrl)&&v.attr(\"template-url\",e.typeaheadTemplateUrl);var y=function(){g.matches=[],g.activeIdx=-1,a.attr(\"aria-expanded\",!1)},w=function(e){return m+\"-option-\"+e};g.$watch(\"activeIdx\",function(e){e<0?a.removeAttr(\"aria-activedescendant\"):a.attr(\"aria-activedescendant\",w(e))});var b=function(r){var i={$viewValue:r};l(o,!0),x.when(f.source(o,i)).then(function(e){var t=r===s.$viewValue;if(t&&u)if(0<e.length){g.activeIdx=p?0:-1;for(var n=g.matches.length=0;n<e.length;n++)i[f.itemName]=e[n],g.matches.push({id:w(n),label:f.viewMapper(g,i),model:e[n]});g.query=r,g.position=d?A.offset(a):A.position(a),g.position.top=g.position.top+a.prop(\"offsetHeight\"),a.attr(\"aria-expanded\",!0)}else y();t&&l(o,!1)},function(){y(),l(o,!1)})};y(),g.query=void 0;var $,_=function(){$&&O.cancel($)};s.$parsers.unshift(function(e){return u=!0,e&&e.length>=t?0<n?(_(),function(e){$=O(function(){b(e)},n)}(e)):b(e):(l(o,!1),_(),y()),r?e:e?void s.$setValidity(\"editable\",!1):(s.$setValidity(\"editable\",!0),e)}),s.$formatters.push(function(e){var t,n={};return c?(n.$model=e,c(o,n)):(n[f.itemName]=e,t=f.viewMapper(o,n),n[f.itemName]=void 0,t!==f.viewMapper(o,n)?t:e)}),g.select=function(e){var t,n,r={};r[f.itemName]=n=g.matches[e].model,t=f.modelMapper(o,r),h(o,t),s.$setValidity(\"editable\",!0),i(o,{$item:n,$model:t,$label:f.viewMapper(o,r)}),y(),O(function(){a[0].focus()},0,!1)},a.bind(\"keydown\",function(e){0!==g.matches.length&&-1!==P.indexOf(e.which)&&(-1!=g.activeIdx||13!==e.which&&9!==e.which)&&(e.preventDefault(),40===e.which?(g.activeIdx=(g.activeIdx+1)%g.matches.length,g.$digest()):38===e.which?(g.activeIdx=(0<g.activeIdx?g.activeIdx:g.matches.length)-1,g.$digest()):13===e.which||9===e.which?g.$apply(function(){g.select(g.activeIdx)}):27===e.which&&(e.stopPropagation(),y(),g.$digest()))}),a.bind(\"blur\",function(){u=!1});var S=function(e){a[0]!==e.target&&(y(),g.$digest())};T.bind(\"click\",S),o.$on(\"$destroy\",function(){T.unbind(\"click\",S),d&&C.remove()});var C=k(v)(g);d?T.find(\"body\").append(C):a.after(C)}}}]).directive(\"typeaheadPopup\",function(){return{restrict:\"EA\",scope:{matches:\"=\",query:\"=\",active:\"=\",position:\"=\",select:\"&\"},replace:!0,templateUrl:\"template/typeahead/typeahead-popup.html\",link:function(t,e,n){t.templateUrl=n.templateUrl,t.isOpen=function(){return 0<t.matches.length},t.isActive=function(e){return t.active==e},t.selectActive=function(e){t.active=e},t.selectMatch=function(e){t.select({activeIdx:e})}}}}).directive(\"typeaheadMatch\",[\"$http\",\"$templateCache\",\"$compile\",\"$parse\",function(i,o,a,s){return{restrict:\"EA\",scope:{index:\"=\",match:\"=\",query:\"=\"},link:function(t,n,e){var r=s(e.templateUrl)(t.$parent)||\"template/typeahead/typeahead-match.html\";i.get(r,{cache:o}).success(function(e){n.replaceWith(a(e.trim())(t))})}}}]).filter(\"typeaheadHighlight\",function(){return function(e,t){return t?(\"\"+e).replace(new RegExp(function(e){return e.replace(/([.?*+^$[\\]\\\\(){}|-])/g,\"\\\\$1\")}(t),\"gi\"),\"<strong>$&</strong>\"):e}}),angular.module(\"template/accordion/accordion-group.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/accordion/accordion-group.html\",'<div class=\"panel panel-default\">\\n  <div class=\"panel-heading\">\\n    <h4 class=\"panel-title\">\\n      <a href class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{\\'text-muted\\': isDisabled}\">{{heading}}</span></a>\\n    </h4>\\n  </div>\\n  <div class=\"panel-collapse\" collapse=\"!isOpen\">\\n\\t  <div class=\"panel-body\" ng-transclude></div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/accordion/accordion.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/accordion/accordion.html\",'<div class=\"panel-group\" ng-transclude></div>')}]),angular.module(\"template/alert/alert.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/alert/alert.html\",'<div class=\"alert\" ng-class=\"[\\'alert-\\' + (type || \\'warning\\'), closeable ? \\'alert-dismissable\\' : null]\" role=\"alert\">\\n    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\\n        <span aria-hidden=\"true\">&times;</span>\\n        <span class=\"sr-only\">Close</span>\\n    </button>\\n    <div ng-transclude></div>\\n</div>\\n')}]),angular.module(\"template/carousel/carousel.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/carousel/carousel.html\",'<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\\n    <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\\n        <li ng-repeat=\"slide in slides track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\\n    </ol>\\n    <div class=\"carousel-inner\" ng-transclude></div>\\n    <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\\n    <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-right\"></span></a>\\n</div>\\n')}]),angular.module(\"template/carousel/slide.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/carousel/slide.html\",\"<div ng-class=\\\"{\\n    'active': leaving || (active && !entering),\\n    'prev': (next || active) && direction=='prev',\\n    'next': (next || active) && direction=='next',\\n    'right': direction=='prev',\\n    'left': direction=='next'\\n  }\\\" class=\\\"item text-center\\\" ng-transclude></div>\\n\")}]),angular.module(\"template/datepicker/datepicker.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/datepicker.html\",'<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\\n  <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\\n  <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\\n  <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\\n</div>')}]),angular.module(\"template/datepicker/day.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/day.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n    <tr>\\n      <th ng-show=\"showWeeks\" class=\"text-center\"></th>\\n      <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-muted\\': dt.secondary, \\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/datepicker/month.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/month.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/datepicker/popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/popup.html\",'<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && \\'block\\') || \\'none\\', top: position.top+\\'px\\', left: position.left+\\'px\\'}\" ng-keydown=\"keydown($event)\">\\n\\t<li ng-transclude></li>\\n\\t<li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\\n\\t\\t<span class=\"btn-group pull-left\">\\n\\t\\t\\t<button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select(\\'today\\')\">{{ getText(\\'current\\') }}</button>\\n\\t\\t\\t<button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText(\\'clear\\') }}</button>\\n\\t\\t</span>\\n\\t\\t<button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText(\\'close\\') }}</button>\\n\\t</li>\\n</ul>\\n')}]),angular.module(\"template/datepicker/year.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/datepicker/year.html\",'<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\\n  <thead>\\n    <tr>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\\n      <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\\n      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\\n    </tr>\\n  </thead>\\n  <tbody>\\n    <tr ng-repeat=\"row in rows track by $index\">\\n      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\\n        <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{\\'btn-info\\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{\\'text-info\\': dt.current}\">{{dt.label}}</span></button>\\n      </td>\\n    </tr>\\n  </tbody>\\n</table>\\n')}]),angular.module(\"template/modal/backdrop.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/modal/backdrop.html\",'<div class=\"modal-backdrop fade {{ backdropClass }}\"\\n     ng-class=\"{in: animate}\"\\n     ng-style=\"{\\'z-index\\': 1040 + (index && 1 || 0) + index*10}\"\\n></div>\\n')}]),angular.module(\"template/modal/window.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/modal/window.html\",'<div tabindex=\"-1\" role=\"dialog\" class=\"modal fade\" ng-class=\"{in: animate}\" ng-style=\"{\\'z-index\\': 1050 + index*10, display: \\'block\\'}\" ng-click=\"close($event)\">\\n    <div class=\"modal-dialog\" ng-class=\"{\\'modal-sm\\': size == \\'sm\\', \\'modal-lg\\': size == \\'lg\\'}\"><div class=\"modal-content\" modal-transclude></div></div>\\n</div>')}]),angular.module(\"template/pagination/pager.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/pagination/pager.html\",'<ul class=\"pager\">\\n  <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1)\">{{getText(\\'previous\\')}}</a></li>\\n  <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1)\">{{getText(\\'next\\')}}</a></li>\\n</ul>')}]),angular.module(\"template/pagination/pagination.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/pagination/pagination.html\",'<ul class=\"pagination\">\\n  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1)\">{{getText(\\'first\\')}}</a></li>\\n  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1)\">{{getText(\\'previous\\')}}</a></li>\\n  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\\n  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1)\">{{getText(\\'next\\')}}</a></li>\\n  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages)\">{{getText(\\'last\\')}}</a></li>\\n</ul>')}]),angular.module(\"template/tooltip/tooltip-html-unsafe-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tooltip/tooltip-html-unsafe-popup.html\",'<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"tooltip-arrow\"></div>\\n  <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\\n</div>\\n')}]),angular.module(\"template/tooltip/tooltip-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tooltip/tooltip-popup.html\",'<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"tooltip-arrow\"></div>\\n  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\\n</div>\\n')}]),angular.module(\"template/popover/popover.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/popover/popover.html\",'<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\\n  <div class=\"arrow\"></div>\\n\\n  <div class=\"popover-inner\">\\n      <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\\n      <div class=\"popover-content\" ng-bind=\"content\"></div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/progressbar/bar.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/bar.html\",'<div class=\"progress-bar\" ng-class=\"type && \\'progress-bar-\\' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + \\'%\\'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>')}]),angular.module(\"template/progressbar/progress.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/progress.html\",'<div class=\"progress\" ng-transclude></div>')}]),angular.module(\"template/progressbar/progressbar.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/progressbar/progressbar.html\",'<div class=\"progress\">\\n  <div class=\"progress-bar\" ng-class=\"type && \\'progress-bar-\\' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + \\'%\\'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\\n</div>')}]),angular.module(\"template/rating/rating.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/rating/rating.html\",'<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\\n    <i ng-repeat=\"r in range track by $index\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || \\'glyphicon-star\\') || (r.stateOff || \\'glyphicon-star-empty\\')\">\\n        <span class=\"sr-only\">({{ $index < value ? \\'*\\' : \\' \\' }})</span>\\n    </i>\\n</span>')}]),angular.module(\"template/tabs/tab.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tabs/tab.html\",'<li ng-class=\"{active: active, disabled: disabled}\">\\n  <a href ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\\n</li>\\n')}]),angular.module(\"template/tabs/tabset.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/tabs/tabset.html\",'<div>\\n  <ul class=\"nav nav-{{type || \\'tabs\\'}}\" ng-class=\"{\\'nav-stacked\\': vertical, \\'nav-justified\\': justified}\" ng-transclude></ul>\\n  <div class=\"tab-content\">\\n    <div class=\"tab-pane\" \\n         ng-repeat=\"tab in tabs\" \\n         ng-class=\"{active: tab.active}\"\\n         tab-content-transclude=\"tab\">\\n    </div>\\n  </div>\\n</div>\\n')}]),angular.module(\"template/timepicker/timepicker.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/timepicker/timepicker.html\",'<table>\\n\\t<tbody>\\n\\t\\t<tr class=\"text-center\">\\n\\t\\t\\t<td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\\n\\t\\t\\t<td>&nbsp;</td>\\n\\t\\t\\t<td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"></td>\\n\\t\\t</tr>\\n\\t\\t<tr>\\n\\t\\t\\t<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{\\'has-error\\': invalidHours}\">\\n\\t\\t\\t\\t<input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\\n\\t\\t\\t</td>\\n\\t\\t\\t<td>:</td>\\n\\t\\t\\t<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{\\'has-error\\': invalidMinutes}\">\\n\\t\\t\\t\\t<input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\\n\\t\\t\\t</td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\\n\\t\\t</tr>\\n\\t\\t<tr class=\"text-center\">\\n\\t\\t\\t<td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\\n\\t\\t\\t<td>&nbsp;</td>\\n\\t\\t\\t<td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\\n\\t\\t\\t<td ng-show=\"showMeridian\"></td>\\n\\t\\t</tr>\\n\\t</tbody>\\n</table>\\n')}]),angular.module(\"template/typeahead/typeahead-match.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/typeahead/typeahead-match.html\",'<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>')}]),angular.module(\"template/typeahead/typeahead-popup.html\",[]).run([\"$templateCache\",function(e){e.put(\"template/typeahead/typeahead-popup.html\",'<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+\\'px\\', left: position.left+\\'px\\'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\\n    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\\n        <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\\n    </li>\\n</ul>\\n')}]),function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){\"use strict\";var e,i;function p(){return e.apply(null,arguments)}function a(e){return e instanceof Array||\"[object Array]\"===Object.prototype.toString.call(e)}function s(e){return null!=e&&\"[object Object]\"===Object.prototype.toString.call(e)}function o(e){return void 0===e}function u(e){return\"number\"==typeof e||\"[object Number]\"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||\"[object Date]\"===Object.prototype.toString.call(e)}function r(e,t){var n,r=[];for(n=0;n<e.length;++n)r.push(t(e[n],n));return r}function h(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function c(e,t){for(var n in t)h(t,n)&&(e[n]=t[n]);return h(t,\"toString\")&&(e.toString=t.toString),h(t,\"valueOf\")&&(e.valueOf=t.valueOf),e}function d(e,t,n,r){return xt(e,t,n,r,!0).utc()}function f(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function g(e){if(null==e._isValid){var t=f(e),n=i.call(t.parsedDateParts,function(e){return null!=e}),r=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(r=r&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return r;e._isValid=r}return e._isValid}function m(e){var t=d(NaN);return null!=e?c(f(t),e):f(t).userInvalidated=!0,t}i=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,r=0;r<n;r++)if(r in t&&e.call(this,t[r],r,t))return!0;return!1};var v=p.momentProperties=[];function y(e,t){var n,r,i;if(o(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),o(t._i)||(e._i=t._i),o(t._f)||(e._f=t._f),o(t._l)||(e._l=t._l),o(t._strict)||(e._strict=t._strict),o(t._tzm)||(e._tzm=t._tzm),o(t._isUTC)||(e._isUTC=t._isUTC),o(t._offset)||(e._offset=t._offset),o(t._pf)||(e._pf=f(t)),o(t._locale)||(e._locale=t._locale),0<v.length)for(n=0;n<v.length;n++)o(i=t[r=v[n]])||(e[r]=i);return e}var t=!1;function w(e){y(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===t&&(t=!0,p.updateOffset(this),t=!1)}function b(e){return e instanceof w||null!=e&&null!=e._isAMomentObject}function $(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function _(e){var t=+e,n=0;return 0!=t&&isFinite(t)&&(n=$(t)),n}function S(e,t,n){var r,i=Math.min(e.length,t.length),o=Math.abs(e.length-t.length),a=0;for(r=0;r<i;r++)(n&&e[r]!==t[r]||!n&&_(e[r])!==_(t[r]))&&a++;return a+o}function C(e){!1===p.suppressDeprecationWarnings&&\"undefined\"!=typeof console&&console.warn&&console.warn(\"Deprecation warning: \"+e)}function n(i,o){var a=!0;return c(function(){if(null!=p.deprecationHandler&&p.deprecationHandler(null,i),a){for(var e,t=[],n=0;n<arguments.length;n++){if(e=\"\",\"object\"==typeof arguments[n]){for(var r in e+=\"\\n[\"+n+\"] \",arguments[0])e+=r+\": \"+arguments[0][r]+\", \";e=e.slice(0,-2)}else e=arguments[n];t.push(e)}C(i+\"\\nArguments: \"+Array.prototype.slice.call(t).join(\"\")+\"\\n\"+(new Error).stack),a=!1}return o.apply(this,arguments)},o)}var k,D={};function x(e,t){null!=p.deprecationHandler&&p.deprecationHandler(e,t),D[e]||(C(t),D[e]=!0)}function O(e){return e instanceof Function||\"[object Function]\"===Object.prototype.toString.call(e)}function T(e,t){var n,r=c({},e);for(n in t)h(t,n)&&(s(e[n])&&s(t[n])?(r[n]={},c(r[n],e[n]),c(r[n],t[n])):null!=t[n]?r[n]=t[n]:delete r[n]);for(n in e)h(e,n)&&!h(t,n)&&s(e[n])&&(r[n]=c({},r[n]));return r}function A(e){null!=e&&this.set(e)}p.suppressDeprecationWarnings=!1,p.deprecationHandler=null,k=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)h(e,t)&&n.push(t);return n};var E={};function P(e,t){var n=e.toLowerCase();E[n]=E[n+\"s\"]=E[t]=e}function M(e){return\"string\"==typeof e?E[e]||E[e.toLowerCase()]:void 0}function R(e){var t,n,r={};for(n in e)h(e,n)&&(t=M(n))&&(r[t]=e[n]);return r}var I={};function V(e,t){I[e]=t}function L(e,t,n){var r=\"\"+Math.abs(e),i=t-r.length;return(0<=e?n?\"+\":\"\":\"-\")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var F=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,j=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,H={},N={};function Y(e,t,n,r){var i=r;\"string\"==typeof r&&(i=function(){return this[r]()}),e&&(N[e]=i),t&&(N[t[0]]=function(){return L(i.apply(this,arguments),t[1],t[2])}),n&&(N[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function U(e,t){return e.isValid()?(t=q(t,e.localeData()),H[t]=H[t]||function(r){var e,i,t,o=r.match(F);for(e=0,i=o.length;e<i;e++)N[o[e]]?o[e]=N[o[e]]:o[e]=(t=o[e]).match(/\\[[\\s\\S]/)?t.replace(/^\\[|\\]$/g,\"\"):t.replace(/\\\\/g,\"\");return function(e){var t,n=\"\";for(t=0;t<i;t++)n+=O(o[t])?o[t].call(e,r):o[t];return n}}(t),H[t](e)):e.localeData().invalidDate()}function q(e,t){var n=5;function r(e){return t.longDateFormat(e)||e}for(j.lastIndex=0;0<=n&&j.test(e);)e=e.replace(j,r),j.lastIndex=0,n-=1;return e}var z=/\\d/,B=/\\d\\d/,W=/\\d{3}/,G=/\\d{4}/,K=/[+-]?\\d{6}/,Q=/\\d\\d?/,Z=/\\d\\d\\d\\d?/,X=/\\d\\d\\d\\d\\d\\d?/,J=/\\d{1,3}/,ee=/\\d{1,4}/,te=/[+-]?\\d{1,6}/,ne=/\\d+/,re=/[+-]?\\d+/,ie=/Z|[+-]\\d\\d:?\\d\\d/gi,oe=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,ae=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i,se={};function ue(e,n,r){se[e]=O(n)?n:function(e,t){return e&&r?r:n}}function le(e,t){return h(se,e)?se[e](t._strict,t._locale):new RegExp(function(e){return ce(e.replace(\"\\\\\",\"\").replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,function(e,t,n,r,i){return t||n||r||i}))}(e))}function ce(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\")}var de={};function pe(e,n){var t,r=n;for(\"string\"==typeof e&&(e=[e]),u(n)&&(r=function(e,t){t[n]=_(e)}),t=0;t<e.length;t++)de[e[t]]=r}function he(e,i){pe(e,function(e,t,n,r){n._w=n._w||{},i(e,n._w,n,r)})}var fe=0,ge=1,me=2,ve=3,ye=4,we=5,be=6,$e=7,_e=8;function Se(e){return Ce(e)?366:365}function Ce(e){return e%4==0&&e%100!=0||e%400==0}Y(\"Y\",0,0,function(){var e=this.year();return e<=9999?\"\"+e:\"+\"+e}),Y(0,[\"YY\",2],0,function(){return this.year()%100}),Y(0,[\"YYYY\",4],0,\"year\"),Y(0,[\"YYYYY\",5],0,\"year\"),Y(0,[\"YYYYYY\",6,!0],0,\"year\"),P(\"year\",\"y\"),V(\"year\",1),ue(\"Y\",re),ue(\"YY\",Q,B),ue(\"YYYY\",ee,G),ue(\"YYYYY\",te,K),ue(\"YYYYYY\",te,K),pe([\"YYYYY\",\"YYYYYY\"],fe),pe(\"YYYY\",function(e,t){t[fe]=2===e.length?p.parseTwoDigitYear(e):_(e)}),pe(\"YY\",function(e,t){t[fe]=p.parseTwoDigitYear(e)}),pe(\"Y\",function(e,t){t[fe]=parseInt(e,10)}),p.parseTwoDigitYear=function(e){return _(e)+(68<_(e)?1900:2e3)};var ke,De=xe(\"FullYear\",!0);function xe(t,n){return function(e){return null!=e?(Te(this,t,e),p.updateOffset(this,n),this):Oe(this,t)}}function Oe(e,t){return e.isValid()?e._d[\"get\"+(e._isUTC?\"UTC\":\"\")+t]():NaN}function Te(e,t,n){e.isValid()&&!isNaN(n)&&(\"FullYear\"===t&&Ce(e.year())&&1===e.month()&&29===e.date()?e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+t](n,e.month(),Ae(n,e.month())):e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+t](n))}function Ae(e,t){if(isNaN(e)||isNaN(t))return NaN;var n=function(e,t){return(e%t+t)%t}(t,12);return e+=(t-n)/12,1===n?Ce(e)?29:28:31-n%7%2}ke=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},Y(\"M\",[\"MM\",2],\"Mo\",function(){return this.month()+1}),Y(\"MMM\",0,0,function(e){return this.localeData().monthsShort(this,e)}),Y(\"MMMM\",0,0,function(e){return this.localeData().months(this,e)}),P(\"month\",\"M\"),V(\"month\",8),ue(\"M\",Q),ue(\"MM\",Q,B),ue(\"MMM\",function(e,t){return t.monthsShortRegex(e)}),ue(\"MMMM\",function(e,t){return t.monthsRegex(e)}),pe([\"M\",\"MM\"],function(e,t){t[ge]=_(e)-1}),pe([\"MMM\",\"MMMM\"],function(e,t,n,r){var i=n._locale.monthsParse(e,r,n._strict);null!=i?t[ge]=i:f(n).invalidMonth=e});var Ee=/D[oD]?(\\[[^\\[\\]]*\\]|\\s)+MMMM?/,Pe=\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\");var Me=\"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec\".split(\"_\");function Re(e,t){var n;if(!e.isValid())return e;if(\"string\"==typeof t)if(/^\\d+$/.test(t))t=_(t);else if(!u(t=e.localeData().monthsParse(t)))return e;return n=Math.min(e.date(),Ae(e.year(),t)),e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+\"Month\"](t,n),e}function Ie(e){return null!=e?(Re(this,e),p.updateOffset(this,!0),this):Oe(this,\"Month\")}var Ve=ae;var Le=ae;function Fe(){function e(e,t){return t.length-e.length}var t,n,r=[],i=[],o=[];for(t=0;t<12;t++)n=d([2e3,t]),r.push(this.monthsShort(n,\"\")),i.push(this.months(n,\"\")),o.push(this.months(n,\"\")),o.push(this.monthsShort(n,\"\"));for(r.sort(e),i.sort(e),o.sort(e),t=0;t<12;t++)r[t]=ce(r[t]),i[t]=ce(i[t]);for(t=0;t<24;t++)o[t]=ce(o[t]);this._monthsRegex=new RegExp(\"^(\"+o.join(\"|\")+\")\",\"i\"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp(\"^(\"+i.join(\"|\")+\")\",\"i\"),this._monthsShortStrictRegex=new RegExp(\"^(\"+r.join(\"|\")+\")\",\"i\")}function je(e){var t;if(e<100&&0<=e){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function He(e,t,n){var r=7+t-n;return r-(7+je(e,0,r).getUTCDay()-t)%7-1}function Ne(e,t,n,r,i){var o,a,s=1+7*(t-1)+(7+n-r)%7+He(e,r,i);return a=s<=0?Se(o=e-1)+s:s>Se(e)?(o=e+1,s-Se(e)):(o=e,s),{year:o,dayOfYear:a}}function Ye(e,t,n){var r,i,o=He(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=He(e,t,n),i=He(e+1,t,n);return(Se(e)-r+i)/7}Y(\"w\",[\"ww\",2],\"wo\",\"week\"),Y(\"W\",[\"WW\",2],\"Wo\",\"isoWeek\"),P(\"week\",\"w\"),P(\"isoWeek\",\"W\"),V(\"week\",5),V(\"isoWeek\",5),ue(\"w\",Q),ue(\"ww\",Q,B),ue(\"W\",Q),ue(\"WW\",Q,B),he([\"w\",\"ww\",\"W\",\"WW\"],function(e,t,n,r){t[r.substr(0,1)]=_(e)});function qe(e,t){return e.slice(t,7).concat(e.slice(0,t))}Y(\"d\",0,\"do\",\"day\"),Y(\"dd\",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),Y(\"ddd\",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),Y(\"dddd\",0,0,function(e){return this.localeData().weekdays(this,e)}),Y(\"e\",0,0,\"weekday\"),Y(\"E\",0,0,\"isoWeekday\"),P(\"day\",\"d\"),P(\"weekday\",\"e\"),P(\"isoWeekday\",\"E\"),V(\"day\",11),V(\"weekday\",11),V(\"isoWeekday\",11),ue(\"d\",Q),ue(\"e\",Q),ue(\"E\",Q),ue(\"dd\",function(e,t){return t.weekdaysMinRegex(e)}),ue(\"ddd\",function(e,t){return t.weekdaysShortRegex(e)}),ue(\"dddd\",function(e,t){return t.weekdaysRegex(e)}),he([\"dd\",\"ddd\",\"dddd\"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e}),he([\"d\",\"e\",\"E\"],function(e,t,n,r){t[r]=_(e)});var ze=\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\");var Be=\"Sun_Mon_Tue_Wed_Thu_Fri_Sat\".split(\"_\");var We=\"Su_Mo_Tu_We_Th_Fr_Sa\".split(\"_\");var Ge=ae;var Ke=ae;var Qe=ae;function Ze(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],s=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),r=this.weekdaysMin(n,\"\"),i=this.weekdaysShort(n,\"\"),o=this.weekdays(n,\"\"),a.push(r),s.push(i),u.push(o),l.push(r),l.push(i),l.push(o);for(a.sort(e),s.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)s[t]=ce(s[t]),u[t]=ce(u[t]),l[t]=ce(l[t]);this._weekdaysRegex=new RegExp(\"^(\"+l.join(\"|\")+\")\",\"i\"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp(\"^(\"+u.join(\"|\")+\")\",\"i\"),this._weekdaysShortStrictRegex=new RegExp(\"^(\"+s.join(\"|\")+\")\",\"i\"),this._weekdaysMinStrictRegex=new RegExp(\"^(\"+a.join(\"|\")+\")\",\"i\")}function Xe(){return this.hours()%12||12}function Je(e,t){Y(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}Y(\"H\",[\"HH\",2],0,\"hour\"),Y(\"h\",[\"hh\",2],0,Xe),Y(\"k\",[\"kk\",2],0,function(){return this.hours()||24}),Y(\"hmm\",0,0,function(){return\"\"+Xe.apply(this)+L(this.minutes(),2)}),Y(\"hmmss\",0,0,function(){return\"\"+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),Y(\"Hmm\",0,0,function(){return\"\"+this.hours()+L(this.minutes(),2)}),Y(\"Hmmss\",0,0,function(){return\"\"+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Je(\"a\",!0),Je(\"A\",!1),P(\"hour\",\"h\"),V(\"hour\",13),ue(\"a\",et),ue(\"A\",et),ue(\"H\",Q),ue(\"h\",Q),ue(\"k\",Q),ue(\"HH\",Q,B),ue(\"hh\",Q,B),ue(\"kk\",Q,B),ue(\"hmm\",Z),ue(\"hmmss\",X),ue(\"Hmm\",Z),ue(\"Hmmss\",X),pe([\"H\",\"HH\"],ve),pe([\"k\",\"kk\"],function(e,t,n){var r=_(e);t[ve]=24===r?0:r}),pe([\"a\",\"A\"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe([\"h\",\"hh\"],function(e,t,n){t[ve]=_(e),f(n).bigHour=!0}),pe(\"hmm\",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r)),f(n).bigHour=!0}),pe(\"hmmss\",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i)),f(n).bigHour=!0}),pe(\"Hmm\",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r))}),pe(\"Hmmss\",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i))});var tt,nt=xe(\"Hours\",!0),rt={calendar:{sameDay:\"[Today at] LT\",nextDay:\"[Tomorrow at] LT\",nextWeek:\"dddd [at] LT\",lastDay:\"[Yesterday at] LT\",lastWeek:\"[Last] dddd [at] LT\",sameElse:\"L\"},longDateFormat:{LTS:\"h:mm:ss A\",LT:\"h:mm A\",L:\"MM/DD/YYYY\",LL:\"MMMM D, YYYY\",LLL:\"MMMM D, YYYY h:mm A\",LLLL:\"dddd, MMMM D, YYYY h:mm A\"},invalidDate:\"Invalid date\",ordinal:\"%d\",dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:\"in %s\",past:\"%s ago\",s:\"a few seconds\",ss:\"%d seconds\",m:\"a minute\",mm:\"%d minutes\",h:\"an hour\",hh:\"%d hours\",d:\"a day\",dd:\"%d days\",M:\"a month\",MM:\"%d months\",y:\"a year\",yy:\"%d years\"},months:Pe,monthsShort:Me,week:{dow:0,doy:6},weekdays:ze,weekdaysMin:We,weekdaysShort:Be,meridiemParse:/[ap]\\.?m?\\.?/i},it={},ot={};function at(e){return e?e.toLowerCase().replace(\"_\",\"-\"):e}function st(e){var t=null;if(!it[e]&&\"undefined\"!=typeof module&&module&&module.exports)try{t=tt._abbr,require(\"./locale/\"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=o(t)?ct(e):lt(e,t))?tt=n:\"undefined\"!=typeof console&&console.warn&&console.warn(\"Locale \"+e+\" not found. Did you forget to load it?\")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,r=rt;if(t.abbr=e,null!=it[e])x(\"defineLocaleOverride\",\"use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.\"),r=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])r=it[t.parentLocale]._config;else{if(null==(n=st(t.parentLocale)))return ot[t.parentLocale]||(ot[t.parentLocale]=[]),ot[t.parentLocale].push({name:e,config:t}),null;r=n._config}return it[e]=new A(T(r,t)),ot[e]&&ot[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ct(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!a(e)){if(t=st(e))return t;e=[e]}return function(e){for(var t,n,r,i,o=0;o<e.length;){for(t=(i=at(e[o]).split(\"-\")).length,n=(n=at(e[o+1]))?n.split(\"-\"):null;0<t;){if(r=st(i.slice(0,t).join(\"-\")))return r;if(n&&n.length>=t&&S(i,n,!0)>=t-1)break;t--}o++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[ge]<0||11<n[ge]?ge:n[me]<1||n[me]>Ae(n[fe],n[ge])?me:n[ve]<0||24<n[ve]||24===n[ve]&&(0!==n[ye]||0!==n[we]||0!==n[be])?ve:n[ye]<0||59<n[ye]?ye:n[we]<0||59<n[we]?we:n[be]<0||999<n[be]?be:-1,f(e)._overflowDayOfYear&&(t<fe||me<t)&&(t=me),f(e)._overflowWeeks&&-1===t&&(t=$e),f(e)._overflowWeekday&&-1===t&&(t=_e),f(e).overflow=t),e}function pt(e,t,n){return null!=e?e:null!=t?t:n}function ht(e){var t,n,r,i,o,a=[];if(!e._d){for(r=function(e){var t=new Date(p.now());return e._useUTC?[t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()]:[t.getFullYear(),t.getMonth(),t.getDate()]}(e),e._w&&null==e._a[me]&&null==e._a[ge]&&function(e){var t,n,r,i,o,a,s,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)o=1,a=4,n=pt(t.GG,e._a[fe],Ye(Ot(),1,4).year),r=pt(t.W,1),((i=pt(t.E,1))<1||7<i)&&(u=!0);else{o=e._locale._week.dow,a=e._locale._week.doy;var l=Ye(Ot(),o,a);n=pt(t.gg,e._a[fe],l.year),r=pt(t.w,l.week),null!=t.d?((i=t.d)<0||6<i)&&(u=!0):null!=t.e?(i=t.e+o,(t.e<0||6<t.e)&&(u=!0)):i=o}r<1||r>Ue(n,o,a)?f(e)._overflowWeeks=!0:null!=u?f(e)._overflowWeekday=!0:(s=Ne(n,r,i,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=pt(e._a[fe],r[fe]),(e._dayOfYear>Se(o)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=je(o,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ve]&&0===e._a[ye]&&0===e._a[we]&&0===e._a[be]&&(e._nextDay=!0,e._a[ve]=0),e._d=(e._useUTC?je:function(e,t,n,r,i,o,a){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,r,i,o,a),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,o,a),s}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ve]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(f(e).weekdayMismatch=!0)}}var ft=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,gt=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,mt=/Z|[+-]\\d\\d(?::?\\d\\d)?/,vt=[[\"YYYYYY-MM-DD\",/[+-]\\d{6}-\\d\\d-\\d\\d/],[\"YYYY-MM-DD\",/\\d{4}-\\d\\d-\\d\\d/],[\"GGGG-[W]WW-E\",/\\d{4}-W\\d\\d-\\d/],[\"GGGG-[W]WW\",/\\d{4}-W\\d\\d/,!1],[\"YYYY-DDD\",/\\d{4}-\\d{3}/],[\"YYYY-MM\",/\\d{4}-\\d\\d/,!1],[\"YYYYYYMMDD\",/[+-]\\d{10}/],[\"YYYYMMDD\",/\\d{8}/],[\"GGGG[W]WWE\",/\\d{4}W\\d{3}/],[\"GGGG[W]WW\",/\\d{4}W\\d{2}/,!1],[\"YYYYDDD\",/\\d{7}/]],yt=[[\"HH:mm:ss.SSSS\",/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],[\"HH:mm:ss,SSSS\",/\\d\\d:\\d\\d:\\d\\d,\\d+/],[\"HH:mm:ss\",/\\d\\d:\\d\\d:\\d\\d/],[\"HH:mm\",/\\d\\d:\\d\\d/],[\"HHmmss.SSSS\",/\\d\\d\\d\\d\\d\\d\\.\\d+/],[\"HHmmss,SSSS\",/\\d\\d\\d\\d\\d\\d,\\d+/],[\"HHmmss\",/\\d\\d\\d\\d\\d\\d/],[\"HHmm\",/\\d\\d\\d\\d/],[\"HH\",/\\d\\d/]],wt=/^\\/?Date\\((\\-?\\d+)/i;function bt(e){var t,n,r,i,o,a,s=e._i,u=ft.exec(s)||gt.exec(s);if(u){for(f(e).iso=!0,t=0,n=vt.length;t<n;t++)if(vt[t][1].exec(u[1])){i=vt[t][0],r=!1!==vt[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=yt.length;t<n;t++)if(yt[t][1].exec(u[3])){o=(u[2]||\" \")+yt[t][0];break}if(null==o)return void(e._isValid=!1)}if(!r&&null!=o)return void(e._isValid=!1);if(u[4]){if(!mt.exec(u[4]))return void(e._isValid=!1);a=\"Z\"}e._f=i+(o||\"\")+(a||\"\"),kt(e)}else e._isValid=!1}var $t=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/;function _t(e,t,n,r,i,o){var a=[function(e){var t=parseInt(e,10);{if(t<=49)return 2e3+t;if(t<=999)return 1900+t}return t}(e),Me.indexOf(t),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return o&&a.push(parseInt(o,10)),a}var St={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Ct(e){var t=$t.exec(function(e){return e.replace(/\\([^)]*\\)|[\\n\\t]/g,\" \").replace(/(\\s\\s+)/g,\" \").replace(/^\\s\\s*/,\"\").replace(/\\s\\s*$/,\"\")}(e._i));if(t){var n=_t(t[4],t[3],t[2],t[5],t[6],t[7]);if(!function(e,t,n){return!e||Be.indexOf(e)===new Date(t[0],t[1],t[2]).getDay()||(f(n).weekdayMismatch=!0,n._isValid=!1)}(t[1],n,e))return;e._a=n,e._tzm=function(e,t,n){if(e)return St[e];if(t)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(t[8],t[9],t[10]),e._d=je.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),f(e).rfc2822=!0}else e._isValid=!1}function kt(e){if(e._f!==p.ISO_8601)if(e._f!==p.RFC_2822){e._a=[],f(e).empty=!0;var t,n,r,i,o,a,s,u,l=\"\"+e._i,c=l.length,d=0;for(r=q(e._f,e._locale).match(F)||[],t=0;t<r.length;t++)i=r[t],(n=(l.match(le(i,e))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&f(e).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),N[i]?(n?f(e).empty=!1:f(e).unusedTokens.push(i),a=i,u=e,null!=(s=n)&&h(de,a)&&de[a](s,u._a,u,a)):e._strict&&!n&&f(e).unusedTokens.push(i);f(e).charsLeftOver=c-d,0<l.length&&f(e).unusedInput.push(l),e._a[ve]<=12&&!0===f(e).bigHour&&0<e._a[ve]&&(f(e).bigHour=void 0),f(e).parsedDateParts=e._a.slice(0),f(e).meridiem=e._meridiem,e._a[ve]=function(e,t,n){var r;if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0)),t)}(e._locale,e._a[ve],e._meridiem),ht(e),dt(e)}else Ct(e);else bt(e)}function Dt(e){var t=e._i,n=e._f;return e._locale=e._locale||ct(e._l),null===t||void 0===n&&\"\"===t?m({nullInput:!0}):(\"string\"==typeof t&&(e._i=t=e._locale.preparse(t)),b(t)?new w(dt(t)):(l(t)?e._d=t:a(n)?function(e){var t,n,r,i,o;if(0===e._f.length)return f(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<e._f.length;i++)o=0,t=y({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],kt(t),g(t)&&(o+=f(t).charsLeftOver,o+=10*f(t).unusedTokens.length,f(t).score=o,(null==r||o<r)&&(r=o,n=t));c(e,n||t)}(e):n?kt(e):function(e){var t=e._i;o(t)?e._d=new Date(p.now()):l(t)?e._d=new Date(t.valueOf()):\"string\"==typeof t?function(e){var t=wt.exec(e._i);null===t?(bt(e),!1===e._isValid&&(delete e._isValid,Ct(e),!1===e._isValid&&(delete e._isValid,p.createFromInputFallback(e)))):e._d=new Date(+t[1])}(e):a(t)?(e._a=r(t.slice(0),function(e){return parseInt(e,10)}),ht(e)):s(t)?function(e){if(!e._d){var t=R(e._i);e._a=r([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),ht(e)}}(e):u(t)?e._d=new Date(t):p.createFromInputFallback(e)}(e),g(e)||(e._d=null),e))}function xt(e,t,n,r,i){var o={};return!0!==n&&!1!==n||(r=n,n=void 0),(s(e)&&function(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}(e)||a(e)&&0===e.length)&&(e=void 0),o._isAMomentObject=!0,o._useUTC=o._isUTC=i,o._l=n,o._i=e,o._f=t,o._strict=r,function(e){var t=new w(dt(Dt(e)));return t._nextDay&&(t.add(1,\"d\"),t._nextDay=void 0),t}(o)}function Ot(e,t,n,r){return xt(e,t,n,r,!1)}p.createFromInputFallback=n(\"value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.\",function(e){e._d=new Date(e._i+(e._useUTC?\" UTC\":\"\"))}),p.ISO_8601=function(){},p.RFC_2822=function(){};var Tt=n(\"moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/\",function(){var e=Ot.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:m()}),At=n(\"moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/\",function(){var e=Ot.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:m()});function Et(e,t){var n,r;if(1===t.length&&a(t[0])&&(t=t[0]),!t.length)return Ot();for(n=t[0],r=1;r<t.length;++r)t[r].isValid()&&!t[r][e](n)||(n=t[r]);return n}var Pt=[\"year\",\"quarter\",\"month\",\"week\",\"day\",\"hour\",\"minute\",\"second\",\"millisecond\"];function Mt(e){var t=R(e),n=t.year||0,r=t.quarter||0,i=t.month||0,o=t.week||t.isoWeek||0,a=t.day||0,s=t.hour||0,u=t.minute||0,l=t.second||0,c=t.millisecond||0;this._isValid=function(e){for(var t in e)if(-1===ke.call(Pt,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,r=0;r<Pt.length;++r)if(e[Pt[r]]){if(n)return!1;parseFloat(e[Pt[r]])!==_(e[Pt[r]])&&(n=!0)}return!0}(t),this._milliseconds=+c+1e3*l+6e4*u+1e3*s*60*60,this._days=+a+7*o,this._months=+i+3*r+12*n,this._data={},this._locale=ct(),this._bubble()}function Rt(e){return e instanceof Mt}function It(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Vt(e,n){Y(e,0,0,function(){var e=this.utcOffset(),t=\"+\";return e<0&&(e=-e,t=\"-\"),t+L(~~(e/60),2)+n+L(~~e%60,2)})}Vt(\"Z\",\":\"),Vt(\"ZZ\",\"\"),ue(\"Z\",oe),ue(\"ZZ\",oe),pe([\"Z\",\"ZZ\"],function(e,t,n){n._useUTC=!0,n._tzm=Ft(oe,e)});var Lt=/([\\+\\-]|\\d\\d)/gi;function Ft(e,t){var n=(t||\"\").match(e);if(null===n)return null;var r=((n[n.length-1]||[])+\"\").match(Lt)||[\"-\",0,0],i=60*r[1]+_(r[2]);return 0===i?0:\"+\"===r[0]?i:-i}function jt(e,t){var n,r;return t._isUTC?(n=t.clone(),r=(b(e)||l(e)?e.valueOf():Ot(e).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),p.updateOffset(n,!1),n):Ot(e).local()}function Ht(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Nt(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}p.updateOffset=function(){};var Yt=/^(\\-|\\+)?(?:(\\d*)[. ])?(\\d+)\\:(\\d+)(?:\\:(\\d+)(\\.\\d*)?)?$/,Ut=/^(-|\\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function qt(e,t){var n,r,i,o=e,a=null;return Rt(e)?o={ms:e._milliseconds,d:e._days,M:e._months}:u(e)?(o={},t?o[t]=e:o.milliseconds=e):(a=Yt.exec(e))?(n=\"-\"===a[1]?-1:1,o={y:0,d:_(a[me])*n,h:_(a[ve])*n,m:_(a[ye])*n,s:_(a[we])*n,ms:_(It(1e3*a[be]))*n}):(a=Ut.exec(e))?(n=\"-\"===a[1]?-1:1,o={y:zt(a[2],n),M:zt(a[3],n),w:zt(a[4],n),d:zt(a[5],n),h:zt(a[6],n),m:zt(a[7],n),s:zt(a[8],n)}):null==o?o={}:\"object\"==typeof o&&(\"from\"in o||\"to\"in o)&&(i=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=jt(t,e),e.isBefore(t)?n=Bt(e,t):((n=Bt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(Ot(o.from),Ot(o.to)),(o={}).ms=i.milliseconds,o.M=i.months),r=new Mt(o),Rt(e)&&h(e,\"_locale\")&&(r._locale=e._locale),r}function zt(e,t){var n=e&&parseFloat(e.replace(\",\",\".\"));return(isNaN(n)?0:n)*t}function Bt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,\"M\").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,\"M\"),n}function Wt(r,i){return function(e,t){var n;return null===t||isNaN(+t)||(x(i,\"moment().\"+i+\"(period, number) is deprecated. Please use moment().\"+i+\"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.\"),n=e,e=t,t=n),Gt(this,qt(e=\"string\"==typeof e?+e:e,t),r),this}}function Gt(e,t,n,r){var i=t._milliseconds,o=It(t._days),a=It(t._months);e.isValid()&&(r=null==r||r,a&&Re(e,Oe(e,\"Month\")+a*n),o&&Te(e,\"Date\",Oe(e,\"Date\")+o*n),i&&e._d.setTime(e._d.valueOf()+i*n),r&&p.updateOffset(e,o||a))}qt.fn=Mt.prototype,qt.invalid=function(){return qt(NaN)};var Kt=Wt(1,\"add\"),Qt=Wt(-1,\"subtract\");function Zt(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),r=e.clone().add(n,\"months\");return-(n+(t-r<0?(t-r)/(r-e.clone().add(n-1,\"months\")):(t-r)/(e.clone().add(1+n,\"months\")-r)))||0}function Xt(e){var t;return void 0===e?this._locale._abbr:(null!=(t=ct(e))&&(this._locale=t),this)}p.defaultFormat=\"YYYY-MM-DDTHH:mm:ssZ\",p.defaultFormatUtc=\"YYYY-MM-DDTHH:mm:ss[Z]\";var Jt=n(\"moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.\",function(e){return void 0===e?this.localeData():this.locale(e)});function en(){return this._locale}var tn=126227808e5;function nn(e,t){return(e%t+t)%t}function rn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-tn:new Date(e,t,n).valueOf()}function on(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-tn:Date.UTC(e,t,n)}function an(e,t){Y(0,[e,e.length],0,t)}function sn(e,t,n,r,i){var o;return null==e?Ye(this,r,i).year:((o=Ue(e,r,i))<t&&(t=o),function(e,t,n,r,i){var o=Ne(e,t,n,r,i),a=je(o.year,0,o.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}.call(this,e,t,n,r,i))}Y(0,[\"gg\",2],0,function(){return this.weekYear()%100}),Y(0,[\"GG\",2],0,function(){return this.isoWeekYear()%100}),an(\"gggg\",\"weekYear\"),an(\"ggggg\",\"weekYear\"),an(\"GGGG\",\"isoWeekYear\"),an(\"GGGGG\",\"isoWeekYear\"),P(\"weekYear\",\"gg\"),P(\"isoWeekYear\",\"GG\"),V(\"weekYear\",1),V(\"isoWeekYear\",1),ue(\"G\",re),ue(\"g\",re),ue(\"GG\",Q,B),ue(\"gg\",Q,B),ue(\"GGGG\",ee,G),ue(\"gggg\",ee,G),ue(\"GGGGG\",te,K),ue(\"ggggg\",te,K),he([\"gggg\",\"ggggg\",\"GGGG\",\"GGGGG\"],function(e,t,n,r){t[r.substr(0,2)]=_(e)}),he([\"gg\",\"GG\"],function(e,t,n,r){t[r]=p.parseTwoDigitYear(e)}),Y(\"Q\",0,\"Qo\",\"quarter\"),P(\"quarter\",\"Q\"),V(\"quarter\",7),ue(\"Q\",z),pe(\"Q\",function(e,t){t[ge]=3*(_(e)-1)}),Y(\"D\",[\"DD\",2],\"Do\",\"date\"),P(\"date\",\"D\"),V(\"date\",9),ue(\"D\",Q),ue(\"DD\",Q,B),ue(\"Do\",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),pe([\"D\",\"DD\"],me),pe(\"Do\",function(e,t){t[me]=_(e.match(Q)[0])});var un=xe(\"Date\",!0);Y(\"DDD\",[\"DDDD\",3],\"DDDo\",\"dayOfYear\"),P(\"dayOfYear\",\"DDD\"),V(\"dayOfYear\",4),ue(\"DDD\",J),ue(\"DDDD\",W),pe([\"DDD\",\"DDDD\"],function(e,t,n){n._dayOfYear=_(e)}),Y(\"m\",[\"mm\",2],0,\"minute\"),P(\"minute\",\"m\"),V(\"minute\",14),ue(\"m\",Q),ue(\"mm\",Q,B),pe([\"m\",\"mm\"],ye);var ln=xe(\"Minutes\",!1);Y(\"s\",[\"ss\",2],0,\"second\"),P(\"second\",\"s\"),V(\"second\",15),ue(\"s\",Q),ue(\"ss\",Q,B),pe([\"s\",\"ss\"],we);var cn,dn=xe(\"Seconds\",!1);for(Y(\"S\",0,0,function(){return~~(this.millisecond()/100)}),Y(0,[\"SS\",2],0,function(){return~~(this.millisecond()/10)}),Y(0,[\"SSS\",3],0,\"millisecond\"),Y(0,[\"SSSS\",4],0,function(){return 10*this.millisecond()}),Y(0,[\"SSSSS\",5],0,function(){return 100*this.millisecond()}),Y(0,[\"SSSSSS\",6],0,function(){return 1e3*this.millisecond()}),Y(0,[\"SSSSSSS\",7],0,function(){return 1e4*this.millisecond()}),Y(0,[\"SSSSSSSS\",8],0,function(){return 1e5*this.millisecond()}),Y(0,[\"SSSSSSSSS\",9],0,function(){return 1e6*this.millisecond()}),P(\"millisecond\",\"ms\"),V(\"millisecond\",16),ue(\"S\",J,z),ue(\"SS\",J,B),ue(\"SSS\",J,W),cn=\"SSSS\";cn.length<=9;cn+=\"S\")ue(cn,ne);function pn(e,t){t[be]=_(1e3*(\"0.\"+e))}for(cn=\"S\";cn.length<=9;cn+=\"S\")pe(cn,pn);var hn=xe(\"Milliseconds\",!1);Y(\"z\",0,0,\"zoneAbbr\"),Y(\"zz\",0,0,\"zoneName\");var fn=w.prototype;function gn(e){return e}fn.add=Kt,fn.calendar=function(e,t){var n=e||Ot(),r=jt(n,this).startOf(\"day\"),i=p.calendarFormat(this,r)||\"sameElse\",o=t&&(O(t[i])?t[i].call(this,n):t[i]);return this.format(o||this.localeData().calendar(i,this,Ot(n)))},fn.clone=function(){return new w(this)},fn.diff=function(e,t,n){var r,i,o;if(!this.isValid())return NaN;if(!(r=jt(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=M(t)){case\"year\":o=Zt(this,r)/12;break;case\"month\":o=Zt(this,r);break;case\"quarter\":o=Zt(this,r)/3;break;case\"second\":o=(this-r)/1e3;break;case\"minute\":o=(this-r)/6e4;break;case\"hour\":o=(this-r)/36e5;break;case\"day\":o=(this-r-i)/864e5;break;case\"week\":o=(this-r-i)/6048e5;break;default:o=this-r}return n?o:$(o)},fn.endOf=function(e){var t;if(void 0===(e=M(e))||\"millisecond\"===e||!this.isValid())return this;var n=this._isUTC?on:rn;switch(e){case\"year\":t=n(this.year()+1,0,1)-1;break;case\"quarter\":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case\"month\":t=n(this.year(),this.month()+1,1)-1;break;case\"week\":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case\"isoWeek\":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case\"day\":case\"date\":t=n(this.year(),this.month(),this.date()+1)-1;break;case\"hour\":t=this._d.valueOf(),t+=36e5-nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case\"minute\":t=this._d.valueOf(),t+=6e4-nn(t,6e4)-1;break;case\"second\":t=this._d.valueOf(),t+=1e3-nn(t,1e3)-1}return this._d.setTime(t),p.updateOffset(this,!0),this},fn.format=function(e){e||(e=this.isUtc()?p.defaultFormatUtc:p.defaultFormat);var t=U(this,e);return this.localeData().postformat(t)},fn.from=function(e,t){return this.isValid()&&(b(e)&&e.isValid()||Ot(e).isValid())?qt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},fn.fromNow=function(e){return this.from(Ot(),e)},fn.to=function(e,t){return this.isValid()&&(b(e)&&e.isValid()||Ot(e).isValid())?qt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},fn.toNow=function(e){return this.to(Ot(),e)},fn.get=function(e){return O(this[e=M(e)])?this[e]():this},fn.invalidAt=function(){return f(this).overflow},fn.isAfter=function(e,t){var n=b(e)?e:Ot(e);return!(!this.isValid()||!n.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},fn.isBefore=function(e,t){var n=b(e)?e:Ot(e);return!(!this.isValid()||!n.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},fn.isBetween=function(e,t,n,r){var i=b(e)?e:Ot(e),o=b(t)?t:Ot(t);return!!(this.isValid()&&i.isValid()&&o.isValid())&&(\"(\"===(r=r||\"()\")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(\")\"===r[1]?this.isBefore(o,n):!this.isAfter(o,n))},fn.isSame=function(e,t){var n,r=b(e)?e:Ot(e);return!(!this.isValid()||!r.isValid())&&(\"millisecond\"===(t=M(t)||\"millisecond\")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},fn.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},fn.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},fn.isValid=function(){return g(this)},fn.lang=Jt,fn.locale=Xt,fn.localeData=en,fn.max=At,fn.min=Tt,fn.parsingFlags=function(){return c({},f(this))},fn.set=function(e,t){if(\"object\"==typeof e)for(var n=function(e){var t=[];for(var n in e)t.push({unit:n,priority:I[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}(e=R(e)),r=0;r<n.length;r++)this[n[r].unit](e[n[r].unit]);else if(O(this[e=M(e)]))return this[e](t);return this},fn.startOf=function(e){var t;if(void 0===(e=M(e))||\"millisecond\"===e||!this.isValid())return this;var n=this._isUTC?on:rn;switch(e){case\"year\":t=n(this.year(),0,1);break;case\"quarter\":t=n(this.year(),this.month()-this.month()%3,1);break;case\"month\":t=n(this.year(),this.month(),1);break;case\"week\":t=n(this.year(),this.month(),this.date()-this.weekday());break;case\"isoWeek\":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case\"day\":case\"date\":t=n(this.year(),this.month(),this.date());break;case\"hour\":t=this._d.valueOf(),t-=nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case\"minute\":t=this._d.valueOf(),t-=nn(t,6e4);break;case\"second\":t=this._d.valueOf(),t-=nn(t,1e3)}return this._d.setTime(t),p.updateOffset(this,!0),this},fn.subtract=Qt,fn.toArray=function(){return[this.year(),this.month(),this.date(),this.hour(),this.minute(),this.second(),this.millisecond()]},fn.toObject=function(){return{years:this.year(),months:this.month(),date:this.date(),hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()}},fn.toDate=function(){return new Date(this.valueOf())},fn.toISOString=function(e){if(!this.isValid())return null;var t=!0!==e,n=t?this.clone().utc():this;return n.year()<0||9999<n.year()?U(n,t?\"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ\"):O(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace(\"Z\",U(n,\"Z\")):U(n,t?\"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]\":\"YYYY-MM-DD[T]HH:mm:ss.SSSZ\")},fn.inspect=function(){if(!this.isValid())return\"moment.invalid(/* \"+this._i+\" */)\";var e=\"moment\",t=\"\";this.isLocal()||(e=0===this.utcOffset()?\"moment.utc\":\"moment.parseZone\",t=\"Z\");var n=\"[\"+e+'(\"]',r=0<=this.year()&&this.year()<=9999?\"YYYY\":\"YYYYYY\",i=t+'[\")]';return this.format(n+r+\"-MM-DD[T]HH:mm:ss.SSS\"+i)},fn.toJSON=function(){return this.isValid()?this.toISOString():null},fn.toString=function(){return this.clone().locale(\"en\").format(\"ddd MMM DD YYYY HH:mm:ss [GMT]ZZ\")},fn.unix=function(){return Math.floor(this.valueOf()/1e3)},fn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},fn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},fn.year=De,fn.isLeapYear=function(){return Ce(this.year())},fn.weekYear=function(e){return sn.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},fn.isoWeekYear=function(e){return sn.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},fn.quarter=fn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},fn.month=Ie,fn.daysInMonth=function(){return Ae(this.year(),this.month())},fn.week=fn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),\"d\")},fn.isoWeek=fn.isoWeeks=function(e){var t=Ye(this,1,4).week;return null==e?t:this.add(7*(e-t),\"d\")},fn.weeksInYear=function(){var e=this.localeData()._week;return Ue(this.year(),e.dow,e.doy)},fn.isoWeeksInYear=function(){return Ue(this.year(),1,4)},fn.date=un,fn.day=fn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=function(e,t){return\"string\"!=typeof e?e:isNaN(e)?\"number\"==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}(e,this.localeData()),this.add(e-t,\"d\")):t},fn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,\"d\")},fn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null==e)return this.day()||7;var t=function(e,t){return\"string\"==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}(e,this.localeData());return this.day(this.day()%7?t:t-7)},fn.dayOfYear=function(e){var t=Math.round((this.clone().startOf(\"day\")-this.clone().startOf(\"year\"))/864e5)+1;return null==e?t:this.add(e-t,\"d\")},fn.hour=fn.hours=nt,fn.minute=fn.minutes=ln,fn.second=fn.seconds=dn,fn.millisecond=fn.milliseconds=hn,fn.utcOffset=function(e,t,n){var r,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Ht(this);if(\"string\"==typeof e){if(null===(e=Ft(oe,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(r=Ht(this)),this._offset=e,this._isUTC=!0,null!=r&&this.add(r,\"m\"),i!==e&&(!t||this._changeInProgress?Gt(this,qt(e-i,\"m\"),1,!1):this._changeInProgress||(this._changeInProgress=!0,p.updateOffset(this,!0),this._changeInProgress=null)),this},fn.utc=function(e){return this.utcOffset(0,e)},fn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Ht(this),\"m\")),this},fn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if(\"string\"==typeof this._i){var e=Ft(ie,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},fn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?Ot(e).utcOffset():0,(this.utcOffset()-e)%60==0)},fn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},fn.isLocal=function(){return!!this.isValid()&&!this._isUTC},fn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},fn.isUtc=Nt,fn.isUTC=Nt,fn.zoneAbbr=function(){return this._isUTC?\"UTC\":\"\"},fn.zoneName=function(){return this._isUTC?\"Coordinated Universal Time\":\"\"},fn.dates=n(\"dates accessor is deprecated. Use date instead.\",un),fn.months=n(\"months accessor is deprecated. Use month instead\",Ie),fn.years=n(\"years accessor is deprecated. Use year instead\",De),fn.zone=n(\"moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/\",function(e,t){return null!=e?(\"string\"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),fn.isDSTShifted=n(\"isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information\",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?d(e._a):Ot(e._a);this._isDSTShifted=this.isValid()&&0<S(e._a,t.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted});var mn=A.prototype;function vn(e,t,n,r){var i=ct(),o=d().set(r,t);return i[n](o,e)}function yn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||\"\",null!=t)return vn(e,t,n,\"month\");var r,i=[];for(r=0;r<12;r++)i[r]=vn(e,r,n,\"month\");return i}function wn(e,t,n,r){t=(\"boolean\"==typeof e?u(t)&&(n=t,t=void 0):(t=e,e=!1,u(n=t)&&(n=t,t=void 0)),t||\"\");var i,o=ct(),a=e?o._week.dow:0;if(null!=n)return vn(t,(n+a)%7,r,\"day\");var s=[];for(i=0;i<7;i++)s[i]=vn(t,(i+a)%7,r,\"day\");return s}mn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return O(r)?r.call(t,n):r},mn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},mn.invalidDate=function(){return this._invalidDate},mn.ordinal=function(e){return this._ordinal.replace(\"%d\",e)},mn.preparse=gn,mn.postformat=gn,mn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return O(i)?i(e,t,n,r):i.replace(/%d/i,e)},mn.pastFuture=function(e,t){var n=this._relativeTime[0<e?\"future\":\"past\"];return O(n)?n(t):n.replace(/%s/i,t)},mn.set=function(e){var t,n;for(n in e)O(t=e[n])?this[n]=t:this[\"_\"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+\"|\"+/\\d{1,2}/.source)},mn.months=function(e,t){return e?a(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Ee).test(t)?\"format\":\"standalone\"][e.month()]:a(this._months)?this._months:this._months.standalone},mn.monthsShort=function(e,t){return e?a(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Ee.test(t)?\"format\":\"standalone\"][e.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},mn.monthsParse=function(e,t,n){var r,i,o;if(this._monthsParseExact)return function(e,t,n){var r,i,o,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)o=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(o,\"\").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(o,\"\").toLocaleLowerCase();return n?\"MMM\"===t?-1!==(i=ke.call(this._shortMonthsParse,a))?i:null:-1!==(i=ke.call(this._longMonthsParse,a))?i:null:\"MMM\"===t?-1!==(i=ke.call(this._shortMonthsParse,a))?i:-1!==(i=ke.call(this._longMonthsParse,a))?i:null:-1!==(i=ke.call(this._longMonthsParse,a))?i:-1!==(i=ke.call(this._shortMonthsParse,a))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp(\"^\"+this.months(i,\"\").replace(\".\",\"\")+\"$\",\"i\"),this._shortMonthsParse[r]=new RegExp(\"^\"+this.monthsShort(i,\"\").replace(\".\",\"\")+\"$\",\"i\")),n||this._monthsParse[r]||(o=\"^\"+this.months(i,\"\")+\"|^\"+this.monthsShort(i,\"\"),this._monthsParse[r]=new RegExp(o.replace(\".\",\"\"),\"i\")),n&&\"MMMM\"===t&&this._longMonthsParse[r].test(e))return r;if(n&&\"MMM\"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},mn.monthsRegex=function(e){return this._monthsParseExact?(h(this,\"_monthsRegex\")||Fe.call(this),e?this._monthsStrictRegex:this._monthsRegex):(h(this,\"_monthsRegex\")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},mn.monthsShortRegex=function(e){return this._monthsParseExact?(h(this,\"_monthsRegex\")||Fe.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,\"_monthsShortRegex\")||(this._monthsShortRegex=Ve),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},mn.week=function(e){return Ye(e,this._week.dow,this._week.doy).week},mn.firstDayOfYear=function(){return this._week.doy},mn.firstDayOfWeek=function(){return this._week.dow},mn.weekdays=function(e,t){var n=a(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?\"format\":\"standalone\"];return!0===e?qe(n,this._week.dow):e?n[e.day()]:n},mn.weekdaysMin=function(e){return!0===e?qe(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},mn.weekdaysShort=function(e){return!0===e?qe(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},mn.weekdaysParse=function(e,t,n){var r,i,o;if(this._weekdaysParseExact)return function(e,t,n){var r,i,o,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)o=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(o,\"\").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(o,\"\").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(o,\"\").toLocaleLowerCase();return n?\"dddd\"===t?-1!==(i=ke.call(this._weekdaysParse,a))?i:null:\"ddd\"===t?-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:\"dddd\"===t?-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:\"ddd\"===t?-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:null:-1!==(i=ke.call(this._minWeekdaysParse,a))?i:-1!==(i=ke.call(this._weekdaysParse,a))?i:-1!==(i=ke.call(this._shortWeekdaysParse,a))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp(\"^\"+this.weekdays(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._shortWeekdaysParse[r]=new RegExp(\"^\"+this.weekdaysShort(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\"),this._minWeekdaysParse[r]=new RegExp(\"^\"+this.weekdaysMin(i,\"\").replace(\".\",\"\\\\.?\")+\"$\",\"i\")),this._weekdaysParse[r]||(o=\"^\"+this.weekdays(i,\"\")+\"|^\"+this.weekdaysShort(i,\"\")+\"|^\"+this.weekdaysMin(i,\"\"),this._weekdaysParse[r]=new RegExp(o.replace(\".\",\"\"),\"i\")),n&&\"dddd\"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&\"ddd\"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&\"dd\"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},mn.weekdaysRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,\"_weekdaysRegex\")||(this._weekdaysRegex=Ge),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},mn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,\"_weekdaysShortRegex\")||(this._weekdaysShortRegex=Ke),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},mn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(h(this,\"_weekdaysRegex\")||Ze.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,\"_weekdaysMinRegex\")||(this._weekdaysMinRegex=Qe),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},mn.isPM=function(e){return\"p\"===(e+\"\").toLowerCase().charAt(0)},mn.meridiem=function(e,t,n){return 11<e?n?\"pm\":\"PM\":n?\"am\":\"AM\"},ut(\"en\",{dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===_(e%100/10)?\"th\":1==t?\"st\":2==t?\"nd\":3==t?\"rd\":\"th\")}}),p.lang=n(\"moment.lang is deprecated. Use moment.locale instead.\",ut),p.langData=n(\"moment.langData is deprecated. Use moment.localeData instead.\",ct);var bn=Math.abs;function $n(e,t,n,r){var i=qt(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function _n(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Cn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}var Dn=kn(\"ms\"),xn=kn(\"s\"),On=kn(\"m\"),Tn=kn(\"h\"),An=kn(\"d\"),En=kn(\"w\"),Pn=kn(\"M\"),Mn=kn(\"Q\"),Rn=kn(\"y\");function In(e){return function(){return this.isValid()?this._data[e]:NaN}}var Vn=In(\"milliseconds\"),Ln=In(\"seconds\"),Fn=In(\"minutes\"),jn=In(\"hours\"),Hn=In(\"days\"),Nn=In(\"months\"),Yn=In(\"years\");var Un=Math.round,qn={ss:44,s:45,m:45,h:22,d:26,M:11};var zn=Math.abs;function Bn(e){return(0<e)-(e<0)||+e}function Wn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=zn(this._milliseconds)/1e3,r=zn(this._days),i=zn(this._months);t=$((e=$(n/60))/60),n%=60,e%=60;var o=$(i/12),a=i%=12,s=r,u=t,l=e,c=n?n.toFixed(3).replace(/\\.?0+$/,\"\"):\"\",d=this.asSeconds();if(!d)return\"P0D\";var p=d<0?\"-\":\"\",h=Bn(this._months)!==Bn(d)?\"-\":\"\",f=Bn(this._days)!==Bn(d)?\"-\":\"\",g=Bn(this._milliseconds)!==Bn(d)?\"-\":\"\";return p+\"P\"+(o?h+o+\"Y\":\"\")+(a?h+a+\"M\":\"\")+(s?f+s+\"D\":\"\")+(u||l||c?\"T\":\"\")+(u?g+u+\"H\":\"\")+(l?g+l+\"M\":\"\")+(c?g+c+\"S\":\"\")}var Gn=Mt.prototype;return Gn.isValid=function(){return this._isValid},Gn.abs=function(){var e=this._data;return this._milliseconds=bn(this._milliseconds),this._days=bn(this._days),this._months=bn(this._months),e.milliseconds=bn(e.milliseconds),e.seconds=bn(e.seconds),e.minutes=bn(e.minutes),e.hours=bn(e.hours),e.months=bn(e.months),e.years=bn(e.years),this},Gn.add=function(e,t){return $n(this,e,t,1)},Gn.subtract=function(e,t){return $n(this,e,t,-1)},Gn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if(\"month\"===(e=M(e))||\"quarter\"===e||\"year\"===e)switch(t=this._days+r/864e5,n=this._months+Sn(t),e){case\"month\":return n;case\"quarter\":return n/3;case\"year\":return n/12}else switch(t=this._days+Math.round(Cn(this._months)),e){case\"week\":return t/7+r/6048e5;case\"day\":return t+r/864e5;case\"hour\":return 24*t+r/36e5;case\"minute\":return 1440*t+r/6e4;case\"second\":return 86400*t+r/1e3;case\"millisecond\":return Math.floor(864e5*t)+r;default:throw new Error(\"Unknown unit \"+e)}},Gn.asMilliseconds=Dn,Gn.asSeconds=xn,Gn.asMinutes=On,Gn.asHours=Tn,Gn.asDays=An,Gn.asWeeks=En,Gn.asMonths=Pn,Gn.asQuarters=Mn,Gn.asYears=Rn,Gn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*_(this._months/12):NaN},Gn._bubble=function(){var e,t,n,r,i,o=this._milliseconds,a=this._days,s=this._months,u=this._data;return 0<=o&&0<=a&&0<=s||o<=0&&a<=0&&s<=0||(o+=864e5*_n(Cn(s)+a),s=a=0),u.milliseconds=o%1e3,e=$(o/1e3),u.seconds=e%60,t=$(e/60),u.minutes=t%60,n=$(t/60),u.hours=n%24,s+=i=$(Sn(a+=$(n/24))),a-=_n(Cn(i)),r=$(s/12),s%=12,u.days=a,u.months=s,u.years=r,this},Gn.clone=function(){return qt(this)},Gn.get=function(e){return e=M(e),this.isValid()?this[e+\"s\"]():NaN},Gn.milliseconds=Vn,Gn.seconds=Ln,Gn.minutes=Fn,Gn.hours=jn,Gn.days=Hn,Gn.weeks=function(){return $(this.days()/7)},Gn.months=Nn,Gn.years=Yn,Gn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t=this.localeData(),n=function(e,t,n){var r=qt(e).abs(),i=Un(r.as(\"s\")),o=Un(r.as(\"m\")),a=Un(r.as(\"h\")),s=Un(r.as(\"d\")),u=Un(r.as(\"M\")),l=Un(r.as(\"y\")),c=i<=qn.ss&&[\"s\",i]||i<qn.s&&[\"ss\",i]||o<=1&&[\"m\"]||o<qn.m&&[\"mm\",o]||a<=1&&[\"h\"]||a<qn.h&&[\"hh\",a]||s<=1&&[\"d\"]||s<qn.d&&[\"dd\",s]||u<=1&&[\"M\"]||u<qn.M&&[\"MM\",u]||l<=1&&[\"y\"]||[\"yy\",l];return c[2]=t,c[3]=0<+e,c[4]=n,function(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}.apply(null,c)}(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)},Gn.toISOString=Wn,Gn.toString=Wn,Gn.toJSON=Wn,Gn.locale=Xt,Gn.localeData=en,Gn.toIsoString=n(\"toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)\",Wn),Gn.lang=Jt,Y(\"X\",0,0,\"unix\"),Y(\"x\",0,0,\"valueOf\"),ue(\"x\",re),ue(\"X\",/[+-]?\\d+(\\.\\d{1,3})?/),pe(\"X\",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),pe(\"x\",function(e,t,n){n._d=new Date(_(e))}),p.version=\"2.24.0\",e=Ot,p.fn=fn,p.min=function(){return Et(\"isBefore\",[].slice.call(arguments,0))},p.max=function(){return Et(\"isAfter\",[].slice.call(arguments,0))},p.now=function(){return Date.now?Date.now():+new Date},p.utc=d,p.unix=function(e){return Ot(1e3*e)},p.months=function(e,t){return yn(e,t,\"months\")},p.isDate=l,p.locale=ut,p.invalid=m,p.duration=qt,p.isMoment=b,p.weekdays=function(e,t,n){return wn(e,t,n,\"weekdays\")},p.parseZone=function(){return Ot.apply(null,arguments).parseZone()},p.localeData=ct,p.isDuration=Rt,p.monthsShort=function(e,t){return yn(e,t,\"monthsShort\")},p.weekdaysMin=function(e,t,n){return wn(e,t,n,\"weekdaysMin\")},p.defineLocale=lt,p.updateLocale=function(e,t){if(null!=t){var n,r,i=rt;null!=(r=st(e))&&(i=r._config),(n=new A(t=T(i,t))).parentLocale=it[e],it[e]=n,ut(e)}else null!=it[e]&&(null!=it[e].parentLocale?it[e]=it[e].parentLocale:null!=it[e]&&delete it[e]);return it[e]},p.locales=function(){return k(it)},p.weekdaysShort=function(e,t,n){return wn(e,t,n,\"weekdaysShort\")},p.normalizeUnits=M,p.relativeTimeRounding=function(e){return void 0===e?Un:\"function\"==typeof e&&(Un=e,!0)},p.relativeTimeThreshold=function(e,t){return void 0!==qn[e]&&(void 0===t?qn[e]:(qn[e]=t,\"s\"===e&&(qn.ss=t-1),!0))},p.calendarFormat=function(e,t){var n=e.diff(t,\"days\",!0);return n<-6?\"sameElse\":n<-1?\"lastWeek\":n<0?\"lastDay\":n<1?\"sameDay\":n<2?\"nextDay\":n<7?\"nextWeek\":\"sameElse\"},p.prototype=fn,p.HTML5_FMT={DATETIME_LOCAL:\"YYYY-MM-DDTHH:mm\",DATETIME_LOCAL_SECONDS:\"YYYY-MM-DDTHH:mm:ss\",DATETIME_LOCAL_MS:\"YYYY-MM-DDTHH:mm:ss.SSS\",DATE:\"YYYY-MM-DD\",TIME:\"HH:mm\",TIME_SECONDS:\"HH:mm:ss\",TIME_MS:\"HH:mm:ss.SSS\",WEEK:\"GGGG-[W]WW\",MONTH:\"YYYY-MM\"},p}),function(e,t){\"use strict\";\"undefined\"!=typeof module&&module.exports?(t(\"undefined\"==typeof angular?require(\"angular\"):angular,\"undefined\"==typeof moment?require(\"moment\"):moment),module.exports=\"ui.bootstrap.datetimepicker\"):\"function\"==typeof define&&define.amd?define([\"angular\",\"moment\"],t):t(e.angular,e.moment)}(this,function(d,p){\"use strict\";function e(){return function(e,t,n){var r,i;switch(e){case\"Date\":r=o(function(e){return a(e).toDate()});break;case\"moment\":r=o(a);break;case\"milliseconds\":r=o(function(e){return p.utc(e,t,p.locale(),n).valueOf()});break;default:r=o((i=e,function(e){return a(e).format(i)}))}return r;function o(t){return function(e){return d.isUndefined(e)||\"\"===e||null===e?null:t(e)}}function a(e){return p(e,t,p.locale(),n)}}}function t(c){return{require:\"ngModel\",restrict:\"A\",scope:{dateFormats:\"=\"},link:function(e,t,n,r){if(d.isDefined(e.dateFormats)&&!d.isString(e.dateFormats)&&!d.isArray(e.dateFormats))throw new Error(\"date-formats must be a single string or an array of strings i.e. date-formats=\\\"['YYYY-MM-DD']\\\" \");if(d.isDefined(n.modelType)&&(!d.isString(n.modelType)||0===n.modelType.length))throw new Error('model-type must be \"Date\", \"moment\", \"milliseconds\", or a moment format string');var i=n.dateTimeInput||p.defaultFormat,o=void 0===n.dateParseStrict||\"true\"===n.dateParseStrict,a=n.modelType||\"Date\",s=[n.dateTimeInput,a].concat(e.dateFormats).concat([p.ISO_8601]).filter(l),u=[a].concat(s).filter(l);function l(e,t,n){return-1===[\"Date\",\"moment\",\"milliseconds\",void 0].indexOf(e)&&n.indexOf(e)===t}r.$parsers.unshift(c(a,s,o)),r.$formatters.push(function(e){if(d.isUndefined(e)||\"\"===e||null===e)return null;{if(d.isDate(e))return p(e).format(i);if(d.isNumber(e))return p.utc(e).format(i)}return p(e,u,p.locale(),o).format(i)}),r.$validators.dateTimeInput=function(e,t){if(d.isUndefined(t)||\"\"===t||null===t)return!0;return p(t,s,p.locale(),o).isValid()},t.bind(\"blur\",function(){function e(){return!0}function t(e,t){return t(e)}r.$viewValue=r.$formatters.filter(e).reverse().reduce(t,r.$modelValue),r.$render()})}}}d.module(\"ui.dateTimeInput\",[]).service(\"dateTimeParserFactory\",e).directive(\"dateTimeInput\",t),e.$inject=[],t.$inject=[\"dateTimeParserFactory\"]}),function(e,t){\"use strict\";\"undefined\"!=typeof module&&module.exports?(t(\"undefined\"==typeof angular?require(\"angular\"):angular,\"undefined\"==typeof moment?require(\"moment\"):moment),module.exports=\"ui.bootstrap.datetimepicker\"):\"function\"==typeof define&&define.amd?define([\"angular\",\"moment\"],t):t(e.angular,e.moment)}(this,function(a,b){\"use strict\";function e(i,o){var e={bindToController:!1,controller:t,controllerAs:\"dateTimePickerController\",replace:!0,require:\"ngModel\",restrict:\"E\",scope:{beforeRender:\"&\",onSetTime:\"&\"},templateUrl:\"templates/datetimepicker.html\"};function t(l,e,n){var m=e.controller(\"ngModel\"),v=t();l.screenReader=v.screenReader,l.changeView=function(e,t,n){n&&(n.stopPropagation(),n.preventDefault());if(e&&t.utcDateValue>-1/0&&t.selectable&&c[e]){var r=c[e](t.utcDateValue),i=[];if(r.weeks)for(var o=0;o<r.weeks.length;o+=1)for(var a=r.weeks[o],s=0;s<a.dates.length;s+=1){var u=a.dates[s];i.push(u)}l.beforeRender({$view:r.currentView,$dates:r.dates||i,$leftDate:r.leftDate,$upDate:r.previousViewDate,$rightDate:r.rightDate}),l.data=r}},m.$render=function(){l.changeView(v.startView,new w({utcDateValue:function(e){var t=new Date;if(e){var n=r(e);if(!n.isValid())throw new Error(\"Invalid date: \"+e);t=n.toDate()}return t.getTime()-6e4*t.getTimezoneOffset()}(m.$viewValue)}))},v.configureOn&&l.$on(v.configureOn,function(){v=t(),l.screenReader=v.screenReader,m.$render()}),v.renderOn&&l.$on(v.renderOn,m.$render);var c={year:function(e){for(var t=b.utc(e).startOf(\"year\"),n=10*parseInt(t.year()/10,10),r=b.utc(d(e)).subtract(1,\"year\").startOf(\"year\"),i=\"YYYY\",o=y(m.$modelValue,i),a=b().format(i),s={currentView:\"year\",nextView:\"year\"===v.minView?\"setTime\":\"month\",previousViewDate:new w({utcDateValue:null,display:n+\"-\"+(9+n)}),leftDate:new w({utcDateValue:b.utc(r).subtract(9,\"year\").valueOf()}),rightDate:new w({utcDateValue:b.utc(r).add(11,\"year\").valueOf()}),dates:[]},u=0;u<12;u+=1){var l=b.utc(r).add(u,\"years\"),c={active:l.format(i)===o,current:l.format(i)===a,display:l.format(i),future:l.year()>9+n,past:l.year()<n,utcDateValue:l.valueOf()};s.dates.push(new w(c))}return s},month:function(e){for(var t=b.utc(e).startOf(\"year\"),n=d(e),r=\"YYYY-MMM\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"year\",currentView:\"month\",nextView:\"month\"===v.minView?\"setTime\":\"day\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"YYYY\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"year\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"year\").valueOf()}),dates:[]},s=0;s<12;s+=1){var u=b.utc(t).add(s,\"months\"),l={active:u.format(r)===i,current:u.format(r)===o,display:u.format(\"MMM\"),utcDateValue:u.valueOf()};a.dates.push(new w(l))}return a},day:function(e){for(var t=b.utc(e),n=b.utc(t).startOf(\"month\"),r=b.utc(t).startOf(\"year\"),i=b.utc(t).endOf(\"month\"),o=b.utc(n).subtract(Math.abs(n.weekday()),\"days\"),a=\"YYYY-MMM-DD\",s=y(m.$modelValue,a),u=b().format(a),l={previousView:\"month\",currentView:\"day\",nextView:\"day\"===v.minView?\"setTime\":\"hour\",previousViewDate:new w({utcDateValue:r.valueOf(),display:n.format(\"YYYY-MMM\")}),leftDate:new w({utcDateValue:b.utc(n).subtract(1,\"months\").valueOf()}),rightDate:new w({utcDateValue:b.utc(n).add(1,\"months\").valueOf()}),dayNames:[],weeks:[]},c=0;c<7;c+=1)l.dayNames.push(b.utc().weekday(c).format(\"dd\"));for(var d=0;d<6;d+=1){for(var p={dates:[]},h=0;h<7;h+=1){var f=b.utc(o).add(7*d+h,\"days\"),g={active:f.format(a)===s,current:f.format(a)===u,display:f.format(\"D\"),future:f.isAfter(i),past:f.isBefore(n),utcDateValue:f.valueOf()};p.dates.push(new w(g))}l.weeks.push(p)}return l},hour:function(e){for(var t=b.utc(e).startOf(\"day\"),n=b.utc(t).startOf(\"month\"),r=\"YYYY-MM-DD H\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"day\",currentView:\"hour\",nextView:\"hour\"===v.minView?\"setTime\":\"minute\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"ll\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"days\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"days\").valueOf()}),dates:[]},s=0;s<24;s+=1){var u=b.utc(t).add(s,\"hours\"),l={active:u.format(r)===i,current:u.format(r)===o,display:u.format(\"LT\"),utcDateValue:u.valueOf()};a.dates.push(new w(l))}return a},minute:function(e){for(var t=b.utc(e).startOf(\"hour\"),n=b.utc(t).startOf(\"day\"),r=\"YYYY-MM-DD H:mm\",i=y(m.$modelValue,r),o=b().format(r),a={previousView:\"hour\",currentView:\"minute\",nextView:\"setTime\",previousViewDate:new w({utcDateValue:n.valueOf(),display:t.format(\"lll\")}),leftDate:new w({utcDateValue:b.utc(t).subtract(1,\"hours\").valueOf()}),rightDate:new w({utcDateValue:b.utc(t).add(1,\"hours\").valueOf()}),dates:[]},s=60/v.minuteStep,u=0;u<s;u+=1){var l=b.utc(t).add(u*v.minuteStep,\"minute\"),c={active:l.format(r)===i,current:l.format(r)===o,display:l.format(\"LT\"),utcDateValue:l.valueOf()};a.dates.push(new w(c))}return a},setTime:function(e){var t=new Date(e),n=new Date(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds());switch(v.modelType){case\"Date\":break;case\"moment\":n=b([t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds()]);break;case\"milliseconds\":n=e;break;default:n=b([t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds(),t.getUTCMilliseconds()]).format(v.modelType)}var r=m.$modelValue;m.$setViewValue(n),v.dropdownSelector&&jQuery(v.dropdownSelector).dropdown(\"toggle\");return l.onSetTime({newDate:n,oldDate:r}),c[v.startView](e)}};function d(e){var t=10*parseInt(b.utc(e).year()/10,10);return b.utc(e).year(t).startOf(\"year\")}function y(e,t){return e?r(e).format(t):\"\"}function r(e){return b(e,a.isString(e)?v.parseFormat:void 0)}function t(){var e={};n.datetimepickerConfig&&(e=l.$parent.$eval(n.datetimepickerConfig));var t=a.extend({},i,e);return o.validate(t),t}}function w(){var e=new Date(arguments[0].utcDateValue),t=6e4*e.getTimezoneOffset();this.utcDateValue=e.getTime(),this.selectable=!0,this.localDateValue=function(){return this.utcDateValue+t};var n=[\"active\",\"current\",\"display\",\"future\",\"past\",\"selectable\",\"utcDateValue\"],r=arguments[0];Object.keys(r).filter(function(e){return 0<=n.indexOf(e)}).forEach(function(e){this[e]=r[e]},this)}return t.$inject=[\"$scope\",\"$element\",\"$attrs\"],e}function t(i){return{validate:function(e){var t=[\"configureOn\",\"dropdownSelector\",\"minuteStep\",\"minView\",\"modelType\",\"parseFormat\",\"renderOn\",\"startView\",\"screenReader\"],n=Object.keys(e).filter(function(e){return t.indexOf(e)<0});if(n.length)throw new Error(\"Invalid options: \"+n.join(\", \"));var r=[\"minute\",\"hour\",\"day\",\"month\",\"year\"];if(r.indexOf(e.startView)<0)throw new Error(\"invalid startView value: \"+e.startView);if(r.indexOf(e.minView)<0)throw new Error(\"invalid minView value: \"+e.minView);if(r.indexOf(e.minView)>r.indexOf(e.startView))throw new Error(\"startView must be greater than minView\");if(!a.isNumber(e.minuteStep))throw new Error(\"minuteStep must be numeric\");if(e.minuteStep<=0||60<=e.minuteStep)throw new Error(\"minuteStep must be greater than zero and less than 60\");if(null!==e.configureOn&&!a.isString(e.configureOn))throw new Error(\"configureOn must be a string\");if(null!==e.configureOn&&e.configureOn.length<1)throw new Error(\"configureOn must not be an empty string\");if(null!==e.renderOn&&!a.isString(e.renderOn))throw new Error(\"renderOn must be a string\");if(null!==e.renderOn&&e.renderOn.length<1)throw new Error(\"renderOn must not be an empty string\");if(null!==e.modelType&&!a.isString(e.modelType))throw new Error(\"modelType must be a string\");if(null!==e.modelType&&e.modelType.length<1)throw new Error(\"modelType must not be an empty string\");\"Date\"!==e.modelType&&\"moment\"!==e.modelType&&\"milliseconds\"!==e.modelType&&(e.parseFormat=e.modelType);if(null!==e.dropdownSelector&&!a.isString(e.dropdownSelector))throw new Error(\"dropdownSelector must be a string\");null===e.dropdownSelector||\"undefined\"!=typeof jQuery&&\"function\"==typeof jQuery().dropdown||(i.error(\"Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \\n\\nThe dropdownSelector configuration option is being removed because it will not function properly.\"),delete e.dropdownSelector)}}}a.module(\"ui.bootstrap.datetimepicker\",[]).service(\"dateTimePickerConfig\",function(){var e={bg:{previous:\"предишна\",next:\"следваща\"},ca:{previous:\"anterior\",next:\"següent\"},da:{previous:\"forrige\",next:\"næste\"},de:{previous:\"vorige\",next:\"weiter\"},\"en-au\":{previous:\"previous\",next:\"next\"},\"en-gb\":{previous:\"previous\",next:\"next\"},en:{previous:\"previous\",next:\"next\"},\"es-us\":{previous:\"atrás\",next:\"siguiente\"},es:{previous:\"atrás\",next:\"siguiente\"},fi:{previous:\"edellinen\",next:\"seuraava\"},fr:{previous:\"précédent\",next:\"suivant\"},hu:{previous:\"előző\",next:\"következő\"},it:{previous:\"precedente\",next:\"successivo\"},ja:{previous:\"前へ\",next:\"次へ\"},ml:{previous:\"മുൻപുള്ളത്\",next:\"അടുത്തത്\"},nl:{previous:\"vorige\",next:\"volgende\"},pl:{previous:\"poprzednia\",next:\"następna\"},\"pt-br\":{previous:\"anteriores\",next:\"próximos\"},pt:{previous:\"anterior\",next:\"próximo\"},ro:{previous:\"anterior\",next:\"următor\"},ru:{previous:\"предыдущая\",next:\"следующая\"},sk:{previous:\"predošlá\",next:\"ďalšia\"},sv:{previous:\"föregående\",next:\"nästa\"},tr:{previous:\"önceki\",next:\"sonraki\"},uk:{previous:\"назад\",next:\"далі\"},\"zh-cn\":{previous:\"上一页\",next:\"下一页\"},\"zh-tw\":{previous:\"上一頁\",next:\"下一頁\"}}[b.locale().toLowerCase()];return a.extend({},{configureOn:null,dropdownSelector:null,minuteStep:5,minView:\"minute\",modelType:\"Date\",parseFormat:\"YYYY-MM-DDTHH:mm:ss.SSSZZ\",renderOn:null,startView:\"day\"},{screenReader:e})}).service(\"dateTimePickerValidator\",t).directive(\"datetimepicker\",e),e.$inject=[\"dateTimePickerConfig\",\"dateTimePickerValidator\"],t.$inject=[\"$log\"]}),angular.module(\"rzTable\",[]),angular.module(\"rzTable\").directive(\"rzTable\",[\"resizeStorage\",\"$injector\",\"$parse\",function(s,i,o){function e(e){}function r(n,r,i){return function(e,t){!0!==i.busy&&void 0!==t&&t!==e&&(a(n),u(n,r,i))}}function a(e){_=!0,v.map(function(e){e.remove()}),v=[]}function u(e,t,n){if(!n.busy){h=$(e).find(\"th\"),l=n.mode,d=!angular.isDefined(n.saveTableSizes)||n.saveTableSizes,p=n.profile;var r=function(t,e){try{var n=e.rzMode?t.mode:\"BasicResizer\",r=i.get(n);return r}catch(e){return console.error(\"The resizer \"+t.mode+\" was not found\"),null}}(n,t);r&&(b=new r(e,h,w),d&&(S=s.loadTableSizes(e,n.mode,n.profile)),g=b.handles(h),f=b.ctrlColumns,b.setup(),function(o){o&&($(y).width(\"auto\"),f.each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr(\"id\"),i=o[r];$(t).css({width:i})}),b.onTableReady())}(S),g.each(function(e,t){!function(e,t,n){var r=$(\"<div>\",{class:e.options.handleClass||\"rz-handle\"});$(n).prepend(r),v.push(r);var i=b.handleMiddleware(r,n);!function(i,e,o,a){$(o).mousedown(function(e){_&&(b.onFirstDrag(a,o),b.onTableReady(),_=!1),i.options.onResizeStarted&&i.options.onResizeStarted(a);var t={};b.intervene&&(((t=b.intervene.selector(a)).column=t).orgWidth=$(t).width()),e.preventDefault(),$(o).addClass(i.options.handleClassActive||\"rz-handle-active\");var n=e.clientX,r=$(a).width();m=function(o,a,s,u,l){return function(e){var t=e.clientX,n=t-s,r=b.calculate(u,n);if(!(r<c(a)||b.restrict(r,n))){if(b.intervene){var i=b.intervene.calculator(l.orgWidth,n);if(i<c(l.column))return;if(b.intervene.restrict(i,n))return;$(l.column).width(i)}o.options.onResizeInProgress&&o.options.onResizeInProgress(a,r,n),$(a).width(r)}}}(i,a,n,r,t),$(window).mousemove(m),$(window).one(\"mouseup\",function(e,t,n){return function(){$(n).removeClass(e.options.handleClassActive||\"rz-handle-active\"),m&&$(window).unbind(\"mousemove\",m),e.options.onResizeEnded&&e.options.onResizeEnded(t),b.onEndDrag(),d&&(S||(S={}),$(h).each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr(\"id\");r&&(S[r]=b.saveAttr(t))}),s.saveTableSizes(y,l,p,S))}}(i,a,o))})}(e,0,r,i)}(n,0,t)}))}}function c(e){return parseInt($(e).css(\"min-width\"))||0}var l,d,p,h=null,f=null,g=null,m=null,v=[],y=null,w=null,b=null,_=!0,S=null;return e.$inject=[\"$scope\",\"$attrs\",\"$element\"],{restrict:\"A\",link:function(e,t,n){y=t,w=e.container?$(e.container):$(y).parent(),e.options=n.rzOptions&&e.options||{},$(y).addClass(e.options.tableClass||\"rz-table\"),u(y,n,e),function(e,t,n){if(t.rzModel){var r=o(t.rzModel);r.assign(n.$parent,{update:function(){a(),u(e,t,n)},reset:function(){(function(e){$(e).outerWidth(\"100%\"),$(e).find(\"th\").width(\"auto\")})(e),this.clearStorageActive(),this.update()},clearStorage:function(){s.clearAll()},clearStorageActive:function(){s.clearCurrent(e,l,p)}})}}(y,n,e),function(e,t,n){n.$watch(function(){return $(e).find(\"th\").length},r(e,t,n))}(y,n,e),function(e,t,n){n.$watch(\"profile\",r(e,t,n)),n.$watch(\"mode\",r(e,t,n)),n.$watch(\"busy\",r(e,t,n))}(y,n,e)},controller:e,scope:{mode:\"=rzMode\",profile:\"=?rzProfile\",busy:\"=?rzBusy\",saveTableSizes:\"=?rzSave\",options:\"=?rzOptions\",model:\"=rzModel\",container:\"@rzContainer\"}}}]),angular.module(\"rzTable\").directive(\"rzCol\",[function(){return{restrict:\"A\",priority:650,link:function(e,t,n){e.rzCol=e.$eval(n.rzCol)},require:\"^^rzTable\",scope:!0}}]),angular.module(\"rzTable\").service(\"resizeStorage\",[\"$window\",function(a){function s(e,t,n){return e.attr(\"id\")?r+\".\"+e.attr(\"id\")+\".\"+t+(n?\".\"+n:\"\"):void console.error(\"Table has no id\",e)}var r=\"ngColumnResize\";this.loadTableSizes=function(e,t,n){var r=s(e,t,n),i=a.localStorage.getItem(r);return JSON.parse(i)},this.saveTableSizes=function(e,t,n,r){var i=s(e,t,n);if(i){var o=JSON.stringify(r);a.localStorage.setItem(i,o)}},this.clearAll=function(){for(var e=[],t=0;t<a.localStorage.length;++t){var n=localStorage.key(t);n&&n.startsWith(r)&&e.push(n)}e.map(function(e){a.localStorage.removeItem(e)})},this.clearCurrent=function(e,t,n){var r=s(e,t,n);r&&a.localStorage.removeItem(r)}}]),angular.module(\"rzTable\").factory(\"ResizerModel\",[function(){function e(e,t,n){this.table=e,this.columns=t,this.container=n,this.handleColumns=this.handles(),this.ctrlColumns=this.ctrlColumns()}return e.prototype.setup=function(){$(this.container).css({overflowX:\"hidden\"})},e.prototype.onTableReady=function(){$(this.table).outerWidth(\"100%\")},e.prototype.getMinWidth=function(e){return parseInt($(e).css(\"min-width\"))||0},e.prototype.handles=function(){return this.columns},e.prototype.ctrlColumns=function(){return this.handleColumns},e.prototype.onFirstDrag=function(){$(this.ctrlColumns).each(function(e,t){$(t).width($(t).width())})},e.prototype.handleMiddleware=function(e,t){return t},e.prototype.restrict=function(e){return!1},e.prototype.calculate=function(e,t){return e+t},e.prototype.onEndDrag=function(){},e.prototype.saveAttr=function(e){return $(e).outerWidth()},e}]),angular.module(\"rzTable\").factory(\"BasicResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n),this.ctrlColumns=this.columns,this.intervene={selector:i,calculator:o,restrict:a}}function i(e){return $(e).next()}function o(e,t){return e-t}function a(e){return e<25}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflowX:\"hidden\"}),$(this.table).css({width:\"100%\"})},e.prototype.handles=function(){return $(this.columns).not(\":last\")},e.prototype.onFirstDrag=function(){this.onEndDrag()},e.prototype.onEndDrag=function(){var r=$(this.table).outerWidth(),i=[];$(this.columns).each(function(e,t){var n=$(t).outerWidth()/r*100+\"%\";i.push(function(){$(t).css({width:n})})}),i.map(function(e){e()})},e.prototype.saveAttr=function(e){return $(e)[0].style.width},e}]),angular.module(\"rzTable\").factory(\"FixedResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n),this.fixedColumn=$(e).find(\"th\").first(),this.bound=!1}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflowX:\"hidden\"}),$(this.table).css({width:\"100%\"}),$(this.columns).first().css({width:\"auto\"})},e.prototype.handles=function(){return $(this.columns).not(\":last\")},e.prototype.ctrlColumns=function(){return $(this.columns).not(\":first\")},e.prototype.onFirstDrag=function(){$(this.ctrlColumns).each(function(e,t){$(t).width($(t).width())})},e.prototype.handleMiddleware=function(e,t){return $(t).next()},e.prototype.restrict=function(e,t){return this.bound&&this.bound<t?this.bound=!1:!!(this.bound&&this.bound>t)||(this.fixedColumn.width()<=this.getMinWidth(this.fixedColumn)?(this.bound=t,$(this.fixedColumn).width(this.minWidth),!0):void 0)},e.prototype.onEndDrag=function(){this.bound=!1},e.prototype.calculate=function(e,t){return e-t},e}]),angular.module(\"rzTable\").factory(\"OverflowResizer\",[\"ResizerModel\",function(r){function e(e,t,n){r.call(this,e,t,n)}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflow:\"auto\"})},e.prototype.onTableReady=function(){$(this.table).width(1)},e}]),function(e,t){\"function\"==typeof define&&define.amd?define([\"angular\"],t):\"object\"==typeof module&&module.exports?module.exports=t(require(\"angular\")):e.angularClipboard=t(e.angular)}(this,function(i){return i.module(\"angular-clipboard\",[]).factory(\"clipboard\",[\"$document\",\"$window\",function(o,a){return{copyText:function(e,t){var n=a.pageXOffset||o[0].documentElement.scrollLeft,r=a.pageYOffset||o[0].documentElement.scrollTop,i=function(e,t){var n=o[0].createElement(\"textarea\");return n.style.position=\"absolute\",n.style.fontSize=\"12pt\",n.style.border=\"0\",n.style.padding=\"0\",n.style.margin=\"0\",n.style.left=\"-10000px\",n.style.top=(a.pageYOffset||o[0].documentElement.scrollTop)+\"px\",n.textContent=e,n}(e);o[0].body.appendChild(i),function(e){try{o[0].body.style.webkitUserSelect=\"initial\";var t=o[0].getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n),e.select(),e.setSelectionRange(0,999999);try{if(!o[0].execCommand(\"copy\"))throw\"failure copy\"}finally{t.removeAllRanges()}}finally{o[0].body.style.webkitUserSelect=\"\"}}(i),a.scrollTo(n,r),o[0].body.removeChild(i)},supported:\"queryCommandSupported\"in o[0]&&o[0].queryCommandSupported(\"copy\")}}]).directive(\"clipboard\",[\"clipboard\",function(r){return{restrict:\"A\",scope:{onCopied:\"&\",onError:\"&\",text:\"=\",supported:\"=?\"},link:function(t,n){t.supported=r.supported,n.on(\"click\",function(e){try{r.copyText(t.text,n[0]),i.isFunction(t.onCopied)&&t.$evalAsync(t.onCopied())}catch(e){i.isFunction(t.onError)&&t.$evalAsync(t.onError({err:e}))}})}}}])}),function(e,t){\"function\"==typeof define&&define.amd?define(\"sifter\",t):\"object\"==typeof exports?module.exports=t():e.Sifter=t()}(this,function(){var e=function(e,t){this.items=e,this.settings=t||{diacritics:!0}};e.prototype.tokenize=function(e){if(!(e=s(String(e||\"\").toLowerCase()))||!e.length)return[];var t,n,r,i,o=[],a=e.split(/ +/);for(t=0,n=a.length;t<n;t++){if(r=u(a[t]),this.settings.diacritics)for(i in l)l.hasOwnProperty(i)&&(r=r.replace(new RegExp(i,\"g\"),l[i]));o.push({string:a[t],regex:new RegExp(r,\"i\")})}return o},e.prototype.iterator=function(e,t){(a(e)?Array.prototype.forEach||function(e){for(var t=0,n=this.length;t<n;t++)e(this[t],t,this)}:function(e){for(var t in this)this.hasOwnProperty(t)&&e(this[t],t,this)}).apply(e,[t])},e.prototype.getScoreFunction=function(e,t){var i,o,a,s;e=this.prepareSearch(e,t),o=e.tokens,i=e.options.fields,a=o.length,s=e.options.nesting;var u,l=function(e,t){var n,r;return e?-1===(r=(e=String(e||\"\")).search(t.regex))?0:(n=t.string.length/e.length,0===r&&(n+=.5),n):0},c=(u=i.length)?1===u?function(e,t){return l(f(t,i[0],s),e)}:function(e,t){for(var n=0,r=0;n<u;n++)r+=l(f(t,i[n],s),e);return r/u}:function(){return 0};return a?1===a?function(e){return c(o[0],e)}:\"and\"===e.options.conjunction?function(e){for(var t,n=0,r=0;n<a;n++){if((t=c(o[n],e))<=0)return 0;r+=t}return r/a}:function(e){for(var t=0,n=0;t<a;t++)n+=c(o[t],e);return n/a}:function(){return 0}},e.prototype.getSortFunction=function(e,n){var t,r,i,o,a,s,u,l,c,d,p;if(p=!(e=(i=this).prepareSearch(e,n)).query&&n.sort_empty||n.sort,c=function(e,t){return\"$score\"===e?t.score:f(i.items[t.id],e,n.nesting)},a=[],p)for(t=0,r=p.length;t<r;t++)(e.query||\"$score\"!==p[t].field)&&a.push(p[t]);if(e.query){for(d=!0,t=0,r=a.length;t<r;t++)if(\"$score\"===a[t].field){d=!1;break}d&&a.unshift({field:\"$score\",direction:\"desc\"})}else for(t=0,r=a.length;t<r;t++)if(\"$score\"===a[t].field){a.splice(t,1);break}for(l=[],t=0,r=a.length;t<r;t++)l.push(\"desc\"===a[t].direction?-1:1);return(s=a.length)?1===s?(o=a[0].field,u=l[0],function(e,t){return u*h(c(o,e),c(o,t))}):function(e,t){var n,r,i;for(n=0;n<s;n++)if(i=a[n].field,r=l[n]*h(c(i,e),c(i,t)))return r;return 0}:null},e.prototype.prepareSearch=function(e,t){if(\"object\"==typeof e)return e;var n=(t=o({},t)).fields,r=t.sort,i=t.sort_empty;return n&&!a(n)&&(t.fields=[n]),r&&!a(r)&&(t.sort=[r]),i&&!a(i)&&(t.sort_empty=[i]),{options:t,query:String(e||\"\").toLowerCase(),tokens:this.tokenize(e),total:0,items:[]}},e.prototype.search=function(e,n){var r,i,t,o;return i=this.prepareSearch(e,n),n=i.options,e=i.query,o=n.score||this.getScoreFunction(i),e.length?this.iterator(this.items,function(e,t){r=o(e),(!1===n.filter||0<r)&&i.items.push({score:r,id:t})}):this.iterator(this.items,function(e,t){i.items.push({score:1,id:t})}),(t=this.getSortFunction(i,n))&&i.items.sort(t),i.total=i.items.length,\"number\"==typeof n.limit&&(i.items=i.items.slice(0,n.limit)),i};var h=function(e,t){return\"number\"==typeof e&&\"number\"==typeof t?t<e?1:e<t?-1:0:(e=n(String(e||\"\")),(t=n(String(t||\"\")))<e?1:e<t?-1:0)},o=function(e,t){var n,r,i,o;for(n=1,r=arguments.length;n<r;n++)if(o=arguments[n])for(i in o)o.hasOwnProperty(i)&&(e[i]=o[i]);return e},f=function(e,t,n){if(e&&t){if(!n)return e[t];for(var r=t.split(\".\");r.length&&(e=e[r.shift()]););return e}},s=function(e){return(e+\"\").replace(/^\\s+|\\s+$|/g,\"\")},u=function(e){return(e+\"\").replace(/([.?*+^$[\\]\\\\(){}|-])/g,\"\\\\$1\")},a=Array.isArray||\"undefined\"!=typeof $&&$.isArray||function(e){return\"[object Array]\"===Object.prototype.toString.call(e)},l={a:\"[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]\",b:\"[b␢βΒB฿𐌁ᛒ]\",c:\"[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄＣｃ]\",d:\"[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅＤｄð]\",e:\"[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇＥｅɘǝƏƐε]\",f:\"[fƑƒḞḟ]\",g:\"[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]\",h:\"[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]\",i:\"[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪＩｉ]\",j:\"[jȷĴĵɈɉʝɟʲ]\",k:\"[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]\",l:\"[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟＬｌ]\",n:\"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴＮｎŊŋ]\",o:\"[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]\",p:\"[pṔṕṖṗⱣᵽƤƥᵱ]\",q:\"[qꝖꝗʠɊɋꝘꝙq̃]\",r:\"[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]\",s:\"[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]\",t:\"[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]\",u:\"[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]\",v:\"[vṼṽṾṿƲʋꝞꝟⱱʋ]\",w:\"[wẂẃẀẁŴŵẄẅẆẇẈẉ]\",x:\"[xẌẍẊẋχ]\",y:\"[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]\",z:\"[zŹźẐẑŽžŻżẒẓẔẕƵƶ]\"},n=function(){var e,t,n,r,i=\"\",o={};for(n in l)if(l.hasOwnProperty(n))for(i+=r=l[n].substring(2,l[n].length-1),e=0,t=r.length;e<t;e++)o[r.charAt(e)]=n;var a=new RegExp(\"[\"+i+\"]\",\"g\");return function(e){return e.replace(a,function(e){return o[e]}).toLowerCase()}}();return e}),function(e,t){\"function\"==typeof define&&define.amd?define(\"microplugin\",t):\"object\"==typeof exports?module.exports=t():e.MicroPlugin=t()}(this,function(){var e={mixin:function(r){r.plugins={},r.prototype.initializePlugins=function(e){var t,n,r,i=[];if(this.plugins={names:[],settings:{},requested:{},loaded:{}},o.isArray(e))for(t=0,n=e.length;t<n;t++)\"string\"==typeof e[t]?i.push(e[t]):(this.plugins.settings[e[t].name]=e[t].options,i.push(e[t].name));else if(e)for(r in e)e.hasOwnProperty(r)&&(this.plugins.settings[r]=e[r],i.push(r));for(;i.length;)this.require(i.shift())},r.prototype.loadPlugin=function(e){var t=this.plugins,n=r.plugins[e];if(!r.plugins.hasOwnProperty(e))throw new Error('Unable to find \"'+e+'\" plugin');t.requested[e]=!0,t.loaded[e]=n.fn.apply(this,[this.plugins.settings[e]||{}]),t.names.push(e)},r.prototype.require=function(e){var t=this.plugins;if(!this.plugins.loaded.hasOwnProperty(e)){if(t.requested[e])throw new Error('Plugin has circular dependency (\"'+e+'\")');this.loadPlugin(e)}return t.loaded[e]},r.define=function(e,t){r.plugins[e]={name:e,fn:t}}}},o={isArray:Array.isArray||function(e){return\"[object Array]\"===Object.prototype.toString.call(e)}};return e}),function(e,t){\"function\"==typeof define&&define.amd?define(\"selectize\",[\"jquery\",\"sifter\",\"microplugin\"],t):\"object\"==typeof exports?module.exports=t(require(\"jquery\"),require(\"sifter\"),require(\"microplugin\")):e.Selectize=t(e.jQuery,e.Sifter,e.MicroPlugin)}(this,function(C,u,e){\"use strict\";var k=function(e,t){if(\"string\"!=typeof t||t.length){var u=\"string\"==typeof t?new RegExp(t,\"i\"):t,l=function(e){var t=0;if(3===e.nodeType){var n=e.data.search(u);if(0<=n&&0<e.data.length){var r=e.data.match(u),i=document.createElement(\"span\");i.className=\"highlight\";var o=e.splitText(n),a=(o.splitText(r[0].length),o.cloneNode(!0));i.appendChild(a),o.parentNode.replaceChild(i,o),t=1}}else if(1===e.nodeType&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&(\"highlight\"!==e.className||\"SPAN\"!==e.tagName))for(var s=0;s<e.childNodes.length;++s)s+=l(e.childNodes[s]);return t};return e.each(function(){l(this)})}};C.fn.removeHighlight=function(){return this.find(\"span.highlight\").each(function(){this.parentNode.firstChild.nodeName;var e=this.parentNode;e.replaceChild(this.firstChild,this),e.normalize()}).end()};var r=function(){};r.prototype={on:function(e,t){this._events=this._events||{},this._events[e]=this._events[e]||[],this._events[e].push(t)},off:function(e,t){var n=arguments.length;return 0===n?delete this._events:1===n?delete this._events[e]:(this._events=this._events||{},void(e in this._events!=!1&&this._events[e].splice(this._events[e].indexOf(t),1)))},trigger:function(e){if(this._events=this._events||{},e in this._events!=!1)for(var t=0;t<this._events[e].length;t++)this._events[e][t].apply(this,Array.prototype.slice.call(arguments,1))}},r.mixin=function(e){for(var t=[\"on\",\"off\",\"trigger\"],n=0;n<t.length;n++)e.prototype[t[n]]=r.prototype[t[n]]};var v=/Mac/.test(navigator.userAgent),y=v?91:17,w=v?18:17,b=!/android/i.test(window.navigator.userAgent)&&!!document.createElement(\"input\").validity,l=function(e){return void 0!==e},D=function(e){return null==e?null:\"boolean\"==typeof e?e?\"1\":\"0\":e+\"\"},s=function(e){return(e+\"\").replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")},t={before:function(e,t,n){var r=e[t];e[t]=function(){return n.apply(e,arguments),r.apply(e,arguments)}},after:function(t,e,n){var r=t[e];t[e]=function(){var e=r.apply(t,arguments);return n.apply(t,arguments),e}}},n=function(t,n,e){var r,i=t.trigger,o={};for(r in t.trigger=function(){var e=arguments[0];if(-1===n.indexOf(e))return i.apply(t,arguments);o[e]=arguments},e.apply(t,[]),t.trigger=i,o)o.hasOwnProperty(r)&&i.apply(t,o[r])},p=function(e){var t={};if(\"selectionStart\"in e)t.start=e.selectionStart,t.length=e.selectionEnd-t.start;else if(document.selection){e.focus();var n=document.selection.createRange(),r=document.selection.createRange().text.length;n.moveStart(\"character\",-e.value.length),t.start=n.text.length-r,t.length=r}return t},$=function(c){var d=null,e=function(e,t){var n,r,i,o,a,s,u,l;t=t||{},(e=e||window.event||{}).metaKey||e.altKey||(t.force||!1!==c.data(\"grow\"))&&(n=c.val(),e.type&&\"keydown\"===e.type.toLowerCase()&&(i=48<=(r=e.keyCode)&&r<=57||65<=r&&r<=90||96<=r&&r<=111||186<=r&&r<=222||32===r,46===r||8===r?(l=p(c[0])).length?n=n.substring(0,l.start)+n.substring(l.start+l.length):8===r&&l.start?n=n.substring(0,l.start-1)+n.substring(l.start+1):46===r&&void 0!==l.start&&(n=n.substring(0,l.start)+n.substring(l.start+1)):i&&(s=e.shiftKey,u=String.fromCharCode(e.keyCode),n+=u=s?u.toUpperCase():u.toLowerCase())),o=c.attr(\"placeholder\"),!n&&o&&(n=o),(a=function(e,t){return e?(_.$testInput||(_.$testInput=C(\"<span />\").css({position:\"absolute\",top:-99999,left:-99999,width:\"auto\",padding:0,whiteSpace:\"pre\"}).appendTo(\"body\")),_.$testInput.text(e),function(e,t,n){var r,i,o={};if(n)for(r=0,i=n.length;r<i;r++)o[n[r]]=e.css(n[r]);else o=e.css();t.css(o)}(t,_.$testInput,[\"letterSpacing\",\"fontSize\",\"fontFamily\",\"fontWeight\",\"textTransform\"]),_.$testInput.width()):0}(n,c)+4)!==d&&(d=a,c.width(a),c.triggerHandler(\"resize\")))};c.on(\"keydown keyup update blur\",e),e()},_=function(e,t){var n,r,i,o,a=this;(o=e[0]).selectize=a;var s=window.getComputedStyle&&window.getComputedStyle(o,null);if(i=(i=s?s.getPropertyValue(\"direction\"):o.currentStyle&&o.currentStyle.direction)||e.parents(\"[dir]:first\").attr(\"dir\")||\"\",C.extend(a,{order:0,settings:t,$input:e,tabIndex:e.attr(\"tabindex\")||\"\",tagType:\"select\"===o.tagName.toLowerCase()?1:2,rtl:/rtl/i.test(i),eventNS:\".selectize\"+ ++_.count,highlightedValue:null,isBlurring:!1,isOpen:!1,isDisabled:!1,isRequired:e.is(\"[required]\"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:\"\",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===t.loadThrottle?a.onSearchChange:function(n,r){var i;return function(){var e=this,t=arguments;window.clearTimeout(i),i=window.setTimeout(function(){n.apply(e,t)},r)}}(a.onSearchChange,t.loadThrottle)}),a.sifter=new u(this.options,{diacritics:t.diacritics}),a.settings.options){for(n=0,r=a.settings.options.length;n<r;n++)a.registerOption(a.settings.options[n]);delete a.settings.options}if(a.settings.optgroups){for(n=0,r=a.settings.optgroups.length;n<r;n++)a.registerOptionGroup(a.settings.optgroups[n]);delete a.settings.optgroups}a.settings.mode=a.settings.mode||(1===a.settings.maxItems?\"single\":\"multi\"),\"boolean\"!=typeof a.settings.hideSelected&&(a.settings.hideSelected=\"multi\"===a.settings.mode),a.initializePlugins(a.settings.plugins),a.setupCallbacks(),a.setupTemplates(),a.setup()};return r.mixin(_),void 0!==e?e.mixin(_):function(e,t){t||(t={});console.error(\"Selectize: \"+e),t.explanation&&(console.group&&console.group(),console.error(t.explanation),console.group&&console.groupEnd())}(\"Dependency MicroPlugin is missing\",{explanation:'Make sure you either: (1) are using the \"standalone\" version of Selectize, or (2) require MicroPlugin before you load Selectize.'}),C.extend(_.prototype,{setup:function(){var e,t,n,r,i,o,a,s,u,l,c=this,d=c.settings,p=c.eventNS,h=C(window),f=C(document),g=c.$input;if(a=c.settings.mode,s=g.attr(\"class\")||\"\",e=C(\"<div>\").addClass(d.wrapperClass).addClass(s).addClass(a),t=C(\"<div>\").addClass(d.inputClass).addClass(\"items\").appendTo(e),n=C('<input type=\"text\" autocomplete=\"off\" />').appendTo(t).attr(\"tabindex\",g.is(\":disabled\")?\"-1\":c.tabIndex),o=C(d.dropdownParent||e),r=C(\"<div>\").addClass(d.dropdownClass).addClass(a).hide().appendTo(o),i=C(\"<div>\").addClass(d.dropdownContentClass).appendTo(r),(l=g.attr(\"id\"))&&(n.attr(\"id\",l+\"-selectized\"),C(\"label[for='\"+l+\"']\").attr(\"for\",l+\"-selectized\")),c.settings.copyClassesToDropdown&&r.addClass(s),e.css({width:g[0].style.width}),c.plugins.names.length&&(u=\"plugin-\"+c.plugins.names.join(\" plugin-\"),e.addClass(u),r.addClass(u)),(null===d.maxItems||1<d.maxItems)&&1===c.tagType&&g.attr(\"multiple\",\"multiple\"),c.settings.placeholder&&n.attr(\"placeholder\",d.placeholder),!c.settings.splitOn&&c.settings.delimiter){var m=c.settings.delimiter.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\");c.settings.splitOn=new RegExp(\"\\\\s*\"+m+\"+\\\\s*\")}g.attr(\"autocorrect\")&&n.attr(\"autocorrect\",g.attr(\"autocorrect\")),g.attr(\"autocapitalize\")&&n.attr(\"autocapitalize\",g.attr(\"autocapitalize\")),n[0].type=g[0].type,c.$wrapper=e,c.$control=t,c.$control_input=n,c.$dropdown=r,c.$dropdown_content=i,r.on(\"mouseenter mousedown click\",\"[data-disabled]>[data-selectable]\",function(e){e.stopImmediatePropagation()}),r.on(\"mouseenter\",\"[data-selectable]\",function(){return c.onOptionHover.apply(c,arguments)}),r.on(\"mousedown click\",\"[data-selectable]\",function(){return c.onOptionSelect.apply(c,arguments)}),function(n,e,t,r){n.on(e,t,function(e){for(var t=e.target;t&&t.parentNode!==n[0];)t=t.parentNode;return e.currentTarget=t,r.apply(this,[e])})}(t,\"mousedown\",\"*:not(input)\",function(){return c.onItemSelect.apply(c,arguments)}),$(n),t.on({mousedown:function(){return c.onMouseDown.apply(c,arguments)},click:function(){return c.onClick.apply(c,arguments)}}),n.on({mousedown:function(e){e.stopPropagation()},keydown:function(){return c.onKeyDown.apply(c,arguments)},keyup:function(){return c.onKeyUp.apply(c,arguments)},keypress:function(){return c.onKeyPress.apply(c,arguments)},resize:function(){c.positionDropdown.apply(c,[])},blur:function(){return c.onBlur.apply(c,arguments)},focus:function(){return c.ignoreBlur=!1,c.onFocus.apply(c,arguments)},paste:function(){return c.onPaste.apply(c,arguments)}}),f.on(\"keydown\"+p,function(e){c.isCmdDown=e[v?\"metaKey\":\"ctrlKey\"],c.isCtrlDown=e[v?\"altKey\":\"ctrlKey\"],c.isShiftDown=e.shiftKey}),f.on(\"keyup\"+p,function(e){e.keyCode===w&&(c.isCtrlDown=!1),16===e.keyCode&&(c.isShiftDown=!1),e.keyCode===y&&(c.isCmdDown=!1)}),f.on(\"mousedown\"+p,function(e){if(c.isFocused){if(e.target===c.$dropdown[0]||e.target.parentNode===c.$dropdown[0])return!1;c.$control.has(e.target).length||e.target===c.$control[0]||c.blur(e.target)}}),h.on([\"scroll\"+p,\"resize\"+p].join(\" \"),function(){c.isOpen&&c.positionDropdown.apply(c,arguments)}),h.on(\"mousemove\"+p,function(){c.ignoreHover=!1}),this.revertSettings={$children:g.children().detach(),tabindex:g.attr(\"tabindex\")},g.attr(\"tabindex\",-1).hide().after(c.$wrapper),C.isArray(d.items)&&(c.setValue(d.items),delete d.items),b&&g.on(\"invalid\"+p,function(e){e.preventDefault(),c.isInvalid=!0,c.refreshState()}),c.updateOriginalInput(),c.refreshItems(),c.refreshState(),c.updatePlaceholder(),c.isSetup=!0,g.is(\":disabled\")&&c.disable(),c.on(\"change\",this.onChange),g.data(\"selectize\",c),g.addClass(\"selectized\"),c.trigger(\"initialize\"),!0===d.preload&&c.onSearchChange(\"\")},setupTemplates:function(){var n=this.settings.labelField,r=this.settings.optgroupLabelField,e={optgroup:function(e){return'<div class=\"optgroup\">'+e.html+\"</div>\"},optgroup_header:function(e,t){return'<div class=\"optgroup-header\">'+t(e[r])+\"</div>\"},option:function(e,t){return'<div class=\"option\">'+t(e[n])+\"</div>\"},item:function(e,t){return'<div class=\"item\">'+t(e[n])+\"</div>\"},option_create:function(e,t){return'<div class=\"create\">Add <strong>'+t(e.input)+\"</strong>&hellip;</div>\"}};this.settings.render=C.extend({},e,this.settings.render)},setupCallbacks:function(){var e,t,n={initialize:\"onInitialize\",change:\"onChange\",item_add:\"onItemAdd\",item_remove:\"onItemRemove\",clear:\"onClear\",option_add:\"onOptionAdd\",option_remove:\"onOptionRemove\",option_clear:\"onOptionClear\",optgroup_add:\"onOptionGroupAdd\",optgroup_remove:\"onOptionGroupRemove\",optgroup_clear:\"onOptionGroupClear\",dropdown_open:\"onDropdownOpen\",dropdown_close:\"onDropdownClose\",type:\"onType\",load:\"onLoad\",focus:\"onFocus\",blur:\"onBlur\"};for(e in n)n.hasOwnProperty(e)&&(t=this.settings[n[e]])&&this.on(e,t)},onClick:function(e){this.isFocused&&this.isOpen||(this.focus(),e.preventDefault())},onMouseDown:function(e){var t=this,n=e.isDefaultPrevented();C(e.target);if(t.isFocused){if(e.target!==t.$control_input[0])return\"single\"===t.settings.mode?t.isOpen?t.close():t.open():n||t.setActiveItem(null),!1}else n||window.setTimeout(function(){t.focus()},0)},onChange:function(){this.$input.trigger(\"change\")},onPaste:function(e){var i=this;i.isFull()||i.isInputHidden||i.isLocked?e.preventDefault():i.settings.splitOn&&setTimeout(function(){var e=i.$control_input.val();if(e.match(i.settings.splitOn))for(var t=C.trim(e).split(i.settings.splitOn),n=0,r=t.length;n<r;n++)i.createItem(t[n])},0)},onKeyPress:function(e){if(this.isLocked)return e&&e.preventDefault();var t=String.fromCharCode(e.keyCode||e.which);return this.settings.create&&\"multi\"===this.settings.mode&&t===this.settings.delimiter?(this.createItem(),e.preventDefault(),!1):void 0},onKeyDown:function(e){e.target,this.$control_input[0];var t=this;if(t.isLocked)9!==e.keyCode&&e.preventDefault();else{switch(e.keyCode){case 65:if(t.isCmdDown)return void t.selectAll();break;case 27:return void(t.isOpen&&(e.preventDefault(),e.stopPropagation(),t.close()));case 78:if(!e.ctrlKey||e.altKey)break;case 40:if(!t.isOpen&&t.hasOptions)t.open();else if(t.$activeOption){t.ignoreHover=!0;var n=t.getAdjacentOption(t.$activeOption,1);n.length&&t.setActiveOption(n,!0,!0)}return void e.preventDefault();case 80:if(!e.ctrlKey||e.altKey)break;case 38:if(t.$activeOption){t.ignoreHover=!0;var r=t.getAdjacentOption(t.$activeOption,-1);r.length&&t.setActiveOption(r,!0,!0)}return void e.preventDefault();case 13:return void(t.isOpen&&t.$activeOption&&(t.onOptionSelect({currentTarget:t.$activeOption}),e.preventDefault()));case 37:return void t.advanceSelection(-1,e);case 39:return void t.advanceSelection(1,e);case 9:return t.settings.selectOnTab&&t.isOpen&&t.$activeOption&&(t.onOptionSelect({currentTarget:t.$activeOption}),t.isFull()||e.preventDefault()),void(t.settings.create&&t.createItem()&&e.preventDefault());case 8:case 46:return void t.deleteSelection(e)}!t.isFull()&&!t.isInputHidden||(v?e.metaKey:e.ctrlKey)||e.preventDefault()}},onKeyUp:function(e){if(this.isLocked)return e&&e.preventDefault();var t=this.$control_input.val()||\"\";this.lastValue!==t&&(this.lastValue=t,this.onSearchChange(t),this.refreshOptions(),this.trigger(\"type\",t))},onSearchChange:function(t){var n=this,r=n.settings.load;r&&(n.loadedSearches.hasOwnProperty(t)||(n.loadedSearches[t]=!0,n.load(function(e){r.apply(n,[t,e])})))},onFocus:function(e){var t=this,n=t.isFocused;if(t.isDisabled)return t.blur(),e&&e.preventDefault(),!1;t.ignoreFocus||(t.isFocused=!0,\"focus\"===t.settings.preload&&t.onSearchChange(\"\"),n||t.trigger(\"focus\"),t.$activeItems.length||(t.showInput(),t.setActiveItem(null),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())},onBlur:function(e,t){var n=this;if(n.isFocused&&(n.isFocused=!1,!n.ignoreFocus)){if(!n.ignoreBlur&&document.activeElement===n.$dropdown_content[0])return n.ignoreBlur=!0,void n.onFocus(e);var r=function(){n.close(),n.setTextboxValue(\"\"),n.setActiveItem(null),n.setActiveOption(null),n.setCaret(n.items.length),n.refreshState(),t&&t.focus&&t.focus(),n.isBlurring=!1,n.ignoreFocus=!1,n.trigger(\"blur\")};n.isBlurring=!0,n.ignoreFocus=!0,n.settings.create&&n.settings.createOnBlur?n.createItem(null,!1,r):r()}},onOptionHover:function(e){this.ignoreHover||this.setActiveOption(e.currentTarget,!1)},onOptionSelect:function(e){var t,n,r=this;e.preventDefault&&(e.preventDefault(),e.stopPropagation()),(n=C(e.currentTarget)).hasClass(\"create\")?r.createItem(null,function(){r.settings.closeAfterSelect&&r.close()}):void 0!==(t=n.attr(\"data-value\"))&&(r.lastQuery=null,r.setTextboxValue(\"\"),r.addItem(t),r.settings.closeAfterSelect?r.close():!r.settings.hideSelected&&e.type&&/mouse/.test(e.type)&&r.setActiveOption(r.getOption(t)))},onItemSelect:function(e){this.isLocked||\"multi\"===this.settings.mode&&(e.preventDefault(),this.setActiveItem(e.currentTarget,e))},load:function(e){var t=this,n=t.$wrapper.addClass(t.settings.loadingClass);t.loading++,e.apply(t,[function(e){t.loading=Math.max(t.loading-1,0),e&&e.length&&(t.addOption(e),t.refreshOptions(t.isFocused&&!t.isInputHidden)),t.loading||n.removeClass(t.settings.loadingClass),t.trigger(\"load\",e)}])},setTextboxValue:function(e){var t=this.$control_input;t.val()!==e&&(t.val(e).triggerHandler(\"update\"),this.lastValue=e)},getValue:function(){return 1===this.tagType&&this.$input.attr(\"multiple\")?this.items:this.items.join(this.settings.delimiter)},setValue:function(e,t){n(this,t?[]:[\"change\"],function(){this.clear(t),this.addItems(e,t)})},setActiveItem:function(e,t){var n,r,i,o,a,s,u,l,c=this;if(\"single\"!==c.settings.mode){if(!(e=C(e)).length)return C(c.$activeItems).removeClass(\"active\"),c.$activeItems=[],void(c.isFocused&&c.showInput());if(\"mousedown\"===(n=t&&t.type.toLowerCase())&&c.isShiftDown&&c.$activeItems.length){for(l=c.$control.children(\".active:last\"),o=Array.prototype.indexOf.apply(c.$control[0].childNodes,[l[0]]),(a=Array.prototype.indexOf.apply(c.$control[0].childNodes,[e[0]]))<o&&(u=o,o=a,a=u),r=o;r<=a;r++)s=c.$control[0].childNodes[r],-1===c.$activeItems.indexOf(s)&&(C(s).addClass(\"active\"),c.$activeItems.push(s));t.preventDefault()}else\"mousedown\"===n&&c.isCtrlDown||\"keydown\"===n&&this.isShiftDown?e.hasClass(\"active\")?(i=c.$activeItems.indexOf(e[0]),c.$activeItems.splice(i,1),e.removeClass(\"active\")):c.$activeItems.push(e.addClass(\"active\")[0]):(C(c.$activeItems).removeClass(\"active\"),c.$activeItems=[e.addClass(\"active\")[0]]);c.hideInput(),this.isFocused||c.focus()}},setActiveOption:function(e,t,n){var r,i,o,a,s,u=this;u.$activeOption&&u.$activeOption.removeClass(\"active\"),u.$activeOption=null,(e=C(e)).length&&(u.$activeOption=e.addClass(\"active\"),!t&&l(t)||(r=u.$dropdown_content.height(),i=u.$activeOption.outerHeight(!0),t=u.$dropdown_content.scrollTop()||0,s=(a=o=u.$activeOption.offset().top-u.$dropdown_content.offset().top+t)-r+i,r+t<o+i?u.$dropdown_content.stop().animate({scrollTop:s},n?u.settings.scrollDuration:0):o<t&&u.$dropdown_content.stop().animate({scrollTop:a},n?u.settings.scrollDuration:0)))},selectAll:function(){\"single\"!==this.settings.mode&&(this.$activeItems=Array.prototype.slice.apply(this.$control.children(\":not(input)\").addClass(\"active\")),this.$activeItems.length&&(this.hideInput(),this.close()),this.focus())},hideInput:function(){this.setTextboxValue(\"\"),this.$control_input.css({opacity:0,position:\"absolute\",left:this.rtl?1e4:-1e4}),this.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:\"relative\",left:0}),this.isInputHidden=!1},focus:function(){var e=this;e.isDisabled||(e.ignoreFocus=!0,e.$control_input[0].focus(),window.setTimeout(function(){e.ignoreFocus=!1,e.onFocus()},0))},blur:function(e){this.$control_input[0].blur(),this.onBlur(null,e)},getScoreFunction:function(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())},getSearchOptions:function(){var e=this.settings,t=e.sortField;return\"string\"==typeof t&&(t=[{field:t}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}},search:function(e){var t,n,r,i=this.settings,o=this.getSearchOptions();if(i.score&&\"function\"!=typeof(r=this.settings.score.apply(this,[e])))throw new Error('Selectize \"score\" setting must be a function that returns a function');if(e!==this.lastQuery?(this.lastQuery=e,n=this.sifter.search(e,C.extend(o,{score:r})),this.currentResults=n):n=C.extend(!0,{},this.currentResults),i.hideSelected)for(t=n.items.length-1;0<=t;t--)-1!==this.items.indexOf(D(n.items[t].id))&&n.items.splice(t,1);return n},refreshOptions:function(e){var t,n,r,i,o,a,s,u,l,c,d,p,h,f,g,m;void 0===e&&(e=!0);var v,y,w=this,b=C.trim(w.$control_input.val()),$=w.search(b),_=w.$dropdown_content,S=w.$activeOption&&D(w.$activeOption.attr(\"data-value\"));for(i=$.items.length,\"number\"==typeof w.settings.maxOptions&&(i=Math.min(i,w.settings.maxOptions)),o={},a=[],t=0;t<i;t++)for(s=w.options[$.items[t].id],u=w.render(\"option\",s),l=s[w.settings.optgroupField]||\"\",n=0,r=(c=C.isArray(l)?l:[l])&&c.length;n<r;n++)l=c[n],w.optgroups.hasOwnProperty(l)||(l=\"\"),o.hasOwnProperty(l)||(o[l]=document.createDocumentFragment(),a.push(l)),o[l].appendChild(u);for(this.settings.lockOptgroupOrder&&a.sort(function(e,t){return(w.optgroups[e].$order||0)-(w.optgroups[t].$order||0)}),d=document.createDocumentFragment(),t=0,i=a.length;t<i;t++)l=a[t],w.optgroups.hasOwnProperty(l)&&o[l].childNodes.length?((p=document.createDocumentFragment()).appendChild(w.render(\"optgroup_header\",w.optgroups[l])),p.appendChild(o[l]),d.appendChild(w.render(\"optgroup\",C.extend({},w.optgroups[l],{html:(v=p,y=void 0,y=document.createElement(\"div\"),y.appendChild(v.cloneNode(!0)),y.innerHTML),dom:p})))):d.appendChild(o[l]);if(_.html(d),w.settings.highlight&&(_.removeHighlight(),$.query.length&&$.tokens.length))for(t=0,i=$.tokens.length;t<i;t++)k(_,$.tokens[t].regex);if(!w.settings.hideSelected)for(t=0,i=w.items.length;t<i;t++)w.getOption(w.items[t]).addClass(\"selected\");(h=w.canCreate(b))&&(_.prepend(w.render(\"option_create\",{input:b})),m=C(_[0].childNodes[0])),w.hasOptions=0<$.items.length||h,w.hasOptions?(0<$.items.length?((g=S&&w.getOption(S))&&g.length?f=g:\"single\"===w.settings.mode&&w.items.length&&(f=w.getOption(w.items[0])),f&&f.length||(f=m&&!w.settings.addPrecedence?w.getAdjacentOption(m,1):_.find(\"[data-selectable]:first\"))):f=m,w.setActiveOption(f),e&&!w.isOpen&&w.open()):(w.setActiveOption(null),e&&w.isOpen&&w.close())},addOption:function(e){var t,n,r;if(C.isArray(e))for(t=0,n=e.length;t<n;t++)this.addOption(e[t]);else(r=this.registerOption(e))&&(this.userOptions[r]=!0,this.lastQuery=null,this.trigger(\"option_add\",r,e))},registerOption:function(e){var t=D(e[this.settings.valueField]);return null!=t&&!this.options.hasOwnProperty(t)&&(e.$order=e.$order||++this.order,this.options[t]=e,t)},registerOptionGroup:function(e){var t=D(e[this.settings.optgroupValueField]);return!!t&&(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)},addOptionGroup:function(e,t){t[this.settings.optgroupValueField]=e,(e=this.registerOptionGroup(t))&&this.trigger(\"optgroup_add\",e,t)},removeOptionGroup:function(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.renderCache={},this.trigger(\"optgroup_remove\",e))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger(\"optgroup_clear\")},updateOption:function(e,t){var n,r,i,o,a,s,u,l=this;if(e=D(e),i=D(t[l.settings.valueField]),null!==e&&l.options.hasOwnProperty(e)){if(\"string\"!=typeof i)throw new Error(\"Value must be set in option data\");u=l.options[e].$order,i!==e&&(delete l.options[e],-1!==(o=l.items.indexOf(e))&&l.items.splice(o,1,i)),t.$order=t.$order||u,l.options[i]=t,a=l.renderCache.item,s=l.renderCache.option,a&&(delete a[e],delete a[i]),s&&(delete s[e],delete s[i]),-1!==l.items.indexOf(i)&&(n=l.getItem(e),r=C(l.render(\"item\",t)),n.hasClass(\"active\")&&r.addClass(\"active\"),n.replaceWith(r)),l.lastQuery=null,l.isOpen&&l.refreshOptions(!1)}},removeOption:function(e,t){e=D(e);var n=this.renderCache.item,r=this.renderCache.option;n&&delete n[e],r&&delete r[e],delete this.userOptions[e],delete this.options[e],this.lastQuery=null,this.trigger(\"option_remove\",e),this.removeItem(e,t)},clearOptions:function(){var n=this;n.loadedSearches={},n.userOptions={},n.renderCache={};var r=n.options;C.each(n.options,function(e,t){-1==n.items.indexOf(e)&&delete r[e]}),n.options=n.sifter.items=r,n.lastQuery=null,n.trigger(\"option_clear\")},getOption:function(e){return this.getElementWithValue(e,this.$dropdown_content.find(\"[data-selectable]\"))},getAdjacentOption:function(e,t){var n=this.$dropdown.find(\"[data-selectable]\"),r=n.index(e)+t;return 0<=r&&r<n.length?n.eq(r):C()},getElementWithValue:function(e,t){if(null!=(e=D(e)))for(var n=0,r=t.length;n<r;n++)if(t[n].getAttribute(\"data-value\")===e)return C(t[n]);return C()},getItem:function(e){return this.getElementWithValue(e,this.$control.children())},addItems:function(e,t){this.buffer=document.createDocumentFragment();for(var n=this.$control[0].childNodes,r=0;r<n.length;r++)this.buffer.appendChild(n[r]);for(var i=C.isArray(e)?e:[e],o=(r=0,i.length);r<o;r++)this.isPending=r<o-1,this.addItem(i[r],t);var a=this.$control[0];a.insertBefore(this.buffer,a.firstChild),this.buffer=null},addItem:function(s,u){n(this,u?[]:[\"change\"],function(){var e,t,n,r,i,o=this,a=o.settings.mode;s=D(s),-1===o.items.indexOf(s)?o.options.hasOwnProperty(s)&&(\"single\"===a&&o.clear(u),\"multi\"===a&&o.isFull()||(e=C(o.render(\"item\",o.options[s])),i=o.isFull(),o.items.splice(o.caretPos,0,s),o.insertAtCaret(e),(!o.isPending||!i&&o.isFull())&&o.refreshState(),o.isSetup&&(n=o.$dropdown_content.find(\"[data-selectable]\"),o.isPending||(t=o.getOption(s),r=o.getAdjacentOption(t,1).attr(\"data-value\"),o.refreshOptions(o.isFocused&&\"single\"!==a),r&&o.setActiveOption(o.getOption(r))),!n.length||o.isFull()?o.close():o.isPending||o.positionDropdown(),o.updatePlaceholder(),o.trigger(\"item_add\",s,e),o.isPending||o.updateOriginalInput({silent:u})))):\"single\"===a&&o.close()})},removeItem:function(e,t){var n,r,i,o=this;n=e instanceof C?e:o.getItem(e),e=D(n.attr(\"data-value\")),-1!==(r=o.items.indexOf(e))&&(n.remove(),n.hasClass(\"active\")&&(i=o.$activeItems.indexOf(n[0]),o.$activeItems.splice(i,1)),o.items.splice(r,1),o.lastQuery=null,!o.settings.persist&&o.userOptions.hasOwnProperty(e)&&o.removeOption(e,t),r<o.caretPos&&o.setCaret(o.caretPos-1),o.refreshState(),o.updatePlaceholder(),o.updateOriginalInput({silent:t}),o.positionDropdown(),o.trigger(\"item_remove\",e,n))},createItem:function(e,n){var r=this,i=r.caretPos;e=e||C.trim(r.$control_input.val()||\"\");var o=arguments[arguments.length-1];if(\"function\"!=typeof o&&(o=function(){}),\"boolean\"!=typeof n&&(n=!0),!r.canCreate(e))return o(),!1;r.lock();var t=\"function\"==typeof r.settings.create?this.settings.create:function(e){var t={};return t[r.settings.labelField]=e,t[r.settings.valueField]=e,t},a=function(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}(function(e){if(r.unlock(),!e||\"object\"!=typeof e)return o();var t=D(e[r.settings.valueField]);if(\"string\"!=typeof t)return o();r.setTextboxValue(\"\"),r.addOption(e),r.setCaret(i),r.addItem(t),r.refreshOptions(n&&\"single\"!==r.settings.mode),o(e)}),s=t.apply(this,[e,a]);return void 0!==s&&a(s),!0},refreshItems:function(){this.lastQuery=null,this.isSetup&&this.addItem(this.items),this.refreshState(),this.updateOriginalInput()},refreshState:function(){this.refreshValidityState(),this.refreshClasses()},refreshValidityState:function(){if(!this.isRequired)return!1;var e=!this.items.length;this.isInvalid=e,this.$control_input.prop(\"required\",e),this.$input.prop(\"required\",!e)},refreshClasses:function(){var e=this,t=e.isFull(),n=e.isLocked;e.$wrapper.toggleClass(\"rtl\",e.rtl),e.$control.toggleClass(\"focus\",e.isFocused).toggleClass(\"disabled\",e.isDisabled).toggleClass(\"required\",e.isRequired).toggleClass(\"invalid\",e.isInvalid).toggleClass(\"locked\",n).toggleClass(\"full\",t).toggleClass(\"not-full\",!t).toggleClass(\"input-active\",e.isFocused&&!e.isInputHidden).toggleClass(\"dropdown-active\",e.isOpen).toggleClass(\"has-options\",!C.isEmptyObject(e.options)).toggleClass(\"has-items\",0<e.items.length),e.$control_input.data(\"grow\",!t&&!n)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(e){var t,n,r,i,o=this;if(e=e||{},1===o.tagType){for(r=[],t=0,n=o.items.length;t<n;t++)i=o.options[o.items[t]][o.settings.labelField]||\"\",r.push('<option value=\"'+s(o.items[t])+'\" selected=\"selected\">'+s(i)+\"</option>\");r.length||this.$input.attr(\"multiple\")||r.push('<option value=\"\" selected=\"selected\"></option>'),o.$input.html(r.join(\"\"))}else o.$input.val(o.getValue()),o.$input.attr(\"value\",o.$input.val());o.isSetup&&(e.silent||o.trigger(\"change\",o.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var e=this.$control_input;this.items.length?e.removeAttr(\"placeholder\"):e.attr(\"placeholder\",this.settings.placeholder),e.triggerHandler(\"update\",{force:!0})}},open:function(){var e=this;e.isLocked||e.isOpen||\"multi\"===e.settings.mode&&e.isFull()||(e.focus(),e.isOpen=!0,e.refreshState(),e.$dropdown.css({visibility:\"hidden\",display:\"block\"}),e.positionDropdown(),e.$dropdown.css({visibility:\"visible\"}),e.trigger(\"dropdown_open\",e.$dropdown))},close:function(){var e=this,t=e.isOpen;\"single\"===e.settings.mode&&e.items.length&&(e.hideInput(),e.isBlurring||e.$control_input.blur()),e.isOpen=!1,e.$dropdown.hide(),e.setActiveOption(null),e.refreshState(),t&&e.trigger(\"dropdown_close\",e.$dropdown)},positionDropdown:function(){var e=this.$control,t=\"body\"===this.settings.dropdownParent?e.offset():e.position();t.top+=e.outerHeight(!0),this.$dropdown.css({width:e[0].getBoundingClientRect().width,top:t.top,left:t.left})},clear:function(e){var t=this;t.items.length&&(t.$control.children(\":not(input)\").remove(),t.items=[],t.lastQuery=null,t.setCaret(0),t.setActiveItem(null),t.updatePlaceholder(),t.updateOriginalInput({silent:e}),t.refreshState(),t.showInput(),t.trigger(\"clear\"))},insertAtCaret:function(e){var t=Math.min(this.caretPos,this.items.length),n=e[0],r=this.buffer||this.$control[0];0===t?r.insertBefore(n,r.firstChild):r.insertBefore(n,r.childNodes[t]),this.setCaret(t+1)},deleteSelection:function(e){var t,n,r,i,o,a,s,u,l,c=this;if(r=e&&8===e.keyCode?-1:1,i=p(c.$control_input[0]),c.$activeOption&&!c.settings.hideSelected&&(s=c.getAdjacentOption(c.$activeOption,-1).attr(\"data-value\")),o=[],c.$activeItems.length){for(l=c.$control.children(\".active:\"+(0<r?\"last\":\"first\")),a=c.$control.children(\":not(input)\").index(l),0<r&&a++,t=0,n=c.$activeItems.length;t<n;t++)o.push(C(c.$activeItems[t]).attr(\"data-value\"));e&&(e.preventDefault(),e.stopPropagation())}else(c.isFocused||\"single\"===c.settings.mode)&&c.items.length&&(r<0&&0===i.start&&0===i.length?o.push(c.items[c.caretPos-1]):0<r&&i.start===c.$control_input.val().length&&o.push(c.items[c.caretPos]));if(!o.length||\"function\"==typeof c.settings.onDelete&&!1===c.settings.onDelete.apply(c,[o]))return!1;for(void 0!==a&&c.setCaret(a);o.length;)c.removeItem(o.pop());return c.showInput(),c.positionDropdown(),c.refreshOptions(!0),s&&(u=c.getOption(s)).length&&c.setActiveOption(u),!0},advanceSelection:function(e,t){var n,r,i,o,a,s=this;0!==e&&(s.rtl&&(e*=-1),n=0<e?\"last\":\"first\",r=p(s.$control_input[0]),s.isFocused&&!s.isInputHidden?(o=s.$control_input.val().length,(e<0?0===r.start&&0===r.length:r.start===o)&&!o&&s.advanceCaret(e,t)):(a=s.$control.children(\".active:\"+n)).length&&(i=s.$control.children(\":not(input)\").index(a),s.setActiveItem(null),s.setCaret(0<e?i+1:i)))},advanceCaret:function(e,t){var n,r;0!==e&&(n=0<e?\"next\":\"prev\",this.isShiftDown?(r=this.$control_input[n]()).length&&(this.hideInput(),this.setActiveItem(r),t&&t.preventDefault()):this.setCaret(this.caretPos+e))},setCaret:function(e){var t,n,r,i;if(e=\"single\"===this.settings.mode?this.items.length:Math.max(0,Math.min(this.items.length,e)),!this.isPending)for(t=0,n=(r=this.$control.children(\":not(input)\")).length;t<n;t++)i=C(r[t]).detach(),t<e?this.$control_input.before(i):this.$control.append(i);this.caretPos=e},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){this.$input.prop(\"disabled\",!0),this.$control_input.prop(\"disabled\",!0).prop(\"tabindex\",-1),this.isDisabled=!0,this.lock()},enable:function(){this.$input.prop(\"disabled\",!1),this.$control_input.prop(\"disabled\",!1).prop(\"tabindex\",this.tabIndex),this.isDisabled=!1,this.unlock()},destroy:function(){var e=this,t=e.eventNS,n=e.revertSettings;e.trigger(\"destroy\"),e.off(),e.$wrapper.remove(),e.$dropdown.remove(),e.$input.html(\"\").append(n.$children).removeAttr(\"tabindex\").removeClass(\"selectized\").attr({tabindex:n.tabindex}).show(),e.$control_input.removeData(\"grow\"),e.$input.removeData(\"selectize\"),0==--_.count&&_.$testInput&&(_.$testInput.remove(),_.$testInput=void 0),C(window).off(t),C(document).off(t),C(document.body).off(t),delete e.$input[0].selectize},render:function(e,t){var n,r,i=\"\",o=!1,a=this;return\"option\"!==e&&\"item\"!==e||(o=!!(n=D(t[a.settings.valueField]))),o&&(l(a.renderCache[e])||(a.renderCache[e]={}),a.renderCache[e].hasOwnProperty(n))?a.renderCache[e][n]:(i=C(a.settings.render[e].apply(this,[t,s])),\"option\"===e||\"option_create\"===e?t[a.settings.disabledField]||i.attr(\"data-selectable\",\"\"):\"optgroup\"===e&&(r=t[a.settings.optgroupValueField]||\"\",i.attr(\"data-group\",r),t[a.settings.disabledField]&&i.attr(\"data-disabled\",\"\")),\"option\"!==e&&\"item\"!==e||i.attr(\"data-value\",n||\"\"),o&&(a.renderCache[e][n]=i[0]),i[0])},clearCache:function(e){void 0===e?this.renderCache={}:delete this.renderCache[e]},canCreate:function(e){if(!this.settings.create)return!1;var t=this.settings.createFilter;return e.length&&(\"function\"!=typeof t||t.apply(this,[e]))&&(\"string\"!=typeof t||new RegExp(t).test(e))&&(!(t instanceof RegExp)||t.test(e))}}),_.count=0,_.defaults={options:[],optgroups:[],plugins:[],delimiter:\",\",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:\"loading\",dataAttr:\"data-data\",optgroupField:\"optgroup\",valueField:\"value\",labelField:\"text\",disabledField:\"disabled\",optgroupLabelField:\"label\",optgroupValueField:\"value\",lockOptgroupOrder:!1,sortField:\"$order\",searchField:[\"text\"],searchConjunction:\"and\",mode:null,wrapperClass:\"selectize-control\",inputClass:\"selectize-input\",dropdownClass:\"selectize-dropdown\",dropdownContentClass:\"selectize-dropdown-content\",dropdownParent:null,copyClassesToDropdown:!0,render:{}},C.fn.selectize=function(i){var o=C.fn.selectize.defaults,d=C.extend({},o,i),p=d.dataAttr,h=d.labelField,f=d.valueField,g=d.disabledField,m=d.optgroupField,v=d.optgroupLabelField,y=d.optgroupValueField;return this.each(function(){if(!this.selectize){var e=C(this),t=this.tagName.toLowerCase(),n=e.attr(\"placeholder\")||e.attr(\"data-placeholder\");n||d.allowEmptyOption||(n=e.children('option[value=\"\"]').text());var r={placeholder:n,options:[],optgroups:[],items:[]};\"select\"===t?function(e,a){var t,n,r,i,o=a.options,s={},u=function(e){var t=p&&e.attr(p);return\"string\"==typeof t&&t.length?JSON.parse(t):null},l=function(e,t){e=C(e);var n=D(e.val());if(n||d.allowEmptyOption)if(s.hasOwnProperty(n)){if(t){var r=s[n][m];r?C.isArray(r)?r.push(t):s[n][m]=[r,t]:s[n][m]=t}}else{var i=u(e)||{};i[h]=i[h]||e.text(),i[f]=i[f]||n,i[g]=i[g]||e.prop(\"disabled\"),i[m]=i[m]||t,s[n]=i,o.push(i),e.is(\":selected\")&&a.items.push(n)}},c=function(e){var t,n,r,i,o;for((r=(e=C(e)).attr(\"label\"))&&((i=u(e)||{})[v]=r,i[y]=r,i[g]=e.prop(\"disabled\"),a.optgroups.push(i)),t=0,n=(o=C(\"option\",e)).length;t<n;t++)l(o[t],r)};for(a.maxItems=e.attr(\"multiple\")?null:1,t=0,n=(i=e.children()).length;t<n;t++)\"optgroup\"===(r=i[t].tagName.toLowerCase())?c(i[t]):\"option\"===r&&l(i[t])}(e,r):function(e,t){var n,r,i,o,a=e.attr(p);if(a)for(t.options=JSON.parse(a),n=0,r=t.options.length;n<r;n++)t.items.push(t.options[n][f]);else{var s=C.trim(e.val()||\"\");if(!d.allowEmptyOption&&!s.length)return;for(n=0,r=(i=s.split(d.delimiter)).length;n<r;n++)(o={})[h]=i[n],o[f]=i[n],t.options.push(o);t.items=i}}(e,r),new _(e,C.extend(!0,{},o,r,i))}})},C.fn.selectize.defaults=_.defaults,C.fn.selectize.support={validity:b},_.define(\"drag_drop\",function(e){if(!C.fn.sortable)throw new Error('The \"drag_drop\" plugin requires jQuery UI \"sortable\".');if(\"multi\"===this.settings.mode){var t,n,r,i=this;i.lock=(t=i.lock,function(){var e=i.$control.data(\"sortable\");return e&&e.disable(),t.apply(i,arguments)}),i.unlock=(n=i.unlock,function(){var e=i.$control.data(\"sortable\");return e&&e.enable(),n.apply(i,arguments)}),i.setup=(r=i.setup,function(){r.apply(this,arguments);var n=i.$control.sortable({items:\"[data-value]\",forcePlaceholderSize:!0,disabled:i.isLocked,start:function(e,t){t.placeholder.css(\"width\",t.helper.css(\"width\")),n.css({overflow:\"visible\"})},stop:function(){n.css({overflow:\"hidden\"});var e=i.$activeItems?i.$activeItems.slice():null,t=[];n.children(\"[data-value]\").each(function(){t.push(C(this).attr(\"data-value\"))}),i.setValue(t),i.setActiveItem(e)}})})}}),_.define(\"dropdown_header\",function(e){var t,n=this;e=C.extend({title:\"Untitled\",headerClass:\"selectize-dropdown-header\",titleRowClass:\"selectize-dropdown-header-title\",labelClass:\"selectize-dropdown-header-label\",closeClass:\"selectize-dropdown-header-close\",html:function(e){return'<div class=\"'+e.headerClass+'\"><div class=\"'+e.titleRowClass+'\"><span class=\"'+e.labelClass+'\">'+e.title+'</span><a href=\"javascript:void(0)\" class=\"'+e.closeClass+'\">&times;</a></div></div>'}},e),n.setup=(t=n.setup,function(){t.apply(n,arguments),n.$dropdown_header=C(e.html(e)),n.$dropdown.prepend(n.$dropdown_header)})}),_.define(\"optgroup_columns\",function(s){var o,u=this;s=C.extend({equalizeWidth:!0,equalizeHeight:!0},s),this.getAdjacentOption=function(e,t){var n=e.closest(\"[data-group]\").find(\"[data-selectable]\"),r=n.index(e)+t;return 0<=r&&r<n.length?n.eq(r):C()},this.onKeyDown=(o=u.onKeyDown,function(e){var t,n,r,i;return!this.isOpen||37!==e.keyCode&&39!==e.keyCode?o.apply(this,arguments):(u.ignoreHover=!0,t=(i=this.$activeOption.closest(\"[data-group]\")).find(\"[data-selectable]\").index(this.$activeOption),void((n=(r=(i=37===e.keyCode?i.prev(\"[data-group]\"):i.next(\"[data-group]\")).find(\"[data-selectable]\")).eq(Math.min(r.length-1,t))).length&&this.setActiveOption(n)))});var l=function(){var e,t=l.width,n=document;return void 0===t&&((e=n.createElement(\"div\")).innerHTML='<div style=\"width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;\"><div style=\"width:1px;height:100px;\"></div></div>',e=e.firstChild,n.body.appendChild(e),t=l.width=e.offsetWidth-e.clientWidth,n.body.removeChild(e)),t},e=function(){var e,t,n,r,i,o,a;if((t=(a=C(\"[data-group]\",u.$dropdown_content)).length)&&u.$dropdown_content.width()){if(s.equalizeHeight){for(e=n=0;e<t;e++)n=Math.max(n,a.eq(e).height());a.css({height:n})}s.equalizeWidth&&(o=u.$dropdown_content.innerWidth()-l(),r=Math.round(o/t),a.css({width:r}),1<t&&(i=o-r*(t-1),a.eq(t-1).css({width:i})))}};(s.equalizeHeight||s.equalizeWidth)&&(t.after(this,\"positionDropdown\",e),t.after(this,\"refreshOptions\",e))}),_.define(\"remove_button\",function(e){e=C.extend({label:\"&times;\",title:\"Remove\",className:\"remove\",append:!0},e);var n,r,i,o,a;\"single\"!==this.settings.mode?(o=n=this,a='<a href=\"javascript:void(0)\" class=\"'+(r=e).className+'\" tabindex=\"-1\" title=\"'+s(r.title)+'\">'+r.label+\"</a>\",n.setup=(i=o.setup,function(){if(r.append){var t=o.settings.render.item;o.settings.render.item=function(e){return function(e,t){var n=e.search(/(<\\/[^>]+>\\s*)$/);return e.substring(0,n)+t+e.substring(n)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on(\"click\",\".\"+r.className,function(e){if(e.preventDefault(),!o.isLocked){var t=C(e.currentTarget).parent();o.setActiveItem(t),o.deleteSelection()&&o.setCaret(o.items.length)}})})):function(n,r){r.className=\"remove-single\";var i,o=n,a='<a href=\"javascript:void(0)\" class=\"'+r.className+'\" tabindex=\"-1\" title=\"'+s(r.title)+'\">'+r.label+\"</a>\";n.setup=(i=o.setup,function(){if(r.append){var e=C(o.$input.context).attr(\"id\"),t=(C(\"#\"+e),o.settings.render.item);o.settings.render.item=function(e){return function(e,t){return C(\"<span>\").append(e).append(t)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on(\"click\",\".\"+r.className,function(e){e.preventDefault(),o.isLocked||o.clear()})})}(this,e)}),_.define(\"restore_on_backspace\",function(r){var i,e=this;r.text=r.text||function(e){return e[this.settings.labelField]},this.onKeyDown=(i=e.onKeyDown,function(e){var t,n;return 8===e.keyCode&&\"\"===this.$control_input.val()&&!this.$activeItems.length&&0<=(t=this.caretPos-1)&&t<this.items.length?(n=this.options[this.items[t]],this.deleteSelection(e)&&(this.setTextboxValue(r.text.apply(this,[n])),this.refreshOptions(!0)),void e.preventDefault()):i.apply(this,arguments)})}),_}),angular.module(\"selectize\",[]).value(\"selectizeConfig\",{}).directive(\"selectize\",[\"selectizeConfig\",function(p){return{restrict:\"EA\",require:\"^ngModel\",scope:{ngModel:\"=\",config:\"=?\",options:\"=?\",ngDisabled:\"=\",ngRequired:\"&\"},link:function(n,e,t,r){Selectize.defaults.maxItems=null;var i,o=angular.extend({},Selectize.defaults,p,n.config);function a(e){e?i.disable():i.enable()}r.$isEmpty=function(e){return null==e||!e.length};function s(e){return e?(e=angular.isArray(e)||angular.isObject(e)?e:[e],$.map(e,function(e){return\"string\"==typeof e?function(e){var t={};return t[o.labelField]=e,t[o.valueField]=e,t}(e):e})):[]}function u(){!function(){var e=(n.ngRequired()||t.required||o.required)&&r.$isEmpty(n.ngModel);r.$setValidity(\"required\",!e)}(),i.$control.toggleClass(\"ng-valid\",r.$valid),i.$control.toggleClass(\"ng-invalid\",r.$invalid),i.$control.toggleClass(\"ng-dirty\",r.$dirty),i.$control.toggleClass(\"ng-pristine\",r.$pristine),angular.equals(i.items,n.ngModel)||(i.addOption(s(n.ngModel)),i.setValue(n.ngModel))}var l=o.onChange,c=o.onOptionAdd;o.onChange=function(){n.disableOnChange||(angular.equals(i.items,n.ngModel)||n.$evalAsync(function(){var e=angular.copy(i.items);1==o.maxItems&&(e=e[0]),r.$setViewValue(e)}),l&&l.apply(this,arguments))},o.onOptionAdd=function(e,t){-1===n.options.indexOf(t)&&(n.options.push(t),c&&c.apply(this,arguments))},n.options?n.options.splice(0,n.options.length,s(n.options)):n.options=s(angular.copy(n.ngModel));var d=o.onInitialize;o.onInitialize=function(){(i=e[0].selectize).addOption(n.options),i.setValue(n.ngModel),d&&d(i),n.$watch(\"options\",function(){n.disableOnChange=!0,i.clearOptions(),i.addOption(n.options),i.setValue(n.ngModel),n.disableOnChange=!1},!0),n.$watchCollection(\"ngModel\",u),n.$watch(\"ngDisabled\",a)},e.selectize(o),e.on(\"$destroy\",function(){i&&(i.destroy(),e=null)})}}}]),function(e,t){\"function\"==typeof define&&define.amd?define([\"jquery\"],t):\"undefined\"!=typeof exports?t(require(\"jquery\")):(t(e.jquery),e.bootstrapSwitch={})}(this,function(e){\"use strict\";var t,n=(t=e)&&t.__esModule?t:{default:t},r=Object.assign||function(e){for(var t,n=1;n<arguments.length;n++)for(var r in t=arguments[n])Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e},i=function(){function r(e,t){for(var n,r=0;r<t.length;r++)(n=t[r]).enumerable=n.enumerable||!1,n.configurable=!0,\"value\"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}return function(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}}(),s=n.default||window.jQuery||window.$,u=function(){function t(r){var i=this,e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};(function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")})(this,t),this.$element=s(r),this.options=s.extend({},s.fn.bootstrapSwitch.defaults,this._getElementOptions(),e),this.prevOptions={},this.$wrapper=s(\"<div>\",{class:function(){var e=[];return e.push(i.options.state?\"on\":\"off\"),i.options.size&&e.push(i.options.size),i.options.disabled&&e.push(\"disabled\"),i.options.readonly&&e.push(\"readonly\"),i.options.indeterminate&&e.push(\"indeterminate\"),i.options.inverse&&e.push(\"inverse\"),i.$element.attr(\"id\")&&e.push(\"id-\"+i.$element.attr(\"id\")),e.map(i._getClass.bind(i)).concat([i.options.baseClass],i._getClasses(i.options.wrapperClass)).join(\" \")}}),this.$container=s(\"<div>\",{class:this._getClass(\"container\")}),this.$on=s(\"<span>\",{html:this.options.onText,class:this._getClass(\"handle-on\")+\" \"+this._getClass(this.options.onColor)}),this.$off=s(\"<span>\",{html:this.options.offText,class:this._getClass(\"handle-off\")+\" \"+this._getClass(this.options.offColor)}),this.$label=s(\"<span>\",{html:this.options.labelText,class:this._getClass(\"label\")}),this.$element.on(\"init.bootstrapSwitch\",this.options.onInit.bind(this,r)),this.$element.on(\"switchChange.bootstrapSwitch\",function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];!1===i.options.onSwitchChange.apply(r,t)&&(i.$element.is(\":radio\")?s('[name=\"'+i.$element.attr(\"name\")+'\"]').trigger(\"previousState.bootstrapSwitch\",!0):i.$element.trigger(\"previousState.bootstrapSwitch\",!0))}),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop(\"indeterminate\",!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger(\"init.bootstrapSwitch\",this.options.state)}return i(t,[{key:\"setPrevOptions\",value:function(){this.prevOptions=r({},this.options)}},{key:\"state\",value:function(e,t){return void 0===e?this.options.state:(this.options.disabled||this.options.readonly||this.options.state&&!this.options.radioAllOff&&this.$element.is(\":radio\")||(this.$element.is(\":radio\")?s('[name=\"'+this.$element.attr(\"name\")+'\"]').trigger(\"setPreviousOptions.bootstrapSwitch\"):this.$element.trigger(\"setPreviousOptions.bootstrapSwitch\"),this.options.indeterminate&&this.indeterminate(!1),this.$element.prop(\"checked\",!!e).trigger(\"change.bootstrapSwitch\",t)),this.$element)}},{key:\"toggleState\",value:function(e){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop(\"checked\",!this.options.state).trigger(\"change.bootstrapSwitch\",e)}},{key:\"size\",value:function(e){return void 0===e?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this._getClass(this.options.size)),e&&this.$wrapper.addClass(this._getClass(e)),this._width(),this._containerPosition(),this.options.size=e,this.$element)}},{key:\"animate\",value:function(e){return void 0===e?this.options.animate:this.options.animate===!!e?this.$element:this.toggleAnimate()}},{key:\"toggleAnimate\",value:function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this._getClass(\"animate\")),this.$element}},{key:\"disabled\",value:function(e){return void 0===e?this.options.disabled:this.options.disabled===!!e?this.$element:this.toggleDisabled()}},{key:\"toggleDisabled\",value:function(){return this.options.disabled=!this.options.disabled,this.$element.prop(\"disabled\",this.options.disabled),this.$wrapper.toggleClass(this._getClass(\"disabled\")),this.$element}},{key:\"readonly\",value:function(e){return void 0===e?this.options.readonly:this.options.readonly===!!e?this.$element:this.toggleReadonly()}},{key:\"toggleReadonly\",value:function(){return this.options.readonly=!this.options.readonly,this.$element.prop(\"readonly\",this.options.readonly),this.$wrapper.toggleClass(this._getClass(\"readonly\")),this.$element}},{key:\"indeterminate\",value:function(e){return void 0===e?this.options.indeterminate:this.options.indeterminate===!!e?this.$element:this.toggleIndeterminate()}},{key:\"toggleIndeterminate\",value:function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop(\"indeterminate\",this.options.indeterminate),this.$wrapper.toggleClass(this._getClass(\"indeterminate\")),this._containerPosition(),this.$element}},{key:\"inverse\",value:function(e){return void 0===e?this.options.inverse:this.options.inverse===!!e?this.$element:this.toggleInverse()}},{key:\"toggleInverse\",value:function(){this.$wrapper.toggleClass(this._getClass(\"inverse\"));var e=this.$on.clone(!0),t=this.$off.clone(!0);return this.$on.replaceWith(t),this.$off.replaceWith(e),this.$on=t,this.$off=e,this.options.inverse=!this.options.inverse,this.$element}},{key:\"onColor\",value:function(e){return void 0===e?this.options.onColor:(this.options.onColor&&this.$on.removeClass(this._getClass(this.options.onColor)),this.$on.addClass(this._getClass(e)),this.options.onColor=e,this.$element)}},{key:\"offColor\",value:function(e){return void 0===e?this.options.offColor:(this.options.offColor&&this.$off.removeClass(this._getClass(this.options.offColor)),this.$off.addClass(this._getClass(e)),this.options.offColor=e,this.$element)}},{key:\"onText\",value:function(e){return void 0===e?this.options.onText:(this.$on.html(e),this._width(),this._containerPosition(),this.options.onText=e,this.$element)}},{key:\"offText\",value:function(e){return void 0===e?this.options.offText:(this.$off.html(e),this._width(),this._containerPosition(),this.options.offText=e,this.$element)}},{key:\"labelText\",value:function(e){return void 0===e?this.options.labelText:(this.$label.html(e),this._width(),this.options.labelText=e,this.$element)}},{key:\"handleWidth\",value:function(e){return void 0===e?this.options.handleWidth:(this.options.handleWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:\"labelWidth\",value:function(e){return void 0===e?this.options.labelWidth:(this.options.labelWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:\"baseClass\",value:function(){return this.options.baseClass}},{key:\"wrapperClass\",value:function(e){return void 0===e?this.options.wrapperClass:(e||(e=s.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(\" \")),this.$wrapper.addClass(this._getClasses(e).join(\" \")),this.options.wrapperClass=e,this.$element)}},{key:\"radioAllOff\",value:function(e){if(void 0===e)return this.options.radioAllOff;var t=!!e;return this.options.radioAllOff===t||(this.options.radioAllOff=t),this.$element}},{key:\"onInit\",value:function(e){return void 0===e?this.options.onInit:(e||(e=s.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=e,this.$element)}},{key:\"onSwitchChange\",value:function(e){return void 0===e?this.options.onSwitchChange:(e||(e=s.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=e,this.$element)}},{key:\"destroy\",value:function(){var e=this.$element.closest(\"form\");return e.length&&e.off(\"reset.bootstrapSwitch\").removeData(\"bootstrap-switch\"),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off(\".bootstrapSwitch\").removeData(\"bootstrap-switch\"),this.$element}},{key:\"_getElementOptions\",value:function(){return{state:this.$element.is(\":checked\"),size:this.$element.data(\"size\"),animate:this.$element.data(\"animate\"),disabled:this.$element.is(\":disabled\"),readonly:this.$element.is(\"[readonly]\"),indeterminate:this.$element.data(\"indeterminate\"),inverse:this.$element.data(\"inverse\"),radioAllOff:this.$element.data(\"radio-all-off\"),onColor:this.$element.data(\"on-color\"),offColor:this.$element.data(\"off-color\"),onText:this.$element.data(\"on-text\"),offText:this.$element.data(\"off-text\"),labelText:this.$element.data(\"label-text\"),handleWidth:this.$element.data(\"handle-width\"),labelWidth:this.$element.data(\"label-width\"),baseClass:this.$element.data(\"base-class\"),wrapperClass:this.$element.data(\"wrapper-class\")}}},{key:\"_width\",value:function(){var n=this,e=this.$on.add(this.$off).add(this.$label).css(\"width\",\"\"),r=\"auto\"===this.options.handleWidth?Math.round(Math.max(this.$on.width(),this.$off.width())):this.options.handleWidth;return e.width(r),this.$label.width(function(e,t){return\"auto\"===n.options.labelWidth?t<r?r:t:n.options.labelWidth}),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)}},{key:\"_containerPosition\",value:function(){var t=this,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:this.options.state;arguments[1];this.$container.css(\"margin-left\",function(){var e=[0,\"-\"+t._handleWidth+\"px\"];return t.options.indeterminate?\"-\"+t._handleWidth/2+\"px\":n?t.options.inverse?e[1]:e[0]:t.options.inverse?e[0]:e[1]})}},{key:\"_init\",value:function(){var e=this,t=function(){e.setPrevOptions(),e._width(),e._containerPosition(),setTimeout(function(){if(e.options.animate)return e.$wrapper.addClass(e._getClass(\"animate\"))},50)};if(this.$wrapper.is(\":visible\"))t();else var n=window.setInterval(function(){if(e.$wrapper.is(\":visible\"))return t(),window.clearInterval(n)},50)}},{key:\"_elementHandlers\",value:function(){var r=this;return this.$element.on({\"setPreviousOptions.bootstrapSwitch\":this.setPrevOptions.bind(this),\"previousState.bootstrapSwitch\":function(){r.options=r.prevOptions,r.options.indeterminate&&r.$wrapper.addClass(r._getClass(\"indeterminate\")),r.$element.prop(\"checked\",r.options.state).trigger(\"change.bootstrapSwitch\",!0)},\"change.bootstrapSwitch\":function(e,t){e.preventDefault(),e.stopImmediatePropagation();var n=r.$element.is(\":checked\");r._containerPosition(n),n===r.options.state||(r.options.state=n,r.$wrapper.toggleClass(r._getClass(\"off\")).toggleClass(r._getClass(\"on\")),!t&&(r.$element.is(\":radio\")&&s('[name=\"'+r.$element.attr(\"name\")+'\"]').not(r.$element).prop(\"checked\",!1).trigger(\"change.bootstrapSwitch\",!0),r.$element.trigger(\"switchChange.bootstrapSwitch\",[n])))},\"focus.bootstrapSwitch\":function(e){e.preventDefault(),r.$wrapper.addClass(r._getClass(\"focused\"))},\"blur.bootstrapSwitch\":function(e){e.preventDefault(),r.$wrapper.removeClass(r._getClass(\"focused\"))},\"keydown.bootstrapSwitch\":function(e){!e.which||r.options.disabled||r.options.readonly||(37===e.which||39===e.which)&&(e.preventDefault(),e.stopImmediatePropagation(),r.state(39===e.which))}})}},{key:\"_handleHandlers\",value:function(){var t=this;return this.$on.on(\"click.bootstrapSwitch\",function(e){return e.preventDefault(),e.stopPropagation(),t.state(!1),t.$element.trigger(\"focus.bootstrapSwitch\")}),this.$off.on(\"click.bootstrapSwitch\",function(e){return e.preventDefault(),e.stopPropagation(),t.state(!0),t.$element.trigger(\"focus.bootstrapSwitch\")})}},{key:\"_labelHandlers\",value:function(){var n=this;this.$label.on({click:function(e){e.stopPropagation()},\"mousedown.bootstrapSwitch touchstart.bootstrapSwitch\":function(e){n._dragStart||n.options.disabled||n.options.readonly||(e.preventDefault(),e.stopPropagation(),n._dragStart=(e.pageX||e.originalEvent.touches[0].pageX)-parseInt(n.$container.css(\"margin-left\"),10),n.options.animate&&n.$wrapper.removeClass(n._getClass(\"animate\")),n.$element.trigger(\"focus.bootstrapSwitch\"))},\"mousemove.bootstrapSwitch touchmove.bootstrapSwitch\":function(e){if(null!=n._dragStart){var t=(e.pageX||e.originalEvent.touches[0].pageX)-n._dragStart;e.preventDefault(),t<-n._handleWidth||0<t||(n._dragEnd=t,n.$container.css(\"margin-left\",n._dragEnd+\"px\"))}},\"mouseup.bootstrapSwitch touchend.bootstrapSwitch\":function(e){if(n._dragStart){if(e.preventDefault(),n.options.animate&&n.$wrapper.addClass(n._getClass(\"animate\")),n._dragEnd){var t=n._dragEnd>-n._handleWidth/2;n._dragEnd=!1,n.state(n.options.inverse?!t:t)}else n.state(!n.options.state);n._dragStart=!1}},\"mouseleave.bootstrapSwitch\":function(){n.$label.trigger(\"mouseup.bootstrapSwitch\")}})}},{key:\"_externalLabelHandler\",value:function(){var t=this,n=this.$element.closest(\"label\");n.on(\"click\",function(e){e.preventDefault(),e.stopImmediatePropagation(),e.target===n[0]&&t.toggleState()})}},{key:\"_formHandler\",value:function(){var e=this.$element.closest(\"form\");e.data(\"bootstrap-switch\")||e.on(\"reset.bootstrapSwitch\",function(){window.setTimeout(function(){e.find(\"input\").filter(function(){return s(this).data(\"bootstrap-switch\")}).each(function(){return s(this).bootstrapSwitch(\"state\",this.checked)})},1)}).data(\"bootstrap-switch\",!0)}},{key:\"_getClass\",value:function(e){return this.options.baseClass+\"-\"+e}},{key:\"_getClasses\",value:function(e){return s.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),t}();s.fn.bootstrapSwitch=function(o){for(var e=arguments.length,a=Array(1<e?e-1:0),t=1;t<e;t++)a[t-1]=arguments[t];return Array.prototype.reduce.call(this,function(e,t){var n=s(t),r=n.data(\"bootstrap-switch\"),i=r||new u(t,o);return r||n.data(\"bootstrap-switch\",i),\"string\"==typeof o?i[o].apply(i,a):e},this)},s.fn.bootstrapSwitch.Constructor=u,s.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:\"primary\",offColor:\"default\",onText:\"ON\",offText:\"OFF\",labelText:\"&nbsp\",handleWidth:\"auto\",labelWidth:\"auto\",baseClass:\"bootstrap-switch\",wrapperClass:\"wrapper\",onInit:function(){},onSwitchChange:function(){}}}),function(e,t){\"undefined\"!=typeof module&&module.exports?(\"undefined\"==typeof angular?t(require(\"angular\")):t(angular),module.exports=\"ngDialog\"):\"function\"==typeof define&&define.amd?define([\"angular\"],t):t(e.angular)}(this,function(O){\"use strict\";var e=O.module(\"ngDialog\",[]),T=O.element,t=O.isDefined,n=(document.body||document.documentElement).style,l=t(n.animation)||t(n.WebkitAnimation)||t(n.MozAnimation)||t(n.MsAnimation)||t(n.OAnimation),c=\"animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend\",r={html:!1,body:!1},A={},E=[],P=!1,M=!1;return e.provider(\"ngDialog\",function(){var S=this.defaults={className:\"ngdialog-theme-default\",appendClassName:\"\",disableAnimation:!1,plain:!1,showClose:!0,closeByDocument:!0,closeByEscape:!0,closeByNavigation:!1,appendTo:!1,preCloseCallback:!1,overlay:!0,cache:!0,trapFocus:!0,preserveFocus:!0,ariaAuto:!0,ariaRole:null,ariaLabelledById:null,ariaLabelledBySelector:null,ariaDescribedById:null,ariaDescribedBySelector:null,bodyClassName:\"ngdialog-open\",width:null,height:null};this.setForceHtmlReload=function(e){r.html=e||!1},this.setForceBodyReload=function(e){r.body=e||!1},this.setDefaults=function(e){O.extend(S,e)},this.setOpenOnePerName=function(e){M=e||!1};var C,k=0,D=0,x={};this.$get=[\"$document\",\"$templateCache\",\"$compile\",\"$q\",\"$http\",\"$rootScope\",\"$timeout\",\"$window\",\"$controller\",\"$injector\",function(n,o,g,a,s,m,v,y,w,u){var b=[],$={onDocumentKeydown:function(e){27===e.keyCode&&_.close(\"$escape\")},activate:function(e){e.data(\"$ngDialogOptions\").trapFocus&&(e.on(\"keydown\",$.onTrapFocusKeydown),b.body.on(\"keydown\",$.onTrapFocusKeydown))},deactivate:function(e){e.off(\"keydown\",$.onTrapFocusKeydown),b.body.off(\"keydown\",$.onTrapFocusKeydown)},deactivateAll:function(e){O.forEach(e,function(e){var t=O.element(e);$.deactivate(t)})},setBodyPadding:function(e){var t=parseInt(b.body.css(\"padding-right\")||0,10);b.body.css(\"padding-right\",t+e+\"px\"),b.body.data(\"ng-dialog-original-padding\",t),m.$broadcast(\"ngDialog.setPadding\",e)},resetBodyPadding:function(){var e=b.body.data(\"ng-dialog-original-padding\");e?b.body.css(\"padding-right\",e+\"px\"):b.body.css(\"padding-right\",\"\"),m.$broadcast(\"ngDialog.setPadding\",0)},performCloseDialog:function(e,t){var n=e.data(\"$ngDialogOptions\"),r=e.attr(\"id\"),i=A[r];if(i){if(void 0!==y.Hammer){var o=i.hammerTime;o.off(\"tap\",C),o.destroy&&o.destroy(),delete i.hammerTime}else e.unbind(\"click\");1===D&&b.body.unbind(\"keydown\",$.onDocumentKeydown),e.hasClass(\"ngdialog-closing\")||(D-=1);var a=e.data(\"$ngDialogPreviousFocus\");a&&a.focus&&a.focus(),m.$broadcast(\"ngDialog.closing\",e,t),D=D<0?0:D,l&&!n.disableAnimation?(i.$destroy(),e.unbind(c).bind(c,function(){$.closeDialogElement(e,t)}).addClass(\"ngdialog-closing\")):(i.$destroy(),$.closeDialogElement(e,t)),x[r]&&(x[r].resolve({id:r,value:t,$dialog:e,remainingDialogs:D}),delete x[r]),A[r]&&delete A[r],E.splice(E.indexOf(r),1),E.length||(b.body.unbind(\"keydown\",$.onDocumentKeydown),P=!1)}},closeDialogElement:function(e,t){var n=e.data(\"$ngDialogOptions\");e.remove(),0===D&&(b.html.removeClass(n.bodyClassName),b.body.removeClass(n.bodyClassName),$.resetBodyPadding()),m.$broadcast(\"ngDialog.closed\",e,t)},closeDialog:function(e,t){var n=e.data(\"$ngDialogPreCloseCallback\");if(n&&O.isFunction(n)){var r=n.call(e,t);if(O.isObject(r))r.closePromise?r.closePromise.then(function(){$.performCloseDialog(e,t)},function(){return!1}):r.then(function(){$.performCloseDialog(e,t)},function(){return!1});else{if(!1===r)return!1;$.performCloseDialog(e,t)}}else $.performCloseDialog(e,t)},onTrapFocusKeydown:function(e){var t,n=O.element(e.currentTarget);if(n.hasClass(\"ngdialog\"))t=n;else if(null===(t=$.getActiveDialog()))return;var r=9===e.keyCode,i=!0===e.shiftKey;r&&$.handleTab(t,e,i)},handleTab:function(e,t,n){var r=$.getFocusableElements(e);if(0!==r.length){var i=document.activeElement,o=Array.prototype.indexOf.call(r,i),a=-1===o,s=0===o,u=o===r.length-1,l=!1;n?(a||s)&&(r[r.length-1].focus(),l=!0):(a||u)&&(r[0].focus(),l=!0),l&&(t.preventDefault(),t.stopPropagation())}else document.activeElement&&document.activeElement.blur&&document.activeElement.blur()},autoFocus:function(e){var t=e[0],n=t.querySelector(\"*[autofocus]\");if(null===n||(n.focus(),document.activeElement!==n)){var r=$.getFocusableElements(e);if(0<r.length)r[0].focus();else{var i=$.filterVisibleElements(t.querySelectorAll(\"h1,h2,h3,h4,h5,h6,p,span\"));if(0<i.length){var o=i[0];T(o).attr(\"tabindex\",\"-1\").css(\"outline\",\"0\"),o.focus()}}}},getFocusableElements:function(e){var t=e[0].querySelectorAll(\"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]\"),n=$.filterTabbableElements(t);return $.filterVisibleElements(n)},filterTabbableElements:function(e){for(var t=[],n=0;n<e.length;n++){var r=e[n];\"-1\"!==T(r).attr(\"tabindex\")&&t.push(r)}return t},filterVisibleElements:function(e){for(var t=[],n=0;n<e.length;n++){var r=e[n];(0<r.offsetWidth||0<r.offsetHeight)&&t.push(r)}return t},getActiveDialog:function(){var e=document.querySelectorAll(\".ngdialog\");return 0===e.length?null:T(e[e.length-1])},applyAriaAttributes:function(e,t){if(t.ariaAuto){if(!t.ariaRole){var n=0<$.getFocusableElements(e).length?\"dialog\":\"alertdialog\";t.ariaRole=n}t.ariaLabelledBySelector||(t.ariaLabelledBySelector=\"h1,h2,h3,h4,h5,h6\"),t.ariaDescribedBySelector||(t.ariaDescribedBySelector=\"article,section,p\")}t.ariaRole&&e.attr(\"role\",t.ariaRole),$.applyAriaAttribute(e,\"aria-labelledby\",t.ariaLabelledById,t.ariaLabelledBySelector),$.applyAriaAttribute(e,\"aria-describedby\",t.ariaDescribedById,t.ariaDescribedBySelector)},applyAriaAttribute:function(e,t,n,r){if(n&&e.attr(t,n),r){var i=e.attr(\"id\"),o=e[0].querySelector(r);if(!o)return;var a=i+\"-\"+t;return T(o).attr(\"id\",a),e.attr(t,a),a}},detectUIRouter:function(){try{return O.module(\"ui.router\"),!0}catch(e){return!1}},getRouterLocationEventName:function(){return $.detectUIRouter()?\"$stateChangeStart\":\"$locationChangeStart\"}},_={__PRIVATE__:$,open:function(e){var l=null;if(e=e||{},!(M&&e.name&&(l=e.name.toLowerCase().replace(/\\s/g,\"-\")+\"-dialog\",this.isOpen(l)))){var t,c,d,p,h,f=O.copy(S),n=++k;l=l||\"ngdialog\"+n,E.push(l),void 0!==f.data&&(void 0===e.data&&(e.data={}),e.data=O.merge(O.copy(f.data),e.data)),O.extend(f,e),x[l]=t=a.defer(),A[l]=c=O.isObject(f.scope)?f.scope.$new():m.$new();var r=O.extend({},f.resolve);return O.forEach(r,function(e,t){r[t]=O.isString(e)?u.get(e):u.invoke(e,null,null,t)}),a.all({template:function(e){if(!e)return\"Empty template\";if(O.isString(e)&&f.plain)return e;return\"boolean\"!=typeof f.cache||f.cache?i(e,{cache:o}):i(e,{cache:!1})}(f.template||f.templateUrl),locals:a.all(r)}).then(function(e){var t=e.template,n=e.locals;f.showClose&&(t+='<div class=\"ngdialog-close\"></div>');var r,i=f.overlay?\"\":\" ngdialog-no-overlay\";if((d=T('<div id=\"'+l+'\" class=\"ngdialog'+i+'\"></div>')).html(f.overlay?'<div class=\"ngdialog-overlay\"></div><div class=\"ngdialog-content\" role=\"document\">'+t+\"</div>\":'<div class=\"ngdialog-content\" role=\"document\">'+t+\"</div>\"),d.data(\"$ngDialogOptions\",f),c.ngDialogId=l,f.data&&O.isString(f.data)){var o=f.data.replace(/^\\s*/,\"\")[0];c.ngDialogData=\"{\"===o||\"[\"===o?O.fromJson(f.data):new String(f.data),c.ngDialogData.ngDialogId=l}else f.data&&O.isObject(f.data)&&(c.ngDialogData=f.data,c.ngDialogData.ngDialogId=l);(f.className&&d.addClass(f.className),f.appendClassName&&d.addClass(f.appendClassName),f.width&&(h=d[0].querySelector(\".ngdialog-content\"),O.isString(f.width)?h.style.width=f.width:h.style.width=f.width+\"px\"),f.height&&(h=d[0].querySelector(\".ngdialog-content\"),O.isString(f.height)?h.style.height=f.height:h.style.height=f.height+\"px\"),f.disableAnimation&&d.addClass(\"ngdialog-disabled-animation\"),p=f.appendTo&&O.isString(f.appendTo)?O.element(document.querySelector(f.appendTo)):b.body,$.applyAriaAttributes(d,f),f.preCloseCallback)&&(O.isFunction(f.preCloseCallback)?r=f.preCloseCallback:O.isString(f.preCloseCallback)&&c&&(O.isFunction(c[f.preCloseCallback])?r=c[f.preCloseCallback]:c.$parent&&O.isFunction(c.$parent[f.preCloseCallback])?r=c.$parent[f.preCloseCallback]:m&&O.isFunction(m[f.preCloseCallback])&&(r=m[f.preCloseCallback])),r&&d.data(\"$ngDialogPreCloseCallback\",r));if(c.closeThisDialog=function(e){$.closeDialog(d,e)},f.controller&&(O.isString(f.controller)||O.isArray(f.controller)||O.isFunction(f.controller))){var a;f.controllerAs&&O.isString(f.controllerAs)&&(a=f.controllerAs);var s=w(f.controller,O.extend(n,{$scope:c,$element:d}),!0,a);f.bindToController&&O.extend(s.instance,{ngDialogId:c.ngDialogId,ngDialogData:c.ngDialogData,closeThisDialog:c.closeThisDialog,confirm:c.confirm}),\"function\"==typeof s?d.data(\"$ngDialogControllerController\",s()):d.data(\"$ngDialogControllerController\",s)}if(v(function(){var e=document.querySelectorAll(\".ngdialog\");$.deactivateAll(e),g(d)(c);var t=y.innerWidth-b.body.prop(\"clientWidth\");b.html.addClass(f.bodyClassName),b.body.addClass(f.bodyClassName);var n=t-(y.innerWidth-b.body.prop(\"clientWidth\"));0<n&&$.setBodyPadding(n),p.append(d),$.activate(d),f.trapFocus&&$.autoFocus(d),f.name?m.$broadcast(\"ngDialog.opened\",{dialog:d,name:f.name}):m.$broadcast(\"ngDialog.opened\",d)}),P||(b.body.bind(\"keydown\",$.onDocumentKeydown),P=!0),f.closeByNavigation){var u=$.getRouterLocationEventName();m.$on(u,function(e){!1===$.closeDialog(d)&&e.preventDefault()})}(f.preserveFocus&&d.data(\"$ngDialogPreviousFocus\",document.activeElement),C=function(e){var t=!!f.closeByDocument&&T(e.target).hasClass(\"ngdialog-overlay\"),n=T(e.target).hasClass(\"ngdialog-close\");(t||n)&&_.close(d.attr(\"id\"),n?\"$closeButton\":\"$document\")},void 0!==y.Hammer)?(c.hammerTime=y.Hammer(d[0])).on(\"tap\",C):d.bind(\"click\",C);return D+=1,_}),{id:l,closePromise:t.promise,close:function(e){$.closeDialog(d,e)}}}function i(t,e){return(e=e||{}).headers=e.headers||{},O.extend(e.headers,{Accept:\"text/html\"}),m.$broadcast(\"ngDialog.templateLoading\",t),s.get(t,e).then(function(e){return m.$broadcast(\"ngDialog.templateLoaded\",t),e.data||\"\"})}},openConfirm:function(e){var n=a.defer(),t=O.copy(S);e=e||{},void 0!==t.data&&(void 0===e.data&&(e.data={}),e.data=O.merge(O.copy(t.data),e.data)),O.extend(t,e),t.scope=O.isObject(t.scope)?t.scope.$new():m.$new(),t.scope.confirm=function(e){n.resolve(e);var t=T(document.getElementById(r.id));$.performCloseDialog(t,e)};var r=_.open(t);if(r)return r.closePromise.then(function(e){return e?n.reject(e.value):n.reject()}),n.promise},isOpen:function(e){return 0<T(document.getElementById(e)).length},close:function(e,t){var n=T(document.getElementById(e));if(n.length)$.closeDialog(n,t);else if(\"$escape\"===e){var r=E[E.length-1];(n=T(document.getElementById(r))).data(\"$ngDialogOptions\").closeByEscape&&$.closeDialog(n,\"$escape\")}else _.closeAll(t);return _},closeAll:function(e){for(var t=document.querySelectorAll(\".ngdialog\"),n=t.length-1;0<=n;n--){var r=t[n];$.closeDialog(T(r),e)}},getOpenDialogs:function(){return E},getDefaults:function(){return S}};return O.forEach([\"html\",\"body\"],function(e){if(b[e]=n.find(e),r[e]){var t=$.getRouterLocationEventName();m.$on(t,function(){b[e]=n.find(e)})}}),_}]}),e.directive(\"ngDialog\",[\"ngDialog\",function(o){return{restrict:\"A\",scope:{ngDialogScope:\"=\"},link:function(r,e,i){e.on(\"click\",function(e){e.preventDefault();var t=O.isDefined(r.ngDialogScope)?r.ngDialogScope:\"noScope\";O.isDefined(i.ngDialogClosePrevious)&&o.close(i.ngDialogClosePrevious);var n=o.getDefaults();o.open({template:i.ngDialog,className:i.ngDialogClass||n.className,appendClassName:i.ngDialogAppendClass,controller:i.ngDialogController,controllerAs:i.ngDialogControllerAs,bindToController:i.ngDialogBindToController,disableAnimation:i.ngDialogDisableAnimation,scope:t,data:i.ngDialogData,showClose:\"false\"!==i.ngDialogShowClose&&(\"true\"===i.ngDialogShowClose||n.showClose),closeByDocument:\"false\"!==i.ngDialogCloseByDocument&&(\"true\"===i.ngDialogCloseByDocument||n.closeByDocument),closeByEscape:\"false\"!==i.ngDialogCloseByEscape&&(\"true\"===i.ngDialogCloseByEscape||n.closeByEscape),overlay:\"false\"!==i.ngDialogOverlay&&(\"true\"===i.ngDialogOverlay||n.overlay),preCloseCallback:i.ngDialogPreCloseCallback||n.preCloseCallback,bodyClassName:i.ngDialogBodyClass||n.bodyClassName})})}}}]),e}),angular.module(\"ui-notification\",[]),angular.module(\"ui-notification\").provider(\"Notification\",function(){this.options={delay:5e3,startTop:10,startRight:10,verticalSpacing:10,horizontalSpacing:10,positionX:\"right\",positionY:\"top\",replaceMessage:!1,templateUrl:\"angular-ui-notification.html\",onClose:void 0,closeOnClick:!0,maxCount:0,container:\"body\",priority:10},this.setOptions=function(e){if(!angular.isObject(e))throw new Error(\"Options should be an object!\");this.options=angular.extend({},this.options,e)},this.$get=[\"$timeout\",\"$http\",\"$compile\",\"$templateCache\",\"$rootScope\",\"$injector\",\"$sce\",\"$q\",\"$window\",function(l,r,c,i,o,e,f,a,g){var m=this.options,v=m.startTop,y=m.startRight,w=m.verticalSpacing,b=m.horizontalSpacing,s=m.delay,$=[],_=!1,t=function(h,e){function t(e){function t(t){[\"-webkit-transition\",\"-o-transition\",\"transition\"].forEach(function(e){i.css(e,t)})}var n=h.scope.$new();n.message=f.trustAsHtml(h.message),n.title=f.trustAsHtml(h.title),n.t=h.type.substr(0,1),n.delay=h.delay,n.onClose=h.onClose;var d=function(e,t){return e._priority-t._priority},p=function(e,t){return t._priority-e._priority},r=function(){var e=0,t=0,n=y,r=[];\"top\"===h.positionY?$.sort(d):\"bottom\"===h.positionY&&$.sort(p);for(var i=$.length-1;0<=i;i--){var o=$[i];if(h.replaceMessage&&i<$.length-1)o.addClass(\"killed\");else{var a=parseInt(o[0].offsetHeight),s=parseInt(o[0].offsetWidth),u=r[o._positionY+o._positionX];l+a>window.innerHeight&&(u=v,t++,e=0);var l=u?0===e?u:u+w:v,c=n+t*(b+s);o.css(o._positionY,l+\"px\"),\"center\"==o._positionX?o.css(\"left\",parseInt(window.innerWidth/2-s/2)+\"px\"):o.css(o._positionX,c+\"px\"),r[o._positionY+o._positionX]=l+a,0<m.maxCount&&$.length>m.maxCount&&0===i&&o.scope().kill(!0),e++}}},i=c(e)(n);i._positionY=h.positionY,i._positionX=h.positionX,i._priority=h.priority,i.addClass(h.type);var o=function(e){(\"click\"===(e=e.originalEvent||e).type||\"opacity\"===e.propertyName&&1<=e.elapsedTime)&&(n.onClose&&n.$apply(n.onClose(i)),i.remove(),$.splice($.indexOf(i),1),n.$destroy(),r())};h.closeOnClick&&(i.addClass(\"clickable\"),i.bind(\"click\",o)),i.bind(\"webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd\",o),angular.isNumber(h.delay)&&l(function(){i.addClass(\"killed\")},h.delay),t(\"none\"),angular.element(document.querySelector(h.container)).append(i);var a=-(parseInt(i[0].offsetHeight)+50);if(i.css(i._positionY,a+\"px\"),$.push(i),\"center\"==h.positionX){var s=parseInt(i[0].offsetWidth);i.css(\"left\",parseInt(window.innerWidth/2-s/2)+\"px\")}l(function(){t(\"\")}),n._templateElement=i,n.kill=function(e){e?(n.onClose&&n.$apply(n.onClose(n._templateElement)),$.splice($.indexOf(n._templateElement),1),n._templateElement.remove(),n.$destroy(),l(r)):n._templateElement.addClass(\"killed\")},l(r),_||(angular.element(g).bind(\"resize\",function(e){l(r)}),_=!0),u.resolve(n)}var u=a.defer();\"object\"==typeof h&&null!==h||(h={message:h}),h.scope=h.scope?h.scope:o,h.template=h.templateUrl?h.templateUrl:m.templateUrl,h.delay=angular.isUndefined(h.delay)?s:h.delay,h.type=e||h.type||m.type||\"\",h.positionY=h.positionY?h.positionY:m.positionY,h.positionX=h.positionX?h.positionX:m.positionX,h.replaceMessage=h.replaceMessage?h.replaceMessage:m.replaceMessage,h.onClose=h.onClose?h.onClose:m.onClose,h.closeOnClick=null!==h.closeOnClick&&void 0!==h.closeOnClick?h.closeOnClick:m.closeOnClick,h.container=h.container?h.container:m.container,h.priority=h.priority?h.priority:m.priority;var n=i.get(h.template);return n?t(n):r.get(h.template,{cache:!0}).then(function(e){t(e.data)}).catch(function(e){throw new Error(\"Template (\"+h.template+\") could not be loaded. \"+e)}),u.promise};return t.primary=function(e){return this(e,\"primary\")},t.error=function(e){return this(e,\"error\")},t.success=function(e){return this(e,\"success\")},t.info=function(e){return this(e,\"info\")},t.warning=function(e){return this(e,\"warning\")},t.clearAll=function(){angular.forEach($,function(e){e.addClass(\"killed\")})},t}]}),angular.module(\"ui-notification\").run([\"$templateCache\",function(e){e.put(\"angular-ui-notification.html\",'<div class=\"ui-notification\"><h3 ng-show=\"title\" ng-bind-html=\"title\"></h3><div class=\"message\" ng-bind-html=\"message\"></div></div>')}]),function(){var g=\"__default\";angular.module(\"angularUtils.directives.dirPagination\",[]).directive(\"dirPaginate\",[\"$compile\",\"$parse\",\"paginationService\",function(l,c,d){return{terminal:!0,multiElement:!0,priority:100,compile:function(e,t){var s=t.dirPaginate,n=s.match(/^\\s*([\\s\\S]+?)\\s+in\\s+([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?(?:\\s+track\\s+by\\s+([\\s\\S]+?))?\\s*$/),r=/\\|\\s*itemsPerPage\\s*:\\s*(.*\\(\\s*\\w*\\)|([^\\)]*?(?=\\s+as\\s+))|[^\\)]*)/;if(null===n[2].match(r))throw\"pagination directive: the 'itemsPerPage' filter must be set.\";var i=n[2].replace(r,\"\"),u=c(i);!function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).attr(\"dir-paginate-no-compile\",!0)})}(e);var o=t.paginationId||g;return d.registerInstance(o),function(e,t,n){var r=c(n.paginationId)(e)||n.paginationId||g;d.registerInstance(r);var i=function(e,t){var n=!!e.match(/(\\|\\s*itemsPerPage\\s*:[^|]*:[^|]*)/);return t===g||n?e:e.replace(/(\\|\\s*itemsPerPage\\s*:\\s*[^|\\s]*)/,\"$1 : '\"+t+\"'\")}(s,r);!function(e,t,n){e[0].hasAttribute(\"dir-paginate-start\")||e[0].hasAttribute(\"data-dir-paginate-start\")?(t.$set(\"ngRepeatStart\",n),e.eq(e.length-1).attr(\"ng-repeat-end\",!0)):t.$set(\"ngRepeat\",n)}(t,n,i),function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).removeAttr(\"dir-paginate-no-compile\")}),e.eq(0).removeAttr(\"dir-paginate-start\").removeAttr(\"dir-paginate\").removeAttr(\"data-dir-paginate-start\").removeAttr(\"data-dir-paginate\"),e.eq(e.length-1).removeAttr(\"dir-paginate-end\").removeAttr(\"data-dir-paginate-end\")}(t);var o=l(t),a=function(e,t,n){var r;if(t.currentPage)r=c(t.currentPage);else{var i=(n+\"__currentPage\").replace(/\\W/g,\"_\");e[i]=1,r=c(i)}return r}(e,n,r);d.setCurrentPageParser(r,a,e),void 0!==n.totalItems?(d.setAsyncModeTrue(r),e.$watch(function(){return c(n.totalItems)(e)},function(e){0<=e&&d.setCollectionLength(r,e)})):(d.setAsyncModeFalse(r),e.$watchCollection(function(){return u(e)},function(e){if(e){var t=e instanceof Array?e.length:Object.keys(e).length;d.setCollectionLength(r,t)}})),o(e)}}}}]).directive(\"dirPaginateNoCompile\",function(){return{priority:5e3,terminal:!0}}).directive(\"dirPaginationControls\",[\"paginationService\",\"paginationTemplate\",function(d,n){var p=/^\\d+$/,e={restrict:\"AE\",scope:{maxSize:\"=?\",onPageChange:\"&?\",paginationId:\"=?\",autoHide:\"=?\"},link:function(r,e,t){var n=t.paginationId||g,i=r.paginationId||t.paginationId||g;if(!d.isRegistered(i)&&!d.isRegistered(n)){var o=i!==g?\" (id: \"+i+\") \":\" \";window.console&&console.warn(\"Pagination directive: the pagination controls\"+o+\"cannot be used without the corresponding pagination directive, which was not found at link time.\")}r.maxSize||(r.maxSize=9);r.autoHide=void 0===r.autoHide||r.autoHide,r.directionLinks=!angular.isDefined(t.directionLinks)||r.$parent.$eval(t.directionLinks),r.boundaryLinks=!!angular.isDefined(t.boundaryLinks)&&r.$parent.$eval(t.boundaryLinks);var a=Math.max(r.maxSize,5);function s(e){if(d.isRegistered(i)&&c(e)){var t=r.pagination.current;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,l(),r.onPageChange&&r.onPageChange({newPageNumber:e,oldPageNumber:t})}}function u(){if(d.isRegistered(i)){var e=parseInt(d.getCurrentPage(i))||1;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,r.pagination.last=r.pages[r.pages.length-1],r.pagination.last<r.pagination.current?r.setCurrent(r.pagination.last):l()}}function l(){if(d.isRegistered(i)){var e=d.getCurrentPage(i),t=d.getItemsPerPage(i),n=d.getCollectionLength(i);r.range.lower=(e-1)*t+1,r.range.upper=Math.min(e*t,n),r.range.total=n}}function c(e){return p.test(e)&&0<e&&e<=r.pagination.last}r.pages=[],r.pagination={last:1,current:1},r.range={lower:1,upper:1,total:1},r.$watch(\"maxSize\",function(e){e&&(a=Math.max(r.maxSize,5),u())}),r.$watch(function(){if(d.isRegistered(i))return(d.getCollectionLength(i)+1)*d.getItemsPerPage(i)},function(e){0<e&&u()}),r.$watch(function(){if(d.isRegistered(i))return d.getItemsPerPage(i)},function(e,t){e!=t&&void 0!==t&&s(r.pagination.current)}),r.$watch(function(){if(d.isRegistered(i))return d.getCurrentPage(i)},function(e,t){e!=t&&s(e)}),r.setCurrent=function(e){d.isRegistered(i)&&c(e)&&(e=parseInt(e,10),d.setCurrentPage(i,e))},r.tracker=function(e,t){return e+\"_\"+t}}},t=n.getString();void 0!==t?e.template=t:e.templateUrl=function(e,t){return t.templateUrl||n.getPath()};return e;function h(e,t,n,r){var i,o=[],a=Math.ceil(t/n),s=Math.ceil(r/2);i=e<=s?\"start\":a-s<e?\"end\":\"middle\";for(var u=r<a,l=1;l<=a&&l<=r;){var c=f(l,e,r,a),d=2===l&&(\"middle\"===i||\"end\"===i),p=l===r-1&&(\"middle\"===i||\"start\"===i);u&&(d||p)?o.push(\"...\"):o.push(c),l++}return o}function f(e,t,n,r){var i=Math.ceil(n/2);return e===n?r:1===e?e:n<r?r-i<t?r-n+e:i<t?t-i+e:e:e}}]).filter(\"itemsPerPage\",[\"paginationService\",function(a){return function(t,e,n){if(void 0===n&&(n=g),!a.isRegistered(n))throw\"pagination directive: the itemsPerPage id argument (id: \"+n+\") does not match a registered pagination-id.\";var r,i;if(angular.isObject(t)){if(e=parseInt(e)||9999999999,i=a.isAsyncMode(n)?0:(a.getCurrentPage(n)-1)*e,r=i+e,a.setItemsPerPage(n,e),t instanceof Array)return t.slice(i,r);var o={};return angular.forEach(function(e){{if(Object.keys)return Object.keys(e);var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}}(t).slice(i,r),function(e){o[e]=t[e]}),o}return t}}]).service(\"paginationService\",function(){var t,r={};this.registerInstance=function(e){void 0===r[e]&&(r[e]={asyncMode:!1},t=e)},this.deregisterInstance=function(e){delete r[e]},this.isRegistered=function(e){return void 0!==r[e]},this.getLastInstanceId=function(){return t},this.setCurrentPageParser=function(e,t,n){r[e].currentPageParser=t,r[e].context=n},this.setCurrentPage=function(e,t){r[e].currentPageParser.assign(r[e].context,t)},this.getCurrentPage=function(e){var t=r[e].currentPageParser;return t?t(r[e].context):1},this.setItemsPerPage=function(e,t){r[e].itemsPerPage=t},this.getItemsPerPage=function(e){return r[e].itemsPerPage},this.setCollectionLength=function(e,t){r[e].collectionLength=t},this.getCollectionLength=function(e){return r[e].collectionLength},this.setAsyncModeTrue=function(e){r[e].asyncMode=!0},this.setAsyncModeFalse=function(e){r[e].asyncMode=!1},this.isAsyncMode=function(e){return r[e].asyncMode}}).provider(\"paginationTemplate\",function(){var t,n=\"angularUtils.directives.dirPagination.template\";this.setPath=function(e){n=e},this.setString=function(e){t=e},this.$get=function(){return{getPath:function(){return n},getString:function(){return t}}}}).run([\"$templateCache\",function(e){e.put(\"angularUtils.directives.dirPagination.template\",'<ul class=\"pagination\" ng-if=\"1 < pages.length || !autoHide\"><li ng-if=\"boundaryLinks\" ng-class=\"{ disabled : pagination.current == 1 }\"><a href=\"\" ng-click=\"setCurrent(1)\">&laquo;</a></li><li ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == 1 }\"><a href=\"\" ng-click=\"setCurrent(pagination.current - 1)\">&lsaquo;</a></li><li ng-repeat=\"pageNumber in pages track by tracker(pageNumber, $index)\" ng-class=\"{ active : pagination.current == pageNumber, disabled : pageNumber == \\'...\\' || ( ! autoHide && pages.length === 1 ) }\"><a href=\"\" ng-click=\"setCurrent(pageNumber)\">{{ pageNumber }}</a></li><li ng-if=\"directionLinks\" ng-class=\"{ disabled : pagination.current == pagination.last }\"><a href=\"\" ng-click=\"setCurrent(pagination.current + 1)\">&rsaquo;</a></li><li ng-if=\"boundaryLinks\"  ng-class=\"{ disabled : pagination.current == pagination.last }\"><a href=\"\" ng-click=\"setCurrent(pagination.last)\">&raquo;</a></li></ul>')}])}();var com_github_culmat_jsTreeTable=function(){function u(e,r,i){return i=i||\"children\",$.each(e,function(e,t){!function n(e){e[i]&&$.each(e[i],function(e,t){n(t)}),r(e)}(t)}),e}function t(e,n,o,a){var t=e;n=n||\"id\",o=o||\"parent\",a=a||\"children\";var s=[];$.each(t,function(e,t){s[t[n]]=t});var u=[];return $.each(t,function(e,r){var t=r[o];if($.isArray(t)||(t=[t]),0==t.length)u.push(r);else{var i=!1;$.each(t,function(e,t){var n=s[t];n&&(n[a]||(n[a]=[]),$.inArray(r,n[a])<0&&n[a].push(r),i=!0)}),i||u.push(r)}}),u}function l(e,a,s,u,l,t){a=a||\"children\",s=s||\"id\",t=t||{};var n=0,r=$(\"<table>\");$.each(t,function(e,t){\"class\"==e&&\"jsTT\"!=t?r.addClass(t):r.attr(e,t)});var i=$(\"<thead>\"),o=$(\"<tr>\"),c=$(\"<tbody>\");return r.append(i),i.append(o),r.append(c),u?$.each(u,function(e,t){$(o).append($(\"<th>\"+t+\"</th>\"))}):($(o).append($(\"<th>\"+s+\"</th>\")),$.each(e[0],function(e,t){e!=a&&e!=s&&$(o).append($(\"<th>\"+e+\"</th>\"))})),function r(e,i,o,t){n=Math.max(n,o),$.each(e,function(e,n){n[\"data-tt-level\"]=o,function(n,e){var r=$(\"<tr>\");$(r).attr(\"data-tt-id\",n[s]),$(r).attr(\"data-tt-level\",n[\"data-tt-level\"]),n[a]&&0!=n[a].length?$(r).attr(\"data-tt-isnode\",!0):$(r).attr(\"data-tt-isleaf\",!0),e&&$(r).attr(\"data-tt-parent-id\",e[s]),l?l($(r),n):u?$.each(u,function(e,t){$(r).append($(\"<td>\"+n[e]+\"</td>\"))}):($(r).append($(\"<td>\"+n[s]+\"</td>\")),$.each(n,function(e,t){e!=a&&e!=s&&\"data-tt-level\"!=e&&$(r).append($(\"<td>\"+t+\"</td>\"))})),c.append(r)}(n,t),n[i]&&$.each(n[i],function(e,t){r([t],i,o+1,n)})})}(e,a,1),e[0]&&(e[0].maxLevel=n),r}function n(e,t){return $.each(e,function(e,n){$.each(t,function(e,t){n[t]=$(n).attr(t)})}),e}function c(i){i.addClass(\"jsTT\"),i.expandLevel=function(n){$(\"tr[data-tt-level]\",i).each(function(e){var t=parseInt($(this).attr(\"data-tt-level\"));n-1<t?this.trCollapse(!0):t==n-1&&this.trExpand(!0)})},$(\"tr[data-tt-id]\",i).each(function(e,t){(t=$(t)).attr(\"data-tt-level\",function e(t){var n=t.attr(\"data-tt-level\");if(null!=n)return parseInt(n);var r=t.attr(\"data-tt-parent-id\");return null==r?0:e($('tr[data-tt-id=\"'+r+'\"]',i).first())+1}(t))});var e=$(\"tr[data-tt-level]\",i).get();$.each(e,function(e,t){t.trChildrenVisible=!0,t.trChildren=[]}),e=t(e=n(e,[\"data-tt-id\",\"data-tt-parent-id\"]),\"data-tt-id\",\"data-tt-parent-id\",\"trChildren\");var o=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC\";return $(\"tr[data-tt-level]\",i).each(function(e,t){var n=$(t).attr(\"data-tt-level\"),r=$(\"td\",t).first();0<t.trChildren.length?r.prepend($('<img id=\"state\" style=\"cursor:pointer\" src=\"'+o+'\"/>')):r.prepend($('<span style=\"padding-left:16px;\" /></span>')),r.prepend($('<span style=\"padding-left:'+15*parseInt(n-1)+'px;\" /></span>')),t.trExpand=function(e){if(!(this.trChildren.length<1)){e&&(this.trChildrenVisible=!0,$(\"#state\",this).get(0).src=o);var n=e||this.trChildrenVisible;$.each(this.trChildren,function(e,t){n&&$(t).css(\"display\",\"table-row\"),t.trExpand()})}},t.trCollapse=function(e){this.trChildren.length<1||(e&&(this.trChildrenVisible=!1,$(\"#state\",this).get(0).src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=\"),$.each(this.trChildren,function(e,t){$(t).css(\"display\",\"none\"),t.trCollapse()}))},$(t).click(function(){this.trChildrenVisible?this.trCollapse(!0):this.trExpand(!0)})}),i}return{depthFirst:u,makeTree:t,renderTree:l,attr2attr:n,treeTable:c,appendTreetable:function(e,t){(t=t||{}).idAttr=t.idAttr||\"id\",t.childrenAttr=t.childrenAttr||\"children\";var n=t.controls||[];t.mountPoint||(t.mountPoint=$(\"body\")),t.depthFirst&&u(e,t.depthFirst,t.childrenAttr);var r=l(e,t.childrenAttr,t.idAttr,t.renderedAttr,t.renderer,t.tableAttributes);c(r),t.replaceContent&&t.mountPoint.html(\"\");var i,o,a=t.initialExpandLevel?parseInt(t.initialExpandLevel):-1;if(a=Math.min(a,e[0].maxLevel),r.expandLevel(a),t.slider){var s=$('<div style=\"margin-right: 15px;\">');s.width(\"200px\"),s.slider({min:1,max:e[0].maxLevel,range:\"min\",value:a,slide:function(e,t){r.expandLevel(t.value)}}),n=[s].concat(t.controls)}return 0<n.length&&t.mountPoint.append((i=n,o=$(\"<tr>\"),$.each(i,function(e,t){o.append($('<td style=\"padding-right: 20px;\">').append(t))}),$('<table border=\"0\"/>').append(o))),t.mountPoint.append(r),r},jsTreeTable:\"1.0\",register:function(n){$.each(this,function(e,t){\"register\"!=e&&(n[e]=t)})}}}();"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/gulpfile.js",
    "content": "const gulp = require('gulp');\nconst plugins = require('gulp-load-plugins')();\nconst open = require('open');\nconst app = {\n  srcPath: 'app/', // 源代码\n  devPath: 'tmp/', // 开发打包\n  prdPath: 'dist/' // 生产打包\n};\n\nconst JS_LIBS = [\n  'node_modules/angular-ui-router/release/angular-ui-router.js',\n  'node_modules/oclazyload/dist/ocLazyLoad.min.js',\n  'node_modules/angular-loading-bar/build/loading-bar.min.js',\n  'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js',\n  'node_modules/moment/moment.js',\n  'node_modules/angular-date-time-input/src/dateTimeInput.js',\n  'node_modules/angularjs-bootstrap-datetimepicker/src/js/datetimepicker.js',\n  'node_modules/angular-table-resize/dist/angular-table-resize.min.js',\n  'node_modules/angular-clipboard/angular-clipboard.js',\n  'node_modules/selectize/dist/js/standalone/selectize.js',\n  'node_modules/angular-selectize2/dist/selectize.js',\n  'node_modules/bootstrap-switch/dist/js/bootstrap-switch.min.js',\n  'node_modules/ng-dialog/js/ngDialog.js',\n  'node_modules/angular-ui-notification/dist/angular-ui-notification.min.js',\n  'node_modules/angular-utils-pagination/dirPagination.js',\n  'app/scripts/libs/treeTable.js',\n];\n\nconst CSS_APP = [\n  'node_modules/angular-loading-bar/build/loading-bar.min.css',\n  'node_modules/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css',\n  'node_modules/ng-dialog/css/ngDialog.min.css',\n  'node_modules/ng-dialog/css/ngDialog-theme-default.css',\n  'node_modules/angularjs-bootstrap-datetimepicker/src/css/datetimepicker.css',\n  'node_modules/angular-ui-notification/dist/angular-ui-notification.min.css',\n  'node_modules/angular-table-resize/dist/angular-table-resize.css',\n  'node_modules/selectize/dist/css/selectize.css',\n  'app/styles/page.css',\n  'app/styles/timeline.css',\n  'app/styles/main.css'\n];\n\nconst JS_APP = [\n  'app/scripts/app.js',\n  'app/scripts/filters/filters.js',\n  'app/scripts/services/auth_service.js',\n  'app/scripts/services/appservice.js',\n  'app/scripts/services/flow_service_v1.js',\n  'app/scripts/services/flow_service_v2.js',\n  'app/scripts/services/degradeservice.js',\n  'app/scripts/services/systemservice.js',\n  'app/scripts/services/machineservice.js',\n  'app/scripts/services/identityservice.js',\n  'app/scripts/services/metricservice.js',\n  'app/scripts/services/param_flow_service.js',\n  'app/scripts/services/authority_service.js',\n  'app/scripts/services/cluster_state_service.js',\n];\n\ngulp.task('lib', function () {\n  gulp.src(JS_LIBS)\n    .pipe(plugins.concat('app.vendor.js'))\n    .pipe(gulp.dest(app.devPath + 'js'))\n    .pipe(plugins.uglify())\n    .pipe(gulp.dest(app.prdPath + 'js'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* css任务\n* 在src下创建style文件夹，里面存放less文件。 \n*/\ngulp.task('css', function () {\n  gulp.src(CSS_APP)\n    .pipe(plugins.concat('app.css'))\n    .pipe(gulp.dest(app.devPath + 'css'))\n    .pipe(plugins.cssmin())\n    .pipe(gulp.dest(app.prdPath + 'css'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* js任务\n* 在src目录下创建script文件夹，里面存放所有的js文件\n*/\ngulp.task('js', function () {\n  gulp.src(JS_APP)\n    .pipe(plugins.concat('app.js'))\n    .pipe(gulp.dest(app.devPath + 'js'))\n    .pipe(plugins.uglify())\n    .pipe(gulp.dest(app.prdPath + 'js'))\n    .pipe(plugins.connect.reload());\n});\n\n/*\n* js任务\n* 在src目录下创建script文件夹，里面存放所有的js文件\n*/\ngulp.task('jshint', function () {\n  gulp.src(JS_APP)\n    .pipe(plugins.jshint())\n    .pipe(plugins.jshint.reporter());\n});\n\n// 每次发布的时候，可能需要把之前目录内的内容清除，避免旧的文件对新的容有所影响。 需要在每次发布前删除dist和build目录\ngulp.task('clean', function () {\n  gulp.src([app.devPath, app.prdPath])\n    .pipe(plugins.clean());\n});\n\n// 总任务\ngulp.task('build', ['clean', 'jshint', 'lib', 'js', 'css']);\n\n// 服务\ngulp.task('serve', ['build'], function () {\n  plugins.connect.server({ //启动一个服务器\n    root: [app.devPath], // 服务器从哪个路径开始读取，默认从开发路径读取\n    livereload: true, // 自动刷新\n    port: 1234\n  });\n  // 打开浏览器\n  setTimeout(() => {\n    open('http://localhost:8080/index_dev.htm')\n  }, 200);\n  // 监听\n  gulp.watch(app.srcPath + '**/*.js', ['js']);\n  gulp.watch(app.srcPath + '**/*.css', ['css']);\n});\n\n// 定义default任务\ngulp.task('default', ['serve']);\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/index.htm",
    "content": "<!doctype html>\n<html class=\"no-js\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Sentinel Dashboard</title>\n  <meta name=\"description\" content=\"Sentinel 控制台,Sentinel Dashboard,Admin\">\n  <meta name=\"viewport\" content=\"width=device-width\">\n\n  <link rel=\"stylesheet\" href=\"./lib/css/bootstrap.min.css\" />\n  <link rel=\"stylesheet\" href=\"./lib/css/font-awesome.min.css\">\n  <link rel=\"stylesheet\" href=\"./dist/css/app.css\">\n</head>\n\n<body>\n  <div ng-app=\"sentinelDashboardApp\">\n    <div ui-view></div>\n  </div>\n  <script src=\"./lib/js/jquery.min.js\"></script>\n  <script src=\"./lib/js/bootstrap.min.js\"></script>\n  <script src=\"./lib/js/angular.min.js\"></script>\n  <script src=\"./lib/js/g2.min.js\"></script>\n\n  <script src=\"./dist/js/app.vendor.js\"></script>\n  <script src=\"./dist/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/index_dev.htm",
    "content": "<!doctype html>\n<html class=\"no-js\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Sentinel 控制台</title>\n  <meta name=\"description\" content=\"Sentinel 控制台,Dashboard,Admin\">\n  <meta name=\"viewport\" content=\"width=device-width\">\n\n  <link rel=\"stylesheet\" href=\"//cdn.bootcss.com/bootstrap/3.0.3/css/bootstrap.min.css\" />\n  <link rel=\"stylesheet\" href=\"//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css\">\n  <link rel=\"stylesheet\" href=\"http://localhost:1234/css/app.css\">\n</head>\n\n<body>\n  <div ng-app=\"sentinelDashboardApp\">\n    <div ui-view></div>\n  </div>\n  <script src=\"//cdn.bootcss.com/jquery/2.1.4/jquery.min.js\"></script>\n  <script src=\"//cdn.bootcss.com/bootstrap/3.0.3/js/bootstrap.min.js\"></script>\n  <script src=\"//cdn.bootcss.com/angular.js/1.4.8/angular.js\"></script>\n  <script src=\"//gw.alipayobjects.com/os/antv/pkg/_antv.g2-3.4.10/dist/g2.min.js\"></script>\n\n  <script src=\"http://localhost:1234/js/app.vendor.js\"></script>\n  <script src=\"http://localhost:1234/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/license-stat.csv",
    "content": "Type,Package,License\nnpm,angular,MIT License\nnpm,angular-animate,MIT License\nnpm,angular-bootstrap,MIT License\nnpm,angular-clipboard,MIT License\nnpm,angular-cookies,MIT License\nnpm,angular-date-time-input,MIT License\nnpm,angular-loading-bar,MIT License\nnpm,angular-mocks,MIT License\nnpm,angular-resource,MIT License\nnpm,angular-route,MIT License\nnpm,angular-selectize2,MIT License\nnpm,angular-table-resize,MIT License\nnpm,angular-touch,MIT License\nnpm,angular-ui-notification,MIT License\nnpm,angular-ui-router,MIT License\nnpm,angular-utils-pagination,MIT License\nnpm,angularjs-bootstrap-datetimepicker,MIT License\nnpm,bootstrap-switch,Apache License 2.0\nnpm,bootstrap-tagsinput,MIT License\nnpm,moment,MIT License\nnpm,ng-dialog,MIT License\nnpm,ng-tags-input,MIT License\nnpm,oclazyload,MIT License\nnpm,selectize,Apache License 2.0\nlib,jsTreeTable,MIT License"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/package.json",
    "content": "{\n  \"name\": \"sentinel-dashboard\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo no test case\",\n    \"build\": \"gulp build\",\n    \"start\": \"gulp\"\n  },\n  \"author\": \"x-cold <lxstart@outlook.com>\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"angular\": \"^1.4.8\",\n    \"angular-animate\": \"^1.4.0\",\n    \"angular-bootstrap\": \"^0.12.2\",\n    \"angular-clipboard\": \"^1.6.2\",\n    \"angular-cookies\": \"^1.4.0\",\n    \"angular-date-time-input\": \"^1.2.1\",\n    \"angular-loading-bar\": \"^0.9.0\",\n    \"angular-mocks\": \"^1.4.0\",\n    \"angular-resource\": \"^1.4.0\",\n    \"angular-route\": \"^1.4.0\",\n    \"angular-selectize2\": \"^v1.2.3\",\n    \"angular-table-resize\": \"^2.0.1\",\n    \"angular-touch\": \"^1.4.0\",\n    \"angular-ui-notification\": \"^0.3.6\",\n    \"angular-ui-router\": \"^1.0.18\",\n    \"angular-utils-pagination\": \"^0.11.1\",\n    \"angularjs-bootstrap-datetimepicker\": \"^1.1.4\",\n    \"bootstrap-switch\": \"^3.3.4\",\n    \"bootstrap-tagsinput\": \"~0.7.1\",\n    \"moment\": \"^2.12.0\",\n    \"ng-dialog\": \"^0.6.6\",\n    \"ng-tags-input\": \"~3.0.0\",\n    \"oclazyload\": \"^1.1.0\",\n    \"selectize\": \"^0.12.1\",\n    \"lodash\": \">=4.17.11\"\n  },\n  \"devDependencies\": {\n    \"gulp\": \"^3.9.1\",\n    \"gulp-clean\": \"^0.4.0\",\n    \"gulp-concat\": \"^2.6.1\",\n    \"gulp-connect\": \"^5.7.0\",\n    \"gulp-csscomb\": \"^3.0.8\",\n    \"gulp-cssmin\": \"^0.2.0\",\n    \"gulp-jshint\": \"^2.1.0\",\n    \"gulp-load-plugins\": \"^1.5.0\",\n    \"gulp-serv\": \"0.0.1\",\n    \"gulp-uglify\": \"^3.0.0\",\n    \"jshint\": \"^2.10.2\",\n    \"open\": \"0.0.5\",\n    \"source-map\": \"^0.7.3\"\n  }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfigTest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.config;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\n\npublic class DashboardConfigTest {\n    @Rule\n    public final EnvironmentVariables environmentVariables = new EnvironmentVariables();\n\n    @Test\n    public void testGetConfigStr() {\n        // clear cache\n        DashboardConfig.clearCache();\n\n        // if not set, return null\n        assertEquals(null, DashboardConfig.getConfigStr(\"a\"));\n\n        // test property\n        System.setProperty(\"a\", \"111\");\n        assertEquals(\"111\", DashboardConfig.getConfigStr(\"a\"));\n\n        // test env\n        environmentVariables.set(\"a\", \"222\");\n        // return value in cache\n        assertEquals(\"111\", DashboardConfig.getConfigStr(\"a\"));\n\n        // clear cache and then test\n        DashboardConfig.clearCache();\n        assertEquals(\"222\", DashboardConfig.getConfigStr(\"a\"));\n    }\n\n    @Test\n    public void testGetConfigInt() {\n        // clear cache\n        DashboardConfig.clearCache();\n\n        // default value\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(1, DashboardConfig.getConfigInt(\"t\", 1, 10));\n        \n        // property, wrong format\n        System.setProperty(\"t\", \"asdf\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        System.setProperty(\"t\", \"\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // min value\n        System.setProperty(\"t\", \"2\");\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        DashboardConfig.clearCache();\n        assertEquals(10, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, -1));\n        \n        // env\n        environmentVariables.set(\"t\", \"20\");\n        DashboardConfig.clearCache();\n        assertEquals(20, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // wrong format env var, but it will override property\n        environmentVariables.set(\"t\", \"20dddd\");\n        DashboardConfig.clearCache();\n        assertEquals(0, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        \n        // clear env, it will take property\n        environmentVariables.set(\"t\", \"\");\n        DashboardConfig.clearCache();\n        assertEquals(10, DashboardConfig.getConfigInt(\"t\", 0, 10));\n        DashboardConfig.clearCache();\n        assertEquals(2, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        \n        // enable cache\n        System.setProperty(\"t\", \"666\");\n        DashboardConfig.clearCache();\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        System.setProperty(\"t\", \"777\");\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n        System.setProperty(\"t\", \"555\");\n        assertEquals(666, DashboardConfig.getConfigInt(\"t\", 0, 1));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersionTest.java",
    "content": "package com.alibaba.csp.sentinel.dashboard.datasource.entity;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\npublic class SentinelVersionTest {\n    @Test\n    public void testEqual() {\n        assertEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0));\n        assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 2, 3));\n        assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0, \"\"));\n        assertEquals(new SentinelVersion(1, 0, 0, \"\"), new SentinelVersion(1, 0, 0, \"\"));\n        assertNotEquals(new SentinelVersion(1, 0, 0, \"\"), new SentinelVersion(1, 0, 0, null));\n        assertEquals(new SentinelVersion(1, 0, 0, null), new SentinelVersion(1, 0, 0, null));\n    }\n    \n    @Test\n    public void testGreater() {\n        assertTrue(new SentinelVersion(2, 0, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 1, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 0)));\n        assertTrue(new SentinelVersion(1, 1, 4).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 1, 0)));\n        assertFalse(new SentinelVersion(1, 1, 3).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 3)));\n        assertFalse(new SentinelVersion(1, 0, 0, \"\").greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 0, 1).greaterThan(new SentinelVersion(1, 0, 0)));\n        assertTrue(new SentinelVersion(1, 0, 1, \"a\").greaterThan(new SentinelVersion(1, 0, 0, \"b\")));\n        assertFalse(new SentinelVersion(1, 0, 0, \"b\").greaterThan(new SentinelVersion(1, 0, 0, \"a\")));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfoTest.java",
    "content": "/*\n * Copyright 1999-2019 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.Set;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\nimport static org.junit.Assert.*;\n\npublic class AppInfoTest {\n\n    @Test\n    public void testConcurrentGetMachines() throws Exception {\n        AppInfo appInfo = new AppInfo(\"testApp\");\n        appInfo.addMachine(genMachineInfo(\"hostName1\", \"10.18.129.91\"));\n        appInfo.addMachine(genMachineInfo(\"hostName2\", \"10.18.129.92\"));\n        Set<MachineInfo> machines = appInfo.getMachines();\n        new Thread(() -> {\n            try {\n                for (MachineInfo m : machines) {\n                    System.out.println(m);\n                    try {\n                        Thread.sleep(200);\n                    } catch (InterruptedException e) {\n                    }\n                }\n            } catch (ConcurrentModificationException e) {\n                e.printStackTrace();\n                fail();\n            }\n\n        }).start();\n        Thread.sleep(100);\n        try {\n            appInfo.addMachine(genMachineInfo(\"hostName3\", \"10.18.129.93\"));\n        } catch (ConcurrentModificationException e) {\n            e.printStackTrace();\n            fail();\n        }\n        Thread.sleep(1000);\n    }\n\n    private MachineInfo genMachineInfo(String hostName, String ip) {\n        MachineInfo machine = new MachineInfo();\n        machine.setApp(\"testApp\");\n        machine.setHostname(hostName);\n        machine.setIp(ip);\n        machine.setPort(8719);\n        machine.setVersion(String.valueOf(System.currentTimeMillis()));\n        return machine;\n    }\n\n    @Test\n    public void addRemoveMachineTest() {\n        AppInfo appInfo = new AppInfo(\"default\");\n        assertEquals(\"default\", appInfo.getApp());\n        assertEquals(0, appInfo.getMachines().size());\n        //add one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3389);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.1\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(1, appInfo.getMachines().size());\n        //add duplicated one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3389);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.2\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(1, appInfo.getMachines().size());\n        //add different one\n        {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setApp(\"default\");\n            machineInfo.setHostname(\"bogon\");\n            machineInfo.setIp(\"127.0.0.1\");\n            machineInfo.setPort(3390);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setVersion(\"0.4.3\");\n            appInfo.addMachine(machineInfo);\n        }\n        assertEquals(2, appInfo.getMachines().size());\n        appInfo.removeMachine(\"127.0.0.1\", 3389);\n        assertEquals(1, appInfo.getMachines().size());\n        appInfo.removeMachine(\"127.0.0.1\", 3390);\n        assertEquals(0, appInfo.getMachines().size());\n    }\n\n    @Test\n    public void testHealthyAndDead() {\n        System.setProperty(DashboardConfig.CONFIG_HIDE_APP_NO_MACHINE_MILLIS, \"60000\");\n        System.setProperty(DashboardConfig.CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, \"600000\");\n        DashboardConfig.clearCache();\n        String appName = \"default\";\n        AppInfo appInfo = new AppInfo();\n        appInfo.setApp(appName);\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis());\n            appInfo.addMachine(machineInfo);\n        }\n        assertTrue(appInfo.isShown());\n        assertFalse(appInfo.isDead());\n\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis() - 70000);\n            appInfo.addMachine(machineInfo);\n        }\n        assertFalse(appInfo.isShown());\n        assertFalse(appInfo.isDead());\n\n        {\n            MachineInfo machineInfo = MachineInfo.of(appName, \"127.0.0.1\", 8801);\n            machineInfo.setHeartbeatVersion(1);\n            machineInfo.setLastHeartbeat(System.currentTimeMillis() - 700000);\n            appInfo.addMachine(machineInfo);\n        }\n        assertFalse(appInfo.isShown());\n        assertTrue(appInfo.isDead());\n    }\n\n}\n\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfoTest.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.discovery;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;\n\n/**\n * @author Jason Joo\n */\npublic class MachineInfoTest {\n\n    @Test\n    public void testHealthyAndDead() {\n        System.setProperty(DashboardConfig.CONFIG_UNHEALTHY_MACHINE_MILLIS, \"60000\");\n        System.setProperty(DashboardConfig.CONFIG_AUTO_REMOVE_MACHINE_MILLIS, \"600000\");\n        DashboardConfig.clearCache();\n        MachineInfo machineInfo = new MachineInfo();\n        machineInfo.setHeartbeatVersion(1);\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 10000);\n        assertTrue(machineInfo.isHealthy());\n        assertFalse(machineInfo.isDead());\n\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 100000);\n        assertFalse(machineInfo.isHealthy());\n        assertFalse(machineInfo.isDead());\n\n        machineInfo.setLastHeartbeat(System.currentTimeMillis() - 1000000);\n        assertFalse(machineInfo.isHealthy());\n        assertTrue(machineInfo.isDead());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepositoryTest.java",
    "content": "/*\n * Copyright 1999-2019 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.repository.metric;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;\n\nimport org.assertj.core.util.Lists;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * Test cases for {@link InMemoryMetricsRepository}.\n *\n * @author Nick Tan\n */\npublic class InMemoryMetricsRepositoryTest {\n\n    private static final String DEFAULT_APP = \"default\";\n    private static final String DEFAULT_EXPIRE_APP = \"default_expire_app\";\n    private static final String DEFAULT_RESOURCE = \"test\";\n    private static final long EXPIRE_TIME = 1000 * 60 * 5L;\n\n    private InMemoryMetricsRepository inMemoryMetricsRepository;\n\n    private ExecutorService executorService;\n\n    @Before\n    public void setUp() throws Exception {\n        inMemoryMetricsRepository = new InMemoryMetricsRepository();\n        executorService = Executors.newFixedThreadPool(8);\n    }\n\n    @After\n    public void tearDown() {\n        executorService.shutdownNow();\n    }\n\n    private void testSave() {\n        for (int i = 0; i < 1000000; i++) {\n            MetricEntity entry = new MetricEntity();\n            entry.setApp(DEFAULT_APP);\n            entry.setResource(DEFAULT_RESOURCE);\n            entry.setTimestamp(new Date(System.currentTimeMillis()));\n            entry.setPassQps(1L);\n            entry.setExceptionQps(1L);\n            entry.setBlockQps(0L);\n            entry.setSuccessQps(1L);\n            inMemoryMetricsRepository.save(entry);\n        }\n    }\n\n    @Test\n    public void testExpireMetric() {\n        long now = System.currentTimeMillis();\n        MetricEntity expireEntry = new MetricEntity();\n        expireEntry.setApp(DEFAULT_EXPIRE_APP);\n        expireEntry.setResource(DEFAULT_RESOURCE);\n        expireEntry.setTimestamp(new Date(now - EXPIRE_TIME - 10L));\n        expireEntry.setPassQps(1L);\n        expireEntry.setExceptionQps(1L);\n        expireEntry.setBlockQps(0L);\n        expireEntry.setSuccessQps(1L);\n        inMemoryMetricsRepository.save(expireEntry);\n\n        MetricEntity entry = new MetricEntity();\n        entry.setApp(DEFAULT_EXPIRE_APP);\n        entry.setResource(DEFAULT_RESOURCE);\n        entry.setTimestamp(new Date(now));\n        entry.setPassQps(1L);\n        entry.setExceptionQps(1L);\n        entry.setBlockQps(0L);\n        entry.setSuccessQps(1L);\n        inMemoryMetricsRepository.save(entry);\n\n        List<MetricEntity> list = inMemoryMetricsRepository.queryByAppAndResourceBetween(\n            DEFAULT_EXPIRE_APP, DEFAULT_RESOURCE, now - 2 * EXPIRE_TIME, now + EXPIRE_TIME);\n\n        assertFalse(CollectionUtils.isEmpty(list));\n        assertEquals(1, list.size());\n    }\n\n    @Test\n    public void testListResourcesOfApp() {\n        // prepare basic test data\n        testSave();\n        System.out.println( \"[\" + System.currentTimeMillis() + \"] Basic test data ready in testListResourcesOfApp\");\n\n        List<CompletableFuture> futures = Lists.newArrayList();\n\n        // concurrent query resources of app\n        final CyclicBarrier cyclicBarrier = new CyclicBarrier(8);\n        for (int j = 0; j < 10000; j++) {\n            futures.add(\n                CompletableFuture.runAsync(() -> {\n                        try {\n                            cyclicBarrier.await();\n                            inMemoryMetricsRepository.listResourcesOfApp(DEFAULT_APP);\n                        } catch (InterruptedException | BrokenBarrierException e) {\n                            e.printStackTrace();\n                        }\n                }, executorService)\n            );\n        }\n\n        // batch add metric entity\n        for (int i = 0; i < 10000; i++) {\n            MetricEntity entry = new MetricEntity();\n            entry.setApp(DEFAULT_APP);\n            entry.setResource(DEFAULT_RESOURCE);\n            entry.setTimestamp(new Date(System.currentTimeMillis() - EXPIRE_TIME - 1000L));\n            entry.setPassQps(1L);\n            entry.setExceptionQps(1L);\n            entry.setBlockQps(0L);\n            entry.setSuccessQps(1L);\n            inMemoryMetricsRepository.save(entry);\n        }\n\n        CompletableFuture all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));\n\n        try {\n            all.get(10, TimeUnit.SECONDS);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        } catch (ExecutionException e) {\n            e.getCause().printStackTrace();\n            if (e.getCause() instanceof ConcurrentModificationException) {\n                fail(\"concurrent error occurred\");\n            } else {\n                fail(\"unexpected exception\");\n            }\n        } catch (TimeoutException e) {\n            fail(\"allOf future timeout\");\n        }\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.List;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Configuration\npublic class ApolloConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ApolloOpenApiClient apolloOpenApiClient() {\n        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()\n            .withPortalUrl(\"http://localhost:10034\")\n            .withToken(\"token\")\n            .build();\n        return client;\n\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfigUtil.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\npublic final class ApolloConfigUtil {\n\n    public static final String FLOW_DATA_ID_POSTFIX = \"-flow-rules\";\n\n    private ApolloConfigUtil() {\n    }\n\n    public static String getFlowDataId(String appName) {\n        return String.format(\"%s%s\", appName, FLOW_DATA_ID_POSTFIX);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloProvider\")\npublic class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String appId = \"appId\";\n        String flowDataId = ApolloConfigUtil.getFlowDataId(appName);\n        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appId, \"DEV\", \"default\", \"application\");\n        String rules = openNamespaceDTO\n            .getItems()\n            .stream()\n            .filter(p -> p.getKey().equals(flowDataId))\n            .map(OpenItemDTO::getValue)\n            .findFirst()\n            .orElse(\"\");\n\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.apollo;\n\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\n\nimport com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;\nimport com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;\nimport com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;\n\n/**\n * @author hantianwei@gmail.com\n * @since 1.5.0\n */\n@Component(\"flowRuleApolloPublisher\")\npublic class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ApolloOpenApiClient apolloOpenApiClient;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n\n        // Increase the configuration\n        String appId = \"appId\";\n        String flowDataId = ApolloConfigUtil.getFlowDataId(app);\n        OpenItemDTO openItemDTO = new OpenItemDTO();\n        openItemDTO.setKey(flowDataId);\n        openItemDTO.setValue(converter.convert(rules));\n        openItemDTO.setComment(\"Program auto-join\");\n        openItemDTO.setDataChangeCreatedBy(\"some-operator\");\n        apolloOpenApiClient.createOrUpdateItem(appId, \"DEV\", \"default\", \"application\", openItemDTO);\n\n        // Release configuration\n        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();\n        namespaceReleaseDTO.setEmergencyPublish(true);\n        namespaceReleaseDTO.setReleaseComment(\"Modify or add configurations\");\n        namespaceReleaseDTO.setReleasedBy(\"some-operator\");\n        namespaceReleaseDTO.setReleaseTitle(\"Modify or add configurations\");\n        apolloOpenApiClient.publishNamespace(appId, \"DEV\", \"default\", \"application\", namespaceReleaseDTO);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.StringUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosProvider\")\npublic class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<String, List<FlowRuleEntity>> converter;\n\n    @Override\n    public List<FlowRuleEntity> getRules(String appName) throws Exception {\n        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,\n            NacosConfigUtil.GROUP_ID, 3000);\n        if (StringUtil.isEmpty(rules)) {\n            return new ArrayList<>();\n        }\n        return converter.convert(rules);\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.util.AssertUtil;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Component(\"flowRuleNacosPublisher\")\npublic class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {\n\n    @Autowired\n    private ConfigService configService;\n    @Autowired\n    private Converter<List<FlowRuleEntity>, String> converter;\n\n    @Override\n    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {\n        AssertUtil.notEmpty(app, \"app name cannot be empty\");\n        if (rules == null) {\n            return;\n        }\n        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,\n            NacosConfigUtil.GROUP_ID, converter.convert(rules));\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\nimport java.util.List;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.nacos.api.config.ConfigFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\n@Configuration\npublic class NacosConfig {\n\n    @Bean\n    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {\n        return JSON::toJSONString;\n    }\n\n    @Bean\n    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {\n        return s -> JSON.parseArray(s, FlowRuleEntity.class);\n    }\n\n    @Bean\n    public ConfigService nacosConfigService() throws Exception {\n        return ConfigFactory.createConfigService(\"localhost\");\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java",
    "content": "/*\n * Copyright 1999-2018 Alibaba Group Holding Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.csp.sentinel.dashboard.rule.nacos;\n\n/**\n * @author Eric Zhao\n * @since 1.4.0\n */\npublic final class NacosConfigUtil {\n\n    public static final String GROUP_ID = \"SENTINEL_GROUP\";\n    \n    public static final String FLOW_DATA_ID_POSTFIX = \"-flow-rules\";\n    public static final String PARAM_FLOW_DATA_ID_POSTFIX = \"-param-rules\";\n    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = \"-cluster-map\";\n\n    /**\n     * cc for `cluster-client`\n     */\n    public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = \"-cc-config\";\n    /**\n     * cs for `cluster-server`\n     */\n    public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = \"-cs-transport-config\";\n    public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = \"-cs-flow-config\";\n    public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = \"-cs-namespace-set\";\n\n    private NacosConfigUtil() {}\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtilsTest.java",
    "content": "package com.alibaba.csp.sentinel.dashboard.util;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Optional;\n\nimport org.junit.Test;\n\nimport com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;\n\npublic class VersionUtilsTest {\n    @Test\n    public void test() {\n        Optional<SentinelVersion> version = VersionUtils.parseVersion(\"1.2.3\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(0, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3.\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3.4\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(0, version.get().getMinorVersion());\n        assertEquals(0, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"1.2.3-\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertNull(version.get().getPostfix());\n        \n        version = VersionUtils.parseVersion(\"-\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"-t\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"\");\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(null);\n        assertFalse(version.isPresent());\n        \n        version = VersionUtils.parseVersion(\"1.2.3-SNAPSHOTS\");\n        assertTrue(version.isPresent());\n        assertEquals(1, version.get().getMajorVersion());\n        assertEquals(2, version.get().getMinorVersion());\n        assertEquals(3, version.get().getFixVersion());\n        assertEquals(\"SNAPSHOTS\", version.get().getPostfix());\n    }\n}\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-sentinel-datasource-apollo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-apollo</artifactId>\n            <version>1.5.2</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-apollo/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java",
    "content": "package com.didispace.alibaba.sentinel;\n\nimport com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableApolloConfig\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/hello\")\n        public String hello() {\n            return \"didispace.com\";\n        }\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-apollo/src/main/resources/apollo-env.properties",
    "content": "local.meta=http://localhost:8080\ndev.meta=http://localhost:8080"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-apollo/src/main/resources/application.properties",
    "content": "spring.application.name=sentinel-datasource-apollo\nserver.port=8002\n\n# apollo config\napp.id=${spring.application.name}\n\n# sentinel dashboard\nspring.cloud.sentinel.transport.dashboard=localhost:8080\n\n# sentinel datasource apollo\nspring.cloud.sentinel.datasource.ds.apollo.namespaceName=application\nspring.cloud.sentinel.datasource.ds.apollo.flowRulesKey=sentinel.flowRules\nspring.cloud.sentinel.datasource.ds.apollo.rule-type=flow\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-sentinel-datasource-nacos</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-nacos</artifactId>\n            <version>1.5.2</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-nacos/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java",
    "content": "package com.didispace.alibaba.sentinel;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/hello\")\n        public String hello() {\n            return \"didispace.com\";\n        }\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-datasource-nacos/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-sentinel-datasource-nacos\nserver.port=8003\n\n# sentinel dashboard\nspring.cloud.sentinel.transport.dashboard=localhost:8080\n\n# sentinel datasource nacos ：http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/\nspring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848\nspring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP\nspring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel\nspring.cloud.sentinel.datasource.ds.nacos.rule-type=flow\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-rate-limiting/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>alibaba-sentinel-rate-limiting</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.2</version>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Finchley.SR1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>0.2.2.RELEASE</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-rate-limiting/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java",
    "content": "package com.didispace.alibaba.sentinel;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/hello\")\n        public String hello() {\n            return \"didispace.com\";\n        }\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/alibaba-sentinel-rate-limiting/src/main/resources/application.properties",
    "content": "spring.application.name=alibaba-sentinel-rate-limiting\nserver.port=8001\n\n# sentinel dashboard\nspring.cloud.sentinel.transport.dashboard=localhost:8080"
  },
  {
    "path": "4-Finchley/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.didispace</groupId>\n    <artifactId>spring-cloud-finchley</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <modules>\n        <!-- spring cloud alibaba专题-->\n        <module>alibaba-nacos-discovery-server</module>                 <!-- 使用Nacos实现服务注册与发现：服务提供方-->\n        <module>alibaba-nacos-discovery-client-common</module>          <!-- 使用Nacos实现服务注册与发现：服务消费方（spring cloud common接口实现负载均衡）-->\n        <module>alibaba-nacos-config-client</module>                    <!-- 使用Nacos作为配置中心 -->\n\n        <module>alibaba-nacos-discovery-client-resttemplate</module>    <!-- 使用Nacos实现服务注册与发现：服务消费方（RestTemplate实现）-->\n        <module>alibaba-nacos-discovery-client-webclient</module>       <!-- 使用Nacos实现服务注册与发现：服务消费方（WebClient实现）-->\n        <module>alibaba-nacos-discovery-client-feign</module>           <!-- 使用Nacos实现服务注册与发现：服务消费方（Feign实现）-->\n\n        <module>alibaba-sentinel-rate-limiting</module>                 <!-- 使用Sentinel实现接口限流 -->\n        <module>alibaba-sentinel-datasource-nacos</module>              <!-- 使用Nacos存储规则 -->\n        <module>alibaba-sentinel-datasource-apollo</module>             <!-- 使用Apollo存储规则 -->\n        <module>alibaba-sentinel-dashboard-apollo</module>              <!-- 使用Apollo存储规则的Dashboard -->\n        <module>alibaba-sentinel-dashboard-nacos</module>               <!-- 使用Nacos存储规则的Dashboard -->\n        <module>alibaba-sentinel-annotation</module>                    <!-- @SentinelResource注解的使用 -->\n\n        <module>alibaba-dubbo-api</module>      <!-- Dubbo API定义 -->\n        <module>alibaba-dubbo-server</module>   <!-- Dubbo 服务端实现 -->\n        <module>alibaba-dubbo-client</module>   <!-- Dubbo 客户端实现 -->\n\n        <!--spring cloud stream专题-->\n        <module>stream-consumer-self</module>       <!-- 消费自己的消息 -->\n        <module>stream-exception-handler-1</module> <!-- 消息重试 -->\n        <module>stream-exception-handler-2</module> <!-- 自定义错误处理逻辑 -->\n        <module>stream-exception-handler-3</module> <!-- 使用DLQ队列（RabbitMQ） -->\n        <module>stream-exception-handler-4</module> <!-- 重入队列（RabbitMQ） -->\n        <module>stream-content-route</module>       <!-- @StreamListener根据内容路由 -->\n        <module>stream-delayed-message</module>     <!-- 使用延迟消息实现定时任务（RabbitMQ）-->\n\n    </modules>\n\n</project>"
  },
  {
    "path": "4-Finchley/stream-consumer-self/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-consumer-self</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n\n@EnableBinding(TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestController.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\npublic class TestController {\n\n    @Autowired\n    private TestTopic testTopic;\n\n    @GetMapping(\"/sendMessage\")\n    public String messageWithMQ(@RequestParam String message) {\n        testTopic.output().send(MessageBuilder.withPayload(message).build());\n        return \"ok\";\n    }\n\n}\n"
  },
  {
    "path": "4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestListener.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class TestListener {\n\n    @StreamListener(TestTopic.INPUT)\n    public void receive(String payload) {\n        log.info(\"Received: \" + payload);\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestTopic.java",
    "content": "package com.didispace.stream;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface TestTopic {\n\n    String OUTPUT = \"example-topic-output\";\n    String INPUT = \"example-topic-input\";\n\n    @Output(OUTPUT)\n    MessageChannel output();\n\n    @Input(INPUT)\n    SubscribableChannel input();\n\n}"
  },
  {
    "path": "4-Finchley/stream-consumer-self/src/main/resources/application.properties",
    "content": "spring.application.name=stream-consumer-self\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=aaa-topic\nspring.cloud.stream.bindings.example-topic-output.destination=aaa-topic\n"
  },
  {
    "path": "4-Finchley/stream-content-route/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-content-route</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-content-route/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            testTopic.output().send(MessageBuilder.withPayload(message).setHeader(\"version\", \"1.0\").build());\n            testTopic.output().send(MessageBuilder.withPayload(message).setHeader(\"version\", \"2.0\").build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        @StreamListener(value = TestTopic.INPUT, condition = \"headers['version']=='1.0'\")\n        public void receiveV1(String payload, @Header(\"version\") String version) {\n            log.info(\"Received v1 : \" + payload + \", \" + version);\n        }\n\n        @StreamListener(value = TestTopic.INPUT, condition = \"headers['version']=='2.0'\")\n        public void receiveV2(String payload, @Header(\"version\") String version) {\n            log.info(\"Received v2 : \" + payload + \", \" + version);\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-content-route/src/main/resources/application.properties",
    "content": "spring.application.name=stream-content-route\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=test-topic\nspring.cloud.stream.bindings.example-topic-input.group=stream-content-route\n\nspring.cloud.stream.bindings.example-topic-output.destination=test-topic"
  },
  {
    "path": "4-Finchley/stream-delayed-message/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-delayed-message</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-delayed-message/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Slf4j\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            log.info(\"Send: \" + message);\n            testTopic.output().send(MessageBuilder.withPayload(message).setHeader(\"x-delay\", 5000).build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        @StreamListener(TestTopic.INPUT)\n        public void receive(String payload) {\n            log.info(\"Received: \" + payload);\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-delayed-message/src/main/resources/application.properties",
    "content": "spring.application.name=stream-delayed-message\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=delay-topic\nspring.cloud.stream.bindings.example-topic-input.group=test\nspring.cloud.stream.rabbit.bindings.example-topic-input.consumer.delayed-exchange=true\n\nspring.cloud.stream.bindings.example-topic-output.destination=delay-topic\nspring.cloud.stream.rabbit.bindings.example-topic-output.producer.delayed-exchange=true\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-1/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-exception-handler-1</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-1/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            testTopic.output().send(MessageBuilder.withPayload(message).build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        int counter = 1;\n\n        @StreamListener(TestTopic.INPUT)\n        public void receive(String payload) {\n            log.info(\"Received: \" + payload + \", \" + counter);\n            throw new RuntimeException(\"Message consumer failed!\");\n\n            // 计数，模拟重试过程中成功消费\n//            if (counter == 3) {\n//                counter = 1;\n//                return;\n//            } else {\n//                counter++;\n//                throw new RuntimeException(\"Message consumer failed!\");\n//            }\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-exception-handler-1/src/main/resources/application.properties",
    "content": "spring.application.name=stream-exception-handler-1\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=test-topic\nspring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1\n\nspring.cloud.stream.bindings.example-topic-output.destination=test-topic"
  },
  {
    "path": "4-Finchley/stream-exception-handler-2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-exception-handler-2</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-2/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            testTopic.output().send(MessageBuilder.withPayload(message).build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        @StreamListener(TestTopic.INPUT)\n        public void receive(String payload) {\n            log.info(\"Received payload : \" + payload);\n            throw new RuntimeException(\"Message consumer failed!\");\n        }\n\n        /**\n         * 消息消费失败的降级处理逻辑\n         *\n         * @param message\n         */\n        @ServiceActivator(inputChannel = \"test-topic.stream-exception-handler.errors\")\n        public void error(Message<?> message) {\n            log.info(\"Message consumer failed, call fallback!\");\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-exception-handler-2/src/main/resources/application.properties",
    "content": "spring.application.name=stream-exception-handler-2\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=test-topic\nspring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler\nspring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1\n\nspring.cloud.stream.bindings.example-topic-output.destination=test-topic\n\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-3/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-exception-handler-3</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-3/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            testTopic.output().send(MessageBuilder.withPayload(message).build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        @StreamListener(TestTopic.INPUT)\n        public void receive(String payload) {\n            log.info(\"Received payload : \" + payload);\n            throw new RuntimeException(\"Message consumer failed!\");\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-exception-handler-3/src/main/resources/application.properties",
    "content": "spring.application.name=stream-exception-handler-3\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=test-topic\nspring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler\nspring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1\n\nspring.cloud.stream.rabbit.bindings.example-topic-input.consumer.auto-bind-dlq=true\nspring.cloud.stream.rabbit.bindings.example-topic-input.consumer.republish-to-dlq=true\n#spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.dlq-ttl=10000\n\nspring.cloud.stream.bindings.example-topic-output.destination=test-topic\n\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-4/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.didispace</groupId>\n\t<artifactId>stream-exception-handler-4</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.0.5.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t\t<version>1.18.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>Finchley.SR1</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "4-Finchley/stream-exception-handler-4/src/main/java/com/didispace/stream/TestApplication.java",
    "content": "package com.didispace.stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.AmqpRejectAndDontRequeueException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.support.MessageBuilder;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.SubscribableChannel;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@EnableBinding(TestApplication.TestTopic.class)\n@SpringBootApplication\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private TestTopic testTopic;\n\n        /**\n         * 消息生产接口\n         *\n         * @param message\n         * @return\n         */\n        @GetMapping(\"/sendMessage\")\n        public String messageWithMQ(@RequestParam String message) {\n            testTopic.output().send(MessageBuilder.withPayload(message).build());\n            return \"ok\";\n        }\n\n    }\n\n    /**\n     * 消息消费逻辑\n     */\n    @Slf4j\n    @Component\n    static class TestListener {\n\n        private int count = 1;\n\n        @StreamListener(TestTopic.INPUT)\n        public void receive(String payload) {\n            log.info(\"Received payload : \" + payload + \", \" + count);\n            throw new RuntimeException(\"Message consumer failed!\");\n\n            // 进入DLQ的逻辑\n//            if (count == 3) {\n//                count = 1;\n//                throw new AmqpRejectAndDontRequeueException(\"tried 3 times failed, send to dlq!\");\n//            } else {\n//                count ++;\n//                throw new RuntimeException(\"Message consumer failed!\");\n//            }\n\n        }\n\n    }\n\n    interface TestTopic {\n\n        String OUTPUT = \"example-topic-output\";\n        String INPUT = \"example-topic-input\";\n\n        @Output(OUTPUT)\n        MessageChannel output();\n\n        @Input(INPUT)\n        SubscribableChannel input();\n\n    }\n\n}"
  },
  {
    "path": "4-Finchley/stream-exception-handler-4/src/main/resources/application.properties",
    "content": "spring.application.name=stream-exception-handler-4\nserver.port=8080\n\nspring.cloud.stream.bindings.example-topic-input.destination=test-topic\nspring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler\nspring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1\n\nspring.cloud.stream.rabbit.bindings.example-topic-input.consumer.auto-bind-dlq=true\nspring.cloud.stream.rabbit.bindings.example-topic-input.consumer.requeue-rejected=true\n\nspring.cloud.stream.bindings.example-topic-output.destination=test-topic\n\n"
  },
  {
    "path": "README.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 公益调试 Eureka：http://eureka.didispace.com\n- 公益调试 Nacos：http://blog.didispace.com/open-nacos-server-1-0-0/\n\n## 特别赞助商\n\n<table>\n      <tbody>\n        <tr>\n          <td align=\"center\" valign=\"middle\">\n             <a href=\"https://start.aliyun.com/\" target=\"_blank\">\n               <img width=\"300\" src=\"http://img.didispace.com/FlCL2IV4kAY92Ko9-MCUM_hVaiDf\">\n             </a>\n          </td>\n          <td align=\"center\" valign=\"middle\">\n            <a href=\"http://gk.link/a/103EK\" target=\"_blank\">\n              <img width=\"300\" src=\"http://img.didispace.com/FraIu771RXtYnQ3o5croL31PVzUB\">\n            </a>\n          </td>  \n          <td align=\"center\" valign=\"middle\">\n             <a href=\"https://openwrite.cn/?from=didi-springcloud\" target=\"_blank\">\n               <img width=\"300\" src=\"http://img.didispace.com/Fq6H6vSRJugF3cLxFNc29D9WVwFA\">\n             </a>\n          </td>          \n        </tr>\n        <tr>\n          <td align=\"center\" valign=\"middle\">\n            <a href=\"https://www.aliyun.com/minisite/goods?userCode=wxfqkr0o&share_source=copy_link\" target=\"_blank\">\n              <img width=\"300\" src=\"https://tva1.sinaimg.cn/large/006tNbRwgy1g9utcfi2hxj308c02i755.jpg\">\n            </a>\n          </td>  \n          <td align=\"center\" valign=\"middle\">\n            <a href=\"https://cloud.tencent.com/redirect.php?redirect=1027&cps_key=f6a8af1297bfac40b9d10ffa1270029a&from=console\" target=\"_blank\">\n              <img width=\"300\" src=\"http://img.didispace.com/FlcCuj0c-JhViRzr1wrynE941T9b\">\n            </a>\n          </td> \n          <td align=\"center\" valign=\"middle\">\n          </td>\n        </tr>\n      </tbody>\n</table>\n\n> 如果您也想赞助支持并出现在上表中的话，可以通过邮件联系我：`didi@didispace.com`\n\n## 教程列表\n\n### 《Spring Cloud构建微服务架构》系列博文\n\n#### Finchley版\n\n**本系列主要补充之前版本新增或是变动的主要内容，基础使用依然可以参考Dalston版教程**\n\n- [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/)\n\n##### Spring Cloud Aliabab专题\n\n- [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/)\n- [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/)\n- [Spring Cloud Alibaba到底坑不坑？](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/)\n\n*注册中心与配置中心：Nacos*\n\n- [Spring Cloud Alibaba基础教程：使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/)\n- [Spring Cloud Alibaba基础教程：Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/)\n- [Spring Cloud Alibaba基础教程：支持的几种服务消费方式（RestTemplate、WebClient、Feign）](http://blog.didispace.com/spring-cloud-alibaba-2/)\n- [Spring Cloud Alibaba基础教程：使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/)\n- [Spring Cloud Alibaba基础教程：Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/)\n- [Spring Cloud Alibaba基础教程：Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/)\n\n*分布式流量防卫兵：Sentinel*\n\n- [Spring Cloud Alibaba基础教程：使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/)\n- [Spring Cloud Alibaba基础教程：@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/)\n\n*国内使用最多的RPC框架整合：Dubbo*\n\n- [Spring Cloud Alibaba基础教程：与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/)\n\n##### Spring Cloud Stream专题补充\n\n- [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1)\n- [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6)\n- [Spring Cloud Stream使用延迟消息实现定时任务（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-7)\n- [Spring Cloud Stream消费失败后的处理策略（一）：自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2)\n- [Spring Cloud Stream消费失败后的处理策略（二）：自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3)\n- [Spring Cloud Stream消费失败后的处理策略（三）：使用DLQ队列（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-4)\n- [Spring Cloud Stream消费失败后的处理策略（四）：重新入队（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-5)\n\n#### Edgware版\n\n> 本系列主要是对Dalston版的补充，包含Edgware版的主要新增或变动的内容，对于Spring Cloud的基础使用依然建议参考Dalston版教程\n\n- [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1)\n\n#### Dalston版\n\n- [服务注册与发现（Eureka、Consul）](http://blog.didispace.com/spring-cloud-starter-dalston-1/)\n- [服务消费者（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/)\n- [服务消费者（Ribbon）](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/)\n- [服务消费者（Feign）](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/)\n- [服务消费者（Feign）传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/)\n- [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3)\n- [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1)\n- [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2)\n- [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3)\n- [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/)\n- [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/)\n- [服务网关（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/)\n- [服务网关（路由配置）](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/)\n- [服务网关（过滤器）](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/)\n- [服务网关（API文档汇总）](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/)\n- [消息驱动的微服务（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/)\n- [消息驱动的微服务（核心概念）](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/)\n- [消息驱动的微服务（消费组）](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/)\n- [消息驱动的微服务（消费组案例：解决消息重复消费）](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/)\n- [消息驱动的微服务（消息分区）](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/)\n- [分布式服务跟踪（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/)\n- [分布式服务跟踪（跟踪原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/)\n- [分布式服务跟踪（整合logstash）](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/)\n- [分布式服务跟踪（整合zipkin）](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/)\n- [分布式服务跟踪（收集原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/)\n- [分布式服务跟踪（抽样收集）](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/)\n\n#### Brixton版\n\n- 1-Brixton版教程示例/chapter1-1-1：[Spring Cloud构建微服务架构（一）服务注册与发现](http://blog.didispace.com/springcloud1/)\n- 1-Brixton版教程示例/chapter1-1-2：[Spring Cloud构建微服务架构（二）服务消费者](http://blog.didispace.com/springcloud2/)\n- 1-Brixton版教程示例/chapter1-1-3：[Spring Cloud构建微服务架构（三）断路器](http://blog.didispace.com/springcloud3/)\n- 1-Brixton版教程示例/chapter1-1-4：[Spring Cloud构建微服务架构（四）分布式配置中心](http://blog.didispace.com/springcloud4/)\n- 1-Brixton版教程示例/chapter1-1-8：[Spring Cloud构建微服务架构（四）分布式配置中心（续）](http://blog.didispace.com/springcloud4-2/)\n- 1-Brixton版教程示例/chapter1-1-5：[Spring Cloud构建微服务架构（五）服务网关](http://blog.didispace.com/springcloud5/)\n- 1-Brixton版教程示例/chapter1-1-6：[Spring Cloud构建微服务架构（六）高可用服务注册中心](http://blog.didispace.com/springcloud6/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Rabbit)](http://blog.didispace.com/springcloud7/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/)\n\n### 《Spring Cloud源码分析》系列博文\n\n- [Spring Cloud源码分析（一）Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（四）Zuul：核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/)\n- 未完待续\n\n### 《Spring Cloud实战小贴士》系列博文\n\n- [Spring Cloud实战小贴士：版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/)\n- [Spring Cloud实战小贴士：随机端口](http://blog.didispace.com/spring-cloud-tips-2/)\n- [Spring Cloud实战小贴士：健康检查](http://blog.didispace.com/spring-cloud-tips-3/)\n- [Spring Cloud实战小贴士：Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（一）](http://blog.didispace.com/spring-cloud-zuul-exception/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（二）](http://blog.didispace.com/spring-cloud-zuul-exception-2/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（三）【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/)\n- [Spring Cloud实战小贴士：Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/)\n- [Spring Cloud实战小贴士：Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/)\n- [Spring Cloud实战小贴士：Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/)\n- [Spring Cloud实战小贴士：Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/)\n\n#### 其他文章\n\n- [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/)\n- [为Spring Cloud Ribbon配置请求重试（Camden.SR2+）](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/)\n- [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/)\n- [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/)\n- [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/)\n- [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/)\n- [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/)\n\n## 我的公众号\n\n![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [我的博客](http://blog.didispace.com)：分享平时学习和实践过的技术内容\n- [知识星球](https://t.xiaomiquan.com/zfEiY3v)：聊聊技术人的斜杠生活\n- [GitHub](https://github.com/dyc87112/SpringCloud-Learning)：Star支持一下呗\n- [Gitee](https://gitee.com/didispace/SpringCloud-Learning)：Star支持一下呗\n- [Spring问答社区](http://www.spring4all.com/)：如果您有什么问题，可以去这里发帖\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)：全网Star最多的免费Spring Boot基础教程\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)：全网最早最全的免费Spring Cloud基础教程\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)\n"
  },
  {
    "path": "README_zh.md",
    "content": "# Spring Cloud教程\n\n本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用，欢迎点击右上方的**Star**按钮，给予支持！！\n\n- 公益调试 Eureka：http://eureka.didispace.com\n- 公益调试 Nacos：http://blog.didispace.com/open-nacos-server-1-0-0/\n\n## 特别赞助商\n\n<table>\n      <tbody>\n         <tr>\n          <td align=\"center\" valign=\"middle\">\n            <a href=\"https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o\" target=\"_blank\">\n              <img width=\"300\" src=\"https://tva1.sinaimg.cn/large/006tNbRwgy1g9utcfi2hxj308c02i755.jpg\">\n            </a>\n          </td>  \n          <td align=\"center\" valign=\"middle\">\n            <a href=\"http://gk.link/a/103EK\" target=\"_blank\">\n              <img width=\"300\" src=\"http://img.didispace.com/FraIu771RXtYnQ3o5croL31PVzUB\">\n            </a>\n          </td>   \n          <td align=\"center\" valign=\"middle\">\n            <a href=\"https://url.cn/5iF8JP2\" target=\"_blank\">\n              <img width=\"300\" src=\"http://img.didispace.com/FlcCuj0c-JhViRzr1wrynE941T9b\">\n            </a>\n          </td> \n        </tr>\n        <tr>\n          <td align=\"center\" valign=\"middle\">\n             <a href=\"https://start.aliyun.com/\" target=\"_blank\">\n               <img width=\"300\" src=\"http://img.didispace.com/FlCL2IV4kAY92Ko9-MCUM_hVaiDf\">\n             </a>\n          </td>\n          <td align=\"center\" valign=\"middle\">\n             <a href=\"https://openwrite.cn/?from=didi-springcloud\" target=\"_blank\">\n               <img width=\"300\" src=\"http://img.didispace.com/Foaph7vaVxs-bPRrFrEEKAYadPgg\">\n             </a>\n          </td> \n        </tr>\n      </tbody>\n</table>\n\n> 如果您也想赞助支持并出现在上表中的话，可以通过邮件联系我：`didi@didispace.com`\n\n## 教程列表\n\n### 《Spring Cloud构建微服务架构》系列博文\n\n#### Finchley版\n\n**本系列主要补充之前版本新增或是变动的主要内容，基础使用依然可以参考Dalston版教程**\n\n- [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/)\n\n##### Spring Cloud Aliabab专题\n\n- [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/)\n- [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/)\n- [Spring Cloud Alibaba到底坑不坑？](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/)\n\n*注册中心与配置中心：Nacos*\n\n- [Spring Cloud Alibaba基础教程：使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/)\n- [Spring Cloud Alibaba基础教程：Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/)\n- [Spring Cloud Alibaba基础教程：支持的几种服务消费方式（RestTemplate、WebClient、Feign）](http://blog.didispace.com/spring-cloud-alibaba-2/)\n- [Spring Cloud Alibaba基础教程：使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/)\n- [Spring Cloud Alibaba基础教程：Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/)\n- [Spring Cloud Alibaba基础教程：Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/)\n- [Spring Cloud Alibaba基础教程：Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/)\n\n*分布式流量防卫兵：Sentinel*\n\n- [Spring Cloud Alibaba基础教程：使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/)\n- [Spring Cloud Alibaba基础教程：Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/)\n- [Spring Cloud Alibaba基础教程：Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/)\n- [Spring Cloud Alibaba基础教程：@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/)\n\n*国内使用最多的RPC框架整合：Dubbo*\n\n- [Spring Cloud Alibaba基础教程：与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/)\n\n##### Spring Cloud Stream专题补充\n\n- [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1)\n- [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6)\n- [Spring Cloud Stream使用延迟消息实现定时任务（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-7)\n- [Spring Cloud Stream消费失败后的处理策略（一）：自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2)\n- [Spring Cloud Stream消费失败后的处理策略（二）：自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3)\n- [Spring Cloud Stream消费失败后的处理策略（三）：使用DLQ队列（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-4)\n- [Spring Cloud Stream消费失败后的处理策略（四）：重新入队（RabbitMQ）](http://blog.didispace.com/spring-cloud-starter-finchley-7-5)\n\n#### Edgware版\n\n> 本系列主要是对Dalston版的补充，包含Edgware版的主要新增或变动的内容，对于Spring Cloud的基础使用依然建议参考Dalston版教程\n\n- [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1)\n\n#### Dalston版\n\n- [服务注册与发现（Eureka、Consul）](http://blog.didispace.com/spring-cloud-starter-dalston-1/)\n- [服务消费者（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/)\n- [服务消费者（Ribbon）](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/)\n- [服务消费者（Feign）](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/)\n- [服务消费者（Feign）传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/)\n- [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3)\n- [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1)\n- [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2)\n- [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3)\n- [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/)\n- [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/)\n- [服务网关（基础）](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/)\n- [服务网关（路由配置）](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/)\n- [服务网关（过滤器）](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/)\n- [服务网关（API文档汇总）](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/)\n- [消息驱动的微服务（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/)\n- [消息驱动的微服务（核心概念）](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/)\n- [消息驱动的微服务（消费组）](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/)\n- [消息驱动的微服务（消费组案例：解决消息重复消费）](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/)\n- [消息驱动的微服务（消息分区）](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/)\n- [分布式服务跟踪（入门）](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/)\n- [分布式服务跟踪（跟踪原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/)\n- [分布式服务跟踪（整合logstash）](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/)\n- [分布式服务跟踪（整合zipkin）](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/)\n- [分布式服务跟踪（收集原理）](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/)\n- [分布式服务跟踪（抽样收集）](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/)\n\n#### Brixton版\n\n- 1-Brixton版教程示例/chapter1-1-1：[Spring Cloud构建微服务架构（一）服务注册与发现](http://blog.didispace.com/springcloud1/)\n- 1-Brixton版教程示例/chapter1-1-2：[Spring Cloud构建微服务架构（二）服务消费者](http://blog.didispace.com/springcloud2/)\n- 1-Brixton版教程示例/chapter1-1-3：[Spring Cloud构建微服务架构（三）断路器](http://blog.didispace.com/springcloud3/)\n- 1-Brixton版教程示例/chapter1-1-4：[Spring Cloud构建微服务架构（四）分布式配置中心](http://blog.didispace.com/springcloud4/)\n- 1-Brixton版教程示例/chapter1-1-8：[Spring Cloud构建微服务架构（四）分布式配置中心（续）](http://blog.didispace.com/springcloud4-2/)\n- 1-Brixton版教程示例/chapter1-1-5：[Spring Cloud构建微服务架构（五）服务网关](http://blog.didispace.com/springcloud5/)\n- 1-Brixton版教程示例/chapter1-1-6：[Spring Cloud构建微服务架构（六）高可用服务注册中心](http://blog.didispace.com/springcloud6/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Rabbit)](http://blog.didispace.com/springcloud7/)\n- 1-Brixton版教程示例/chapter1-1-7：[Spring Cloud构建微服务架构（七）消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/)\n\n### 《Spring Cloud源码分析》系列博文\n\n- [Spring Cloud源码分析（一）Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（二）Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/)\n- [Spring Cloud源码分析（四）Zuul：核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/)\n- 未完待续\n\n### 《Spring Cloud实战小贴士》系列博文\n\n- [Spring Cloud实战小贴士：版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/)\n- [Spring Cloud实战小贴士：随机端口](http://blog.didispace.com/spring-cloud-tips-2/)\n- [Spring Cloud实战小贴士：健康检查](http://blog.didispace.com/spring-cloud-tips-3/)\n- [Spring Cloud实战小贴士：Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（一）](http://blog.didispace.com/spring-cloud-zuul-exception/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（二）](http://blog.didispace.com/spring-cloud-zuul-exception-2/)\n- [Spring Cloud实战小贴士：Zuul统一异常处理（三）【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/)\n- [Spring Cloud实战小贴士：Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/)\n- [Spring Cloud实战小贴士：Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/)\n- [Spring Cloud实战小贴士：Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/)\n- [Spring Cloud实战小贴士：Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/)\n\n#### 其他文章\n\n- [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/)\n- [为Spring Cloud Ribbon配置请求重试（Camden.SR2+）](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/)\n- [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/)\n- [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/)\n- [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/)\n- [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/)\n- [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/)\n\n## 我的公众号\n\n![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg \"在这里输入图片标题\")\n\n## 推荐我的书\n\n![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png \"在这里输入图片标题\")\n\n## 其他推荐\n\n- [我的博客](http://blog.didispace.com)：分享平时学习和实践过的技术内容\n- [知识星球](https://t.xiaomiquan.com/zfEiY3v)：聊聊技术人的斜杠生活\n- [GitHub](https://github.com/dyc87112/SpringCloud-Learning)：Star支持一下呗\n- [Gitee](https://gitee.com/didispace/SpringCloud-Learning)：Star支持一下呗\n- [Spring问答社区](http://www.spring4all.com/)：如果您有什么问题，可以去这里发帖\n- [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)：全网Star最多的免费Spring Boot基础教程\n- [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)：全网最早最全的免费Spring Cloud基础教程\n- [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)\n"
  }
]