Commit 7c7f7b2a authored by Jason Song's avatar Jason Song

format according google code style

parent d25e0cbf
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId> <artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apollo-adminservice</artifactId> <artifactId>apollo-adminservice</artifactId>
<name>Apollo AdminService</name> <name>Apollo AdminService</name>
<dependencies> <dependencies>
<!-- apollo --> <!-- apollo -->
<dependency> <dependency>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId> <artifactId>apollo-biz</artifactId>
</dependency> </dependency>
<!-- end of apollo --> <!-- end of apollo -->
<!-- redis --> <!-- redis -->
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId> <artifactId>spring-data-redis</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>redis.clients</groupId> <groupId>redis.clients</groupId>
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
</dependency> </dependency>
<!-- end of redis --> <!-- end of redis -->
<!-- eureka --> <!-- eureka -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId> <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency> </dependency>
<!-- end of eureka --> <!-- end of eureka -->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
...@@ -2,13 +2,12 @@ package com.ctrip.apollo; ...@@ -2,13 +2,12 @@ package com.ctrip.apollo;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication @SpringBootApplication
//@EnableEurekaClient //@EnableEurekaClient
public class AdminServiceApplication { public class AdminServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
new SpringApplicationBuilder(AdminServiceApplication.class).web(true).run(args); new SpringApplicationBuilder(AdminServiceApplication.class).web(true).run(args);
} }
} }
...@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller; ...@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService; import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.ClusterDTO; import com.ctrip.apollo.core.dto.ClusterDTO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -13,11 +14,11 @@ import java.util.List; ...@@ -13,11 +14,11 @@ import java.util.List;
@RequestMapping("/cluster") @RequestMapping("/cluster")
public class ClusterController { public class ClusterController {
@Autowired @Autowired
private AdminConfigService adminConfigService; private AdminConfigService adminConfigService;
@RequestMapping("/app/{appId}") @RequestMapping("/app/{appId}")
public List<ClusterDTO> findClustersByApp(@PathVariable long appId){ public List<ClusterDTO> findClustersByApp(@PathVariable long appId) {
return adminConfigService.findClustersByApp(appId); return adminConfigService.findClustersByApp(appId);
} }
} }
...@@ -3,31 +3,34 @@ package com.ctrip.apollo.adminservice.controller; ...@@ -3,31 +3,34 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService; import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.ConfigItemDTO; import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO; import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List; import java.util.List;
import javax.annotation.Resource;
@RestController @RestController
@RequestMapping("/configs") @RequestMapping("/configs")
public class ConfigController { public class ConfigController {
@Resource(name = "adminConfigService") @Resource(name = "adminConfigService")
private AdminConfigService adminConfigService; private AdminConfigService adminConfigService;
@RequestMapping("/release/{releaseId}") @RequestMapping("/release/{releaseId}")
public List<ReleaseSnapshotDTO> getRelaseSnapshot(@PathVariable long releaseId){ public List<ReleaseSnapshotDTO> getRelaseSnapshot(@PathVariable long releaseId) {
return adminConfigService.findReleaseSnapshotByReleaseId(releaseId); return adminConfigService.findReleaseSnapshotByReleaseId(releaseId);
} }
@RequestMapping("/latest") @RequestMapping("/latest")
public List<ConfigItemDTO> findConfigItemsByClusters(@RequestParam(value = "clusterIds") List<Long> clusterIds){ public List<ConfigItemDTO> findConfigItemsByClusters(
return adminConfigService.findConfigItemsByClusters(clusterIds); @RequestParam(value = "clusterIds") List<Long> clusterIds) {
} return adminConfigService.findConfigItemsByClusters(clusterIds);
}
} }
...@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller; ...@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService; import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.VersionDTO; import com.ctrip.apollo.core.dto.VersionDTO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -13,21 +14,19 @@ import java.util.List; ...@@ -13,21 +14,19 @@ import java.util.List;
@RequestMapping("/version") @RequestMapping("/version")
public class VersionController { public class VersionController {
@Autowired @Autowired
private AdminConfigService adminConfigService; private AdminConfigService adminConfigService;
@RequestMapping("/app/{appId}")
public List<VersionDTO> versions(@PathVariable long appId){
return adminConfigService.findVersionsByApp(appId);
}
@RequestMapping("/{versionId}") @RequestMapping("/app/{appId}")
public VersionDTO version(@PathVariable long versionId){ public List<VersionDTO> versions(@PathVariable long appId) {
return adminConfigService.loadVersionById(versionId);
}
return adminConfigService.findVersionsByApp(appId);
}
@RequestMapping("/{versionId}")
public VersionDTO version(@PathVariable long versionId) {
return adminConfigService.loadVersionById(versionId);
}
} }
package com.ctrip.apollo.biz.entity; package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ClusterDTO; import com.ctrip.apollo.core.dto.ClusterDTO;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
...@@ -16,58 +17,58 @@ import javax.persistence.Id; ...@@ -16,58 +17,58 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?") @SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?")
public class Cluster { public class Cluster {
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
@Column(nullable = false) @Column(nullable = false)
private String name; private String name;
@Column(nullable = false) @Column(nullable = false)
private long appId; private long appId;
private boolean isDeleted; private boolean isDeleted;
public Cluster() { public Cluster() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public boolean isDeleted() { public boolean isDeleted() {
return isDeleted; return isDeleted;
} }
public void setDeleted(boolean deleted) { public void setDeleted(boolean deleted) {
isDeleted = deleted; isDeleted = deleted;
} }
public ClusterDTO toDTO(){ public ClusterDTO toDTO() {
ClusterDTO dto = new ClusterDTO(); ClusterDTO dto = new ClusterDTO();
dto.setAppId(appId); dto.setAppId(appId);
dto.setId(id); dto.setId(id);
dto.setName(name); dto.setName(name);
return dto; return dto;
} }
} }
package com.ctrip.apollo.biz.entity; package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ConfigItemDTO; import com.ctrip.apollo.core.dto.ConfigItemDTO;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import java.util.Date;
@Entity @Entity
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update ConfigItem set isDeleted = 1 where id = ?") @SQLDelete(sql = "Update ConfigItem set isDeleted = 1 where id = ?")
public class ConfigItem { public class ConfigItem {
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
@Column(nullable = false) @Column(nullable = false)
private long clusterId; private long clusterId;
@Column(nullable = false) @Column(nullable = false)
private String clusterName; private String clusterName;
@Column(nullable = false) @Column(nullable = false)
private long appId; private long appId;
@Column(nullable = false) @Column(nullable = false)
private String key; private String key;
@Column @Column
private String value; private String value;
@Column @Column
private String comment; private String comment;
@Column @Column
private String dataChangeCreatedBy; private String dataChangeCreatedBy;
@Column @Column
private Date dataChangeCreatedTime; private Date dataChangeCreatedTime;
@Column @Column
private String dataChangeLastModifiedBy; private String dataChangeLastModifiedBy;
@Column @Column
private Date dataChangeLastModifiedTime; private Date dataChangeLastModifiedTime;
@Column @Column
private boolean IsDeleted; private boolean IsDeleted;
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public long getClusterId() { public long getClusterId() {
return clusterId; return clusterId;
} }
public void setClusterId(long clusterId) { public void setClusterId(long clusterId) {
this.clusterId = clusterId; this.clusterId = clusterId;
} }
public String getClusterName() { public String getClusterName() {
return clusterName; return clusterName;
} }
public void setClusterName(String clusterName) { public void setClusterName(String clusterName) {
this.clusterName = clusterName; this.clusterName = clusterName;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public String getValue() { public String getValue() {
return value; return value;
} }
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
} }
public String getComment() { public String getComment() {
return comment; return comment;
} }
public void setComment(String comment) { public void setComment(String comment) {
this.comment = comment; this.comment = comment;
} }
public String getDataChangeCreatedBy() { public String getDataChangeCreatedBy() {
return dataChangeCreatedBy; return dataChangeCreatedBy;
} }
public void setDataChangeCreatedBy(String dataChangeCreatedBy) { public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy; this.dataChangeCreatedBy = dataChangeCreatedBy;
} }
public Date getDataChangeCreatedTime() { public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime; return dataChangeCreatedTime;
} }
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) { public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime; this.dataChangeCreatedTime = dataChangeCreatedTime;
} }
public String getDataChangeLastModifiedBy() { public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy; return dataChangeLastModifiedBy;
} }
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) { public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
this.dataChangeLastModifiedBy = dataChangeLastModifiedBy; this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
} }
public boolean isDeleted() { public boolean isDeleted() {
return IsDeleted; return IsDeleted;
} }
public void setDeleted(boolean isDeleted) { public void setDeleted(boolean isDeleted) {
IsDeleted = isDeleted; IsDeleted = isDeleted;
} }
public Date getDataChangeLastModifiedTime() { public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime; return dataChangeLastModifiedTime;
} }
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) { public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
} }
public ConfigItemDTO toDTO(){ public ConfigItemDTO toDTO() {
ConfigItemDTO dto = new ConfigItemDTO(); ConfigItemDTO dto = new ConfigItemDTO();
dto.setAppId(appId); dto.setAppId(appId);
dto.setId(id); dto.setId(id);
dto.setClusterId(clusterId); dto.setClusterId(clusterId);
dto.setClusterName(clusterName); dto.setClusterName(clusterName);
dto.setDataChangeCreatedBy(dataChangeCreatedBy); dto.setDataChangeCreatedBy(dataChangeCreatedBy);
dto.setDataChangeLastModifiedBy(dataChangeLastModifiedBy); dto.setDataChangeLastModifiedBy(dataChangeLastModifiedBy);
dto.setDataChangeCreatedTime(dataChangeCreatedTime); dto.setDataChangeCreatedTime(dataChangeCreatedTime);
dto.setDataChangeLastModifiedTime(dataChangeLastModifiedTime); dto.setDataChangeLastModifiedTime(dataChangeLastModifiedTime);
dto.setKey(key); dto.setKey(key);
dto.setValue(value); dto.setValue(value);
dto.setComment(comment); dto.setComment(comment);
return dto; return dto;
} }
} }
...@@ -14,55 +14,55 @@ import javax.persistence.Id; ...@@ -14,55 +14,55 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Release set isDeleted = 1 where id = ?") @SQLDelete(sql = "Update Release set isDeleted = 1 where id = ?")
public class Release { public class Release {
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
private String name; private String name;
private long appId; private long appId;
private String comment; private String comment;
private boolean isDeleted; private boolean isDeleted;
public Release() { public Release() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public String getComment() { public String getComment() {
return comment; return comment;
} }
public void setComment(String comment) { public void setComment(String comment) {
this.comment = comment; this.comment = comment;
} }
public boolean isDeleted() { public boolean isDeleted() {
return isDeleted; return isDeleted;
} }
public void setDeleted(boolean deleted) { public void setDeleted(boolean deleted) {
isDeleted = deleted; isDeleted = deleted;
} }
} }
package com.ctrip.apollo.biz.entity; package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO; import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
...@@ -16,70 +17,70 @@ import javax.persistence.Id; ...@@ -16,70 +17,70 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update ReleaseSnapShot set isDeleted = 1 where id = ?") @SQLDelete(sql = "Update ReleaseSnapShot set isDeleted = 1 where id = ?")
public class ReleaseSnapShot { public class ReleaseSnapShot {
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
@Column(nullable = false) @Column(nullable = false)
private long releaseId; private long releaseId;
@Column(nullable = false) @Column(nullable = false)
private String clusterName; private String clusterName;
@Column(nullable = false) @Column(nullable = false)
private String configurations; private String configurations;
private boolean isDeleted; private boolean isDeleted;
public ReleaseSnapShot() { public ReleaseSnapShot() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
} }
public void setReleaseId(long releaseId) { public void setReleaseId(long releaseId) {
this.releaseId = releaseId; this.releaseId = releaseId;
} }
public String getClusterName() { public String getClusterName() {
return clusterName; return clusterName;
} }
public void setClusterName(String clusterName) { public void setClusterName(String clusterName) {
this.clusterName = clusterName; this.clusterName = clusterName;
} }
public String getConfigurations() { public String getConfigurations() {
return configurations; return configurations;
} }
public void setConfigurations(String configurations) { public void setConfigurations(String configurations) {
this.configurations = configurations; this.configurations = configurations;
} }
public boolean isDeleted() { public boolean isDeleted() {
return isDeleted; return isDeleted;
} }
public void setDeleted(boolean deleted) { public void setDeleted(boolean deleted) {
isDeleted = deleted; isDeleted = deleted;
} }
public ReleaseSnapshotDTO toDTO(){ public ReleaseSnapshotDTO toDTO() {
ReleaseSnapshotDTO dto = new ReleaseSnapshotDTO(); ReleaseSnapshotDTO dto = new ReleaseSnapshotDTO();
dto.setId(id); dto.setId(id);
dto.setClusterName(clusterName); dto.setClusterName(clusterName);
dto.setConfigurations(configurations); dto.setConfigurations(configurations);
dto.setReleaseId(releaseId); dto.setReleaseId(releaseId);
return dto; return dto;
} }
} }
package com.ctrip.apollo.biz.entity; package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.VersionDTO; import com.ctrip.apollo.core.dto.VersionDTO;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
...@@ -16,80 +17,80 @@ import javax.persistence.Id; ...@@ -16,80 +17,80 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Version set isDeleted = 1 where id = ?") @SQLDelete(sql = "Update Version set isDeleted = 1 where id = ?")
public class Version { public class Version {
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
@Column(nullable = false) @Column(nullable = false)
private String name; private String name;
@Column(nullable = false) @Column(nullable = false)
private long appId; private long appId;
@Column(nullable = false) @Column(nullable = false)
private long releaseId; private long releaseId;
//parent version could be null //parent version could be null
private Long parentVersion; private Long parentVersion;
private boolean isDeleted; private boolean isDeleted;
public Version() { public Version() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
} }
public void setReleaseId(long releaseId) { public void setReleaseId(long releaseId) {
this.releaseId = releaseId; this.releaseId = releaseId;
} }
public boolean isDeleted() { public boolean isDeleted() {
return isDeleted; return isDeleted;
} }
public void setDeleted(boolean deleted) { public void setDeleted(boolean deleted) {
isDeleted = deleted; isDeleted = deleted;
} }
public Long getParentVersion() { public Long getParentVersion() {
return parentVersion; return parentVersion;
} }
public void setParentVersion(Long parentVersion) { public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion; this.parentVersion = parentVersion;
} }
public VersionDTO toDTO() { public VersionDTO toDTO() {
VersionDTO dto = new VersionDTO(); VersionDTO dto = new VersionDTO();
dto.setAppId(this.appId); dto.setAppId(this.appId);
dto.setId(this.id); dto.setId(this.id);
dto.setName(this.name); dto.setName(this.name);
dto.setParentVersion(this.parentVersion); dto.setParentVersion(this.parentVersion);
dto.setReleaseId(this.releaseId); dto.setReleaseId(this.releaseId);
return dto; return dto;
} }
} }
...@@ -2,12 +2,13 @@ package com.ctrip.apollo.biz.repository; ...@@ -2,12 +2,13 @@ package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.Cluster; import com.ctrip.apollo.biz.entity.Cluster;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List; import java.util.List;
public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> { public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> {
List<Cluster> findByAppId(long appId); List<Cluster> findByAppId(long appId);
} }
package com.ctrip.apollo.biz.repository; package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.ConfigItem; import com.ctrip.apollo.biz.entity.ConfigItem;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List; import java.util.List;
public interface ConfigItemRepository extends PagingAndSortingRepository<ConfigItem, Long> { public interface ConfigItemRepository extends PagingAndSortingRepository<ConfigItem, Long> {
List<ConfigItem> findByClusterIdIsIn(List<Long> clusterIds); List<ConfigItem> findByClusterIdIsIn(List<Long> clusterIds);
} }
package com.ctrip.apollo.biz.repository; package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot; import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List; import java.util.List;
...@@ -8,8 +9,9 @@ import java.util.List; ...@@ -8,8 +9,9 @@ import java.util.List;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public interface ReleaseSnapShotRepository extends PagingAndSortingRepository<ReleaseSnapShot, Long> { public interface ReleaseSnapShotRepository
ReleaseSnapShot findByReleaseIdAndClusterName(long releaseId, String clusterName); extends PagingAndSortingRepository<ReleaseSnapShot, Long> {
ReleaseSnapShot findByReleaseIdAndClusterName(long releaseId, String clusterName);
List<ReleaseSnapShot> findByReleaseId(long releaseId); List<ReleaseSnapShot> findByReleaseId(long releaseId);
} }
package com.ctrip.apollo.biz.repository; package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.Version; import com.ctrip.apollo.biz.entity.Version;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List; import java.util.List;
...@@ -9,9 +10,9 @@ import java.util.List; ...@@ -9,9 +10,9 @@ import java.util.List;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public interface VersionRepository extends PagingAndSortingRepository<Version, Long> { public interface VersionRepository extends PagingAndSortingRepository<Version, Long> {
Version findByAppIdAndName(long appId, String name); Version findByAppIdAndName(long appId, String name);
Version findById(long id); Version findById(long id);
List<Version> findByAppId(long appId); List<Version> findByAppId(long appId);
} }
package com.ctrip.apollo.biz.service; package com.ctrip.apollo.biz.service;
import com.ctrip.apollo.core.dto.*; import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import com.ctrip.apollo.core.dto.VersionDTO;
import java.util.List; import java.util.List;
...@@ -9,14 +12,14 @@ import java.util.List; ...@@ -9,14 +12,14 @@ import java.util.List;
*/ */
public interface AdminConfigService { public interface AdminConfigService {
List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId); List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId);
List<VersionDTO> findVersionsByApp(long appId); List<VersionDTO> findVersionsByApp(long appId);
VersionDTO loadVersionById(long versionId); VersionDTO loadVersionById(long versionId);
List<ClusterDTO> findClustersByApp(long appId); List<ClusterDTO> findClustersByApp(long appId);
List<ConfigItemDTO> findConfigItemsByClusters(List<Long> clusterIds); List<ConfigItemDTO> findConfigItemsByClusters(List<Long> clusterIds);
} }
...@@ -5,31 +5,22 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -5,31 +5,22 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
/** /**
* Config Service * Config Service
*
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public interface ConfigService { public interface ConfigService {
/** /**
* Load configuration from database * Load configuration from database
* @param appId */
* @param clusterName ApolloConfig loadConfig(long appId, String clusterName, String versionName);
* @param versionName
* @return
*/
ApolloConfig loadConfig(long appId, String clusterName, String versionName);
/** /**
* Load Version by appId and versionName from database * Load Version by appId and versionName from database
* @param appId */
* @param versionName Version loadVersionByAppIdAndVersionName(long appId, String versionName);
* @return
*/
Version loadVersionByAppIdAndVersionName(long appId, String versionName);
/** /**
* Load Config by version and clusterName from database * Load Config by version and clusterName from database
* @param version */
* @param clusterName ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName);
* @return
*/
ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName);
} }
...@@ -9,103 +9,109 @@ import com.ctrip.apollo.biz.repository.ConfigItemRepository; ...@@ -9,103 +9,109 @@ import com.ctrip.apollo.biz.repository.ConfigItemRepository;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository; import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
import com.ctrip.apollo.biz.repository.VersionRepository; import com.ctrip.apollo.biz.repository.VersionRepository;
import com.ctrip.apollo.biz.service.AdminConfigService; import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.*; import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import com.ctrip.apollo.core.dto.VersionDTO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service("adminConfigService") @Service("adminConfigService")
public class AdminConfigServiceImpl implements AdminConfigService { public class AdminConfigServiceImpl implements AdminConfigService {
@Autowired @Autowired
private VersionRepository versionRepository; private VersionRepository versionRepository;
@Autowired @Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository; private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired @Autowired
private ClusterRepository clusterRepository; private ClusterRepository clusterRepository;
@Autowired @Autowired
private ConfigItemRepository configItemRepository; private ConfigItemRepository configItemRepository;
@Override @Override
public List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId) { public List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId) {
if (releaseId <= 0){ if (releaseId <= 0) {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
}
List<ReleaseSnapShot> releaseSnapShots = releaseSnapShotRepository.findByReleaseId(releaseId);
if (releaseSnapShots == null || releaseSnapShots.size() == 0){
return Collections.EMPTY_LIST;
}
List<ReleaseSnapshotDTO> result = new ArrayList<>(releaseSnapShots.size());
for (ReleaseSnapShot releaseSnapShot: releaseSnapShots){
result.add(releaseSnapShot.toDTO());
}
return result;
} }
List<ReleaseSnapShot> releaseSnapShots = releaseSnapShotRepository.findByReleaseId(releaseId);
if (releaseSnapShots == null || releaseSnapShots.size() == 0) {
return Collections.EMPTY_LIST;
}
@Override List<ReleaseSnapshotDTO> result = new ArrayList<>(releaseSnapShots.size());
public List<VersionDTO> findVersionsByApp(long appId) { for (ReleaseSnapShot releaseSnapShot : releaseSnapShots) {
if (appId <= 0) { result.add(releaseSnapShot.toDTO());
return Collections.EMPTY_LIST; }
} return result;
}
List<Version> versions = versionRepository.findByAppId(appId);
if (versions == null || versions.size() == 0) {
return Collections.EMPTY_LIST;
}
List<VersionDTO> result = new ArrayList<>(versions.size()); @Override
for (Version version : versions) { public List<VersionDTO> findVersionsByApp(long appId) {
result.add(version.toDTO()); if (appId <= 0) {
} return Collections.EMPTY_LIST;
return result;
} }
@Override List<Version> versions = versionRepository.findByAppId(appId);
public VersionDTO loadVersionById(long versionId) { if (versions == null || versions.size() == 0) {
if (versionId <= 0){ return Collections.EMPTY_LIST;
return null;
}
Version version = versionRepository.findById(versionId);
return version.toDTO();
} }
@Override List<VersionDTO> result = new ArrayList<>(versions.size());
public List<ClusterDTO> findClustersByApp(long appId) { for (Version version : versions) {
if (appId <= 0){ result.add(version.toDTO());
return Collections.EMPTY_LIST; }
} return result;
List<Cluster> clusters = clusterRepository.findByAppId(appId); }
if (clusters == null || clusters.size() == 0){
return Collections.EMPTY_LIST; @Override
} public VersionDTO loadVersionById(long versionId) {
if (versionId <= 0) {
List<ClusterDTO> result = new ArrayList<>(clusters.size()); return null;
for (Cluster cluster: clusters){ }
result.add(cluster.toDTO()); Version version = versionRepository.findById(versionId);
} return version.toDTO();
return result; }
@Override
public List<ClusterDTO> findClustersByApp(long appId) {
if (appId <= 0) {
return Collections.EMPTY_LIST;
}
List<Cluster> clusters = clusterRepository.findByAppId(appId);
if (clusters == null || clusters.size() == 0) {
return Collections.EMPTY_LIST;
}
List<ClusterDTO> result = new ArrayList<>(clusters.size());
for (Cluster cluster : clusters) {
result.add(cluster.toDTO());
}
return result;
}
@Override
public List<ConfigItemDTO> findConfigItemsByClusters(List<Long> clusterIds) {
if (clusterIds == null || clusterIds.size() == 0) {
return Collections.EMPTY_LIST;
}
List<ConfigItem> configItems = configItemRepository.findByClusterIdIsIn(clusterIds);
if (configItems == null || configItems.size() == 0) {
return Collections.EMPTY_LIST;
} }
@Override List<ConfigItemDTO> result = new ArrayList<>(configItems.size());
public List<ConfigItemDTO> findConfigItemsByClusters(List<Long> clusterIds) { for (ConfigItem configItem : configItems) {
if (clusterIds == null || clusterIds.size() == 0){ result.add(configItem.toDTO());
return Collections.EMPTY_LIST;
}
List<ConfigItem> configItems = configItemRepository.findByClusterIdIsIn(clusterIds);
if (configItems == null || configItems.size() == 0){
return Collections.EMPTY_LIST;
}
List<ConfigItemDTO> result = new ArrayList<>(configItems.size());
for (ConfigItem configItem: configItems){
result.add(configItem.toDTO());
}
return result;
} }
return result;
}
} }
package com.ctrip.apollo.biz.service.impl; package com.ctrip.apollo.biz.service.impl;
import com.google.common.collect.Maps;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot; import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import com.ctrip.apollo.biz.entity.Version; import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository; import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
...@@ -8,7 +10,7 @@ import com.ctrip.apollo.biz.service.ConfigService; ...@@ -8,7 +10,7 @@ import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -20,56 +22,58 @@ import java.util.Map; ...@@ -20,56 +22,58 @@ import java.util.Map;
*/ */
@Service("configService") @Service("configService")
public class ConfigServiceImpl implements ConfigService { public class ConfigServiceImpl implements ConfigService {
@Autowired @Autowired
private VersionRepository versionRepository; private VersionRepository versionRepository;
@Autowired @Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository; private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
private TypeReference<Map<String, Object>> configurationTypeReference = private TypeReference<Map<String, Object>> configurationTypeReference =
new TypeReference<Map<String, Object>>() { new TypeReference<Map<String, Object>>() {
}; };
@Override @Override
public ApolloConfig loadConfig(long appId, String clusterName, String versionName) { public ApolloConfig loadConfig(long appId, String clusterName, String versionName) {
Version version = loadVersionByAppIdAndVersionName(appId, versionName); Version version = loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) { if (version == null) {
return null; return null;
}
return loadConfigByVersionAndClusterName(version, clusterName);
} }
@Override return loadConfigByVersionAndClusterName(version, clusterName);
public Version loadVersionByAppIdAndVersionName(long appId, String versionName) { }
return versionRepository.findByAppIdAndName(appId, versionName);
}
@Override @Override
public ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName) { public Version loadVersionByAppIdAndVersionName(long appId, String versionName) {
ReleaseSnapShot releaseSnapShot = return versionRepository.findByAppIdAndName(appId, versionName);
releaseSnapShotRepository.findByReleaseIdAndClusterName(version.getReleaseId(), clusterName); }
if (releaseSnapShot == null) {
return null;
}
return assembleConfig(version, releaseSnapShot); @Override
public ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName) {
ReleaseSnapShot releaseSnapShot =
releaseSnapShotRepository
.findByReleaseIdAndClusterName(version.getReleaseId(), clusterName);
if (releaseSnapShot == null) {
return null;
} }
private ApolloConfig assembleConfig(Version version, ReleaseSnapShot releaseSnapShot) { return assembleConfig(version, releaseSnapShot);
ApolloConfig config = }
new ApolloConfig(version.getAppId(), releaseSnapShot.getClusterName(), version.getName(), version.getReleaseId());
config.setConfigurations(transformConfigurationToMap(releaseSnapShot.getConfigurations()));
return config; private ApolloConfig assembleConfig(Version version, ReleaseSnapShot releaseSnapShot) {
} ApolloConfig config =
new ApolloConfig(version.getAppId(), releaseSnapShot.getClusterName(), version.getName(),
version.getReleaseId());
config.setConfigurations(transformConfigurationToMap(releaseSnapShot.getConfigurations()));
return config;
}
Map<String, Object> transformConfigurationToMap(String configurations) { Map<String, Object> transformConfigurationToMap(String configurations) {
try { try {
return objectMapper.readValue(configurations, configurationTypeReference); return objectMapper.readValue(configurations, configurationTypeReference);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return Maps.newHashMap(); return Maps.newHashMap();
}
} }
}
} }
package com.ctrip.apollo.biz; package com.ctrip.apollo.biz;
import com.ctrip.apollo.biz.service.impl.ConfigServiceImplTest; import com.ctrip.apollo.biz.service.impl.ConfigServiceImplTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({
ConfigServiceImplTest.class}) ConfigServiceImplTest.class})
public class AllTests { public class AllTests {
} }
package com.ctrip.apollo.biz.service.impl; package com.ctrip.apollo.biz.service.impl;
import com.google.common.collect.Maps;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot; import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import com.ctrip.apollo.biz.entity.Version; import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository; import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
...@@ -7,7 +9,7 @@ import com.ctrip.apollo.biz.repository.VersionRepository; ...@@ -7,7 +9,7 @@ import com.ctrip.apollo.biz.repository.VersionRepository;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -21,123 +23,140 @@ import java.util.Map; ...@@ -21,123 +23,140 @@ import java.util.Map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigServiceImplTest { public class ConfigServiceImplTest {
@Mock @Mock
private VersionRepository versionRepository; private VersionRepository versionRepository;
@Mock @Mock
private ReleaseSnapShotRepository releaseSnapShotRepository; private ReleaseSnapShotRepository releaseSnapShotRepository;
@Mock @Mock
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
private ConfigServiceImpl configService; private ConfigServiceImpl configService;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
configService = new ConfigServiceImpl(); configService = new ConfigServiceImpl();
ReflectionTestUtils.setField(configService, "versionRepository", versionRepository); ReflectionTestUtils.setField(configService, "versionRepository", versionRepository);
ReflectionTestUtils.setField(configService, "releaseSnapShotRepository", releaseSnapShotRepository); ReflectionTestUtils
ReflectionTestUtils.setField(configService, "objectMapper", objectMapper); .setField(configService, "releaseSnapShotRepository", releaseSnapShotRepository);
} ReflectionTestUtils.setField(configService, "objectMapper", objectMapper);
}
@Test
public void testLoadConfig() throws Exception { @Test
long someAppId = 1; public void testLoadConfig() throws Exception {
String someClusterName = "someClusterName"; long someAppId = 1;
String someVersionName = "someVersionName"; String someClusterName = "someClusterName";
long someReleaseId = 1; String someVersionName = "someVersionName";
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}"; long someReleaseId = 1;
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
ReleaseSnapShot someReleaseSnapShot = assembleReleaseSnapShot(someReleaseId, someClusterName, someValidConfiguration); Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
Map<String, Object> someMap = Maps.newHashMap(); ReleaseSnapShot
someReleaseSnapShot =
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion); assembleReleaseSnapShot(someReleaseId, someClusterName, someValidConfiguration);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName)).thenReturn(someReleaseSnapShot); Map<String, Object> someMap = Maps.newHashMap();
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap);
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName); when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName))
.thenReturn(someReleaseSnapShot);
assertEquals(someAppId, result.getAppId()); when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
assertEquals(someClusterName, result.getCluster()); .thenReturn(someMap);
assertEquals(someVersionName, result.getVersion());
assertEquals(someReleaseId, result.getReleaseId()); ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertEquals(someMap, result.getConfigurations());
} assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
@Test assertEquals(someVersionName, result.getVersion());
public void testLoadConfigWithVersionNotFound() throws Exception { assertEquals(someReleaseId, result.getReleaseId());
long someAppId = 1; assertEquals(someMap, result.getConfigurations());
String someClusterName = "someClusterName"; }
String someVersionName = "someVersionName";
@Test
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(null); public void testLoadConfigWithVersionNotFound() throws Exception {
long someAppId = 1;
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName); String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
assertNull(result);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName); when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(null);
}
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
@Test
public void testLoadConfigWithConfigNotFound() throws Exception { assertNull(result);
long someAppId = 1; verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
String someClusterName = "someClusterName"; }
String someVersionName = "someVersionName";
long someReleaseId = 1; @Test
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId); public void testLoadConfigWithConfigNotFound() throws Exception {
long someAppId = 1;
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion); String someClusterName = "someClusterName";
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName)).thenReturn(null); String someVersionName = "someVersionName";
long someReleaseId = 1;
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName); Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
assertNull(result); when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName); when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName))
verify(releaseSnapShotRepository, times(1)).findByReleaseIdAndClusterName(someReleaseId, someClusterName); .thenReturn(null);
}
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
private Version assembleVersion(long appId, String versionName, long releaseId) {
Version version = new Version(); assertNull(result);
version.setAppId(appId); verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
version.setName(versionName); verify(releaseSnapShotRepository, times(1))
version.setReleaseId(releaseId); .findByReleaseIdAndClusterName(someReleaseId, someClusterName);
return version; }
}
private Version assembleVersion(long appId, String versionName, long releaseId) {
private ReleaseSnapShot assembleReleaseSnapShot(long releaseId, String clusterName, String configurations) { Version version = new Version();
ReleaseSnapShot releaseSnapShot = new ReleaseSnapShot(); version.setAppId(appId);
releaseSnapShot.setReleaseId(releaseId); version.setName(versionName);
releaseSnapShot.setClusterName(clusterName); version.setReleaseId(releaseId);
releaseSnapShot.setConfigurations(configurations); return version;
return releaseSnapShot; }
}
private ReleaseSnapShot assembleReleaseSnapShot(long releaseId, String clusterName,
String configurations) {
@Test ReleaseSnapShot releaseSnapShot = new ReleaseSnapShot();
public void testTransformConfigurationToMapSuccessful() throws Exception { releaseSnapShot.setReleaseId(releaseId);
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}"; releaseSnapShot.setClusterName(clusterName);
Map<String, String> someMap = Maps.newHashMap(); releaseSnapShot.setConfigurations(configurations);
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap); return releaseSnapShot;
}
Map<String, Object> result = configService.transformConfigurationToMap(someValidConfiguration);
assertEquals(someMap, result); @Test
verify(objectMapper, times(1)).readValue(eq(someValidConfiguration), (TypeReference) anyObject()); public void testTransformConfigurationToMapSuccessful() throws Exception {
} String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Map<String, String> someMap = Maps.newHashMap();
@Test when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
public void testTransformConfigurationToMapFailed() throws Exception { .thenReturn(someMap);
String someInvalidConfiguration = "xxx";
when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject())).thenThrow(IOException.class); Map<String, Object> result = configService.transformConfigurationToMap(someValidConfiguration);
Map<String, Object> result = configService.transformConfigurationToMap(someInvalidConfiguration); assertEquals(someMap, result);
verify(objectMapper, times(1))
assertTrue(result.isEmpty()); .readValue(eq(someValidConfiguration), (TypeReference) anyObject());
}
}
@Test
public void testTransformConfigurationToMapFailed() throws Exception {
String someInvalidConfiguration = "xxx";
when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject()))
.thenThrow(IOException.class);
Map<String, Object>
result =
configService.transformConfigurationToMap(someInvalidConfiguration);
assertTrue(result.isEmpty());
}
} }
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId> <artifactId>apollo</artifactId>
......
...@@ -5,6 +5,7 @@ import com.ctrip.apollo.client.loader.ConfigLoaderManager; ...@@ -5,6 +5,7 @@ import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange; import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult; import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
...@@ -35,140 +36,155 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -35,140 +36,155 @@ import java.util.concurrent.atomic.AtomicReference;
* *
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware { public class ApolloConfigManager
private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class); implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
private static AtomicReference<ApolloConfigManager> singletonProtector = new AtomicReference<ApolloConfigManager>(); private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class);
private static AtomicReference<ApolloConfigManager>
private ConfigLoaderManager configLoaderManager; singletonProtector =
private ConfigurableApplicationContext applicationContext; new AtomicReference<ApolloConfigManager>();
private ConfigUtil configUtil;
private ScheduledExecutorService executorService; private ConfigLoaderManager configLoaderManager;
private AtomicLong counter; private ConfigurableApplicationContext applicationContext;
private RefreshScope scope; private ConfigUtil configUtil;
private ScheduledExecutorService executorService;
public ApolloConfigManager() { private AtomicLong counter;
if(!singletonProtector.compareAndSet(null, this)) { private RefreshScope scope;
throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
} public ApolloConfigManager() {
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager(); if (!singletonProtector.compareAndSet(null, this)) {
this.configUtil = ConfigUtil.getInstance(); throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
this.counter = new AtomicLong();
executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "ApolloConfigManager-" + counter.incrementAndGet());
return thread;
}
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
throw new RuntimeException(
String.format("ApplicationContext must implement ConfigurableApplicationContext, but found: %s", applicationContext.getClass().getName()));
}
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
this.configUtil.setApplicationContext(applicationContext);
}
/**
* This is the first method invoked, so we could prepare the property sources here.
* Specifically we need to finish preparing property source before PropertySourcesPlaceholderConfigurer
* so that configurations could be injected correctly
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registerDependentBeans(registry);
initializePropertySource();
schedulePeriodicRefresh();
} }
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
/** this.configUtil = ConfigUtil.getInstance();
* Register beans needed for Apollo Config Client this.counter = new AtomicLong();
* <li> executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
* - RefreshScope: used to refresh beans when configurations changes @Override
* </li> public Thread newThread(Runnable runnable) {
* <li> Thread thread = new Thread(runnable, "ApolloConfigManager-" + counter.incrementAndGet());
* - PropertySourcesPlaceholderConfigurer: used to support placeholder configuration injection return thread;
* </li> }
* @param registry });
*/ }
private void registerDependentBeans(BeanDefinitionRegistry registry) {
BeanDefinition refreshScope = BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class).getBeanDefinition(); @Override
registry.registerBeanDefinition("refreshScope", refreshScope); public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanDefinition propertySourcesPlaceholderConfigurer = BeanDefinitionBuilder.genericBeanDefinition(PropertySourcesPlaceholderConfigurer.class).getBeanDefinition(); if (!(applicationContext instanceof ConfigurableApplicationContext)) {
registry.registerBeanDefinition("propertySourcesPlaceholderConfigurer", propertySourcesPlaceholderConfigurer); throw new RuntimeException(
String.format(
"ApplicationContext must implement ConfigurableApplicationContext, but found: %s",
applicationContext.getClass().getName()));
} }
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
/** this.configUtil.setApplicationContext(applicationContext);
* This is executed after postProcessBeanDefinitionRegistry }
*/
@Override /**
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { * This is the first method invoked, so we could prepare the property sources here.
} * Specifically we need to finish preparing property source before PropertySourcesPlaceholderConfigurer
* so that configurations could be injected correctly
/** */
* Make sure this bean is called before other beans @Override
* @return public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
*/ throws BeansException {
@Override registerDependentBeans(registry);
public int getOrder() { initializePropertySource();
return Ordered.HIGHEST_PRECEDENCE; schedulePeriodicRefresh();
}
/**
* Register beans needed for Apollo Config Client
* <li>
* - RefreshScope: used to refresh beans when configurations changes
* </li>
* <li>
* - PropertySourcesPlaceholderConfigurer: used to support placeholder configuration injection
* </li>
*/
private void registerDependentBeans(BeanDefinitionRegistry registry) {
BeanDefinition
refreshScope =
BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class).getBeanDefinition();
registry.registerBeanDefinition("refreshScope", refreshScope);
BeanDefinition
propertySourcesPlaceholderConfigurer =
BeanDefinitionBuilder.genericBeanDefinition(PropertySourcesPlaceholderConfigurer.class)
.getBeanDefinition();
registry.registerBeanDefinition("propertySourcesPlaceholderConfigurer",
propertySourcesPlaceholderConfigurer);
}
/**
* This is executed after postProcessBeanDefinitionRegistry
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
/**
* Make sure this bean is called before other beans
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
/**
* Initialize property sources
*/
void initializePropertySource() {
//TODO stop application from starting when config cannot be loaded?
CompositePropertySource result = this.configLoaderManager.loadPropertySource();
updateEnvironmentPropertySource(result);
}
private void updateEnvironmentPropertySource(CompositePropertySource currentPropertySource) {
MutablePropertySources
currentPropertySources =
applicationContext.getEnvironment().getPropertySources();
if (currentPropertySources.contains(currentPropertySource.getName())) {
currentPropertySources.replace(currentPropertySource.getName(), currentPropertySource);
return;
} }
currentPropertySources.addFirst(currentPropertySource);
/** }
* Initialize property sources
*/ void schedulePeriodicRefresh() {
void initializePropertySource() { executorService.scheduleAtFixedRate(
//TODO stop application from starting when config cannot be loaded? new Runnable() {
CompositePropertySource result = this.configLoaderManager.loadPropertySource(); @Override
public void run() {
updateEnvironmentPropertySource(result); try {
} updatePropertySource();
} catch (Throwable ex) {
private void updateEnvironmentPropertySource(CompositePropertySource currentPropertySource) { logger.error("Refreshing config failed", ex);
MutablePropertySources currentPropertySources = applicationContext.getEnvironment().getPropertySources();
if (currentPropertySources.contains(currentPropertySource.getName())) {
currentPropertySources.replace(currentPropertySource.getName(), currentPropertySource);
return;
}
currentPropertySources.addFirst(currentPropertySource);
}
void schedulePeriodicRefresh() {
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
updatePropertySource();
} catch (Throwable e) {
logger.error("Refreshing config failed", e);
}
} }
}, configUtil.getRefreshInterval(), configUtil.getRefreshInterval(), configUtil.getRefreshTimeUnit()); }
}, configUtil.getRefreshInterval(), configUtil.getRefreshInterval(),
configUtil.getRefreshTimeUnit());
}
public List<PropertyChange> updatePropertySource() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource();
if (result.hasChanges()) {
logger.info("Found changes, refresh environment and refreshscope beans.");
updateEnvironmentPropertySource(result.getPropertySource());
refreshBeans();
} }
return result.getChanges();
}
public List<PropertyChange> updatePropertySource() { private void refreshBeans() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource(); if (this.scope == null) {
if (result.hasChanges()) { this.scope = applicationContext.getBean("refreshScope", RefreshScope.class);
logger.info("Found changes, refresh environment and refreshscope beans.");
updateEnvironmentPropertySource(result.getPropertySource());
refreshBeans();
}
return result.getChanges();
} }
private void refreshBeans() { if (this.scope == null) {
if (this.scope == null) { logger.error("Could not get refresh scope object, skip refresh beans");
this.scope = applicationContext.getBean("refreshScope", RefreshScope.class); return;
}
if (this.scope == null) {
logger.error("Could not get refresh scope object, skip refresh beans");
return;
}
this.scope.refreshAll();
} }
this.scope.refreshAll();
}
} }
...@@ -4,8 +4,8 @@ package com.ctrip.apollo.client.constants; ...@@ -4,8 +4,8 @@ package com.ctrip.apollo.client.constants;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class Constants { public class Constants {
public static final String APP_ID = "app.id"; public static final String APP_ID = "app.id";
public static final String VERSION = "version"; public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release"; public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env"; public static final String ENV = "env";
} }
...@@ -5,17 +5,17 @@ package com.ctrip.apollo.client.enums; ...@@ -5,17 +5,17 @@ package com.ctrip.apollo.client.enums;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public enum PropertyChangeType { public enum PropertyChangeType {
NEW("New"), NEW("New"),
MODIFIED("Modified"), MODIFIED("Modified"),
DELETED("Deleted"); DELETED("Deleted");
private String type; private String type;
PropertyChangeType(String type) { PropertyChangeType(String type) {
this.type = type; this.type = type;
} }
public String getType() { public String getType() {
return type; return type;
} }
} }
package com.ctrip.apollo.client.env; package com.ctrip.apollo.client.env;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
...@@ -10,20 +19,11 @@ import java.util.Enumeration; ...@@ -10,20 +19,11 @@ import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.StringUtils;
public class ClientEnvironment { public class ClientEnvironment {
private static final Logger logger = LoggerFactory.getLogger(ClientEnvironment.class); private static final Logger logger = LoggerFactory.getLogger(ClientEnvironment.class);
private final static String DEFAULT_FILE = "/apollo.properties"; private static final String DEFAULT_FILE = "/apollo.properties";
private AtomicReference<Env> env = new AtomicReference<Env>(); private AtomicReference<Env> env = new AtomicReference<Env>();
...@@ -84,20 +84,20 @@ public class ClientEnvironment { ...@@ -84,20 +84,20 @@ public class ClientEnvironment {
props.load(in); props.load(in);
in.close(); in.close();
} }
} catch (Exception e) { } catch (Exception ex) {
logger.warn("Reading config failed: {}", e.getMessage()); logger.warn("Reading config failed: {}", ex.getMessage());
} finally { } finally {
if (in != null) { if (in != null) {
try { try {
in.close(); in.close();
} catch (IOException e) { } catch (IOException ex) {
logger.warn("Close config failed: {}", e.getMessage()); logger.warn("Close config failed: {}", ex.getMessage());
} }
} }
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Enumeration<String> e = (Enumeration<String>) props.propertyNames(); e for (Enumeration<String> e = (Enumeration<String>) props.propertyNames(); e
.hasMoreElements();) { .hasMoreElements(); ) {
String key = e.nextElement(); String key = e.nextElement();
String val = (String) props.getProperty(key); String val = (String) props.getProperty(key);
sb.append(key).append('=').append(val).append('\n'); sb.append(key).append('=').append(val).append('\n');
......
...@@ -7,7 +7,7 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -7,7 +7,7 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
* @author Jason Song(songs_ctrip.com) * @author Jason Song(songs_ctrip.com)
*/ */
public interface ConfigLoader { public interface ConfigLoader {
ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous); ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
void setFallBackLoader(ConfigLoader configLoader); void setFallBackLoader(ConfigLoader configLoader);
} }
package com.ctrip.apollo.client.loader; package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.Apollo.Env; import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.env.ClientEnvironment; import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader; import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
...@@ -14,35 +15,39 @@ import org.springframework.web.client.RestTemplate; ...@@ -14,35 +15,39 @@ import org.springframework.web.client.RestTemplate;
public class ConfigLoaderFactory { public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory(); private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {} private ConfigLoaderFactory() {
}
public static ConfigLoaderFactory getInstance() { public static ConfigLoaderFactory getInstance() {
return configLoaderFactory; return configLoaderFactory;
} }
public ConfigLoader getLocalFileConfigLoader() { public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader(); ConfigLoader configLoader = new LocalFileConfigLoader();
return configLoader; return configLoader;
} }
public ConfigLoader getInMemoryConfigLoader() { public ConfigLoader getInMemoryConfigLoader() {
ConfigLoader inMemoryConfigLoader = new InMemoryConfigLoader(); ConfigLoader inMemoryConfigLoader = new InMemoryConfigLoader();
inMemoryConfigLoader.setFallBackLoader(getLocalFileConfigLoader()); inMemoryConfigLoader.setFallBackLoader(getLocalFileConfigLoader());
return inMemoryConfigLoader; return inMemoryConfigLoader;
} }
public ConfigLoader getRemoteConfigLoader() { public ConfigLoader getRemoteConfigLoader() {
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(), new ConfigServiceLocator()); ConfigLoader
remoteConfigLoader =
new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(),
new ConfigServiceLocator());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader()); // remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader; return remoteConfigLoader;
} }
public ConfigLoaderManager getConfigLoaderManager() { public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance(); ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) { if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance()); return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else { } else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance()); return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
} }
}
} }
package com.ctrip.apollo.client.loader; package com.ctrip.apollo.client.loader;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ctrip.apollo.client.enums.PropertyChangeType; import com.ctrip.apollo.client.enums.PropertyChangeType;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange; import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult; import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
...@@ -19,172 +21,186 @@ import java.util.Collections; ...@@ -19,172 +21,186 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.*; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ConfigLoaderManager { public class ConfigLoaderManager {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties"; public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderManager.class); private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderManager.class);
private ConfigLoader configLoader; private ConfigLoader configLoader;
private ConfigUtil configUtil; private ConfigUtil configUtil;
private final ExecutorService executorService; private final ExecutorService executorService;
private final AtomicLong counter; private final AtomicLong counter;
private Map<ApolloRegistry, ApolloConfig> currentApolloRegistryConfigCache; private Map<ApolloRegistry, ApolloConfig> currentApolloRegistryConfigCache;
private Map<ApolloRegistry, ApolloConfig> previousApolloRegistryConfigCache; private Map<ApolloRegistry, ApolloConfig> previousApolloRegistryConfigCache;
private List<ApolloRegistry> apolloRegistries; private List<ApolloRegistry> apolloRegistries;
public ConfigLoaderManager(ConfigLoader configLoader, ConfigUtil configUtil) { public ConfigLoaderManager(ConfigLoader configLoader, ConfigUtil configUtil) {
this.configLoader = configLoader; this.configLoader = configLoader;
this.configUtil = configUtil; this.configUtil = configUtil;
this.counter = new AtomicLong(); this.counter = new AtomicLong();
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() { this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
@Override @Override
public Thread newThread(Runnable r) { public Thread newThread(Runnable runnable) {
Thread thread = new Thread(r, "ConfigLoaderManager-" + counter.incrementAndGet()); Thread thread = new Thread(runnable, "ConfigLoaderManager-" + counter.incrementAndGet());
return thread; return thread;
} }
}); });
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap(); this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
public CompositePropertySource loadPropertySource() {
try {
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException ex) {
throw new RuntimeException("Load apollo config registry failed", ex);
} }
public CompositePropertySource loadPropertySource() { return loadPropertySourceWithApolloRegistries(apolloRegistries);
try { }
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException e) { public PropertySourceReloadResult reloadPropertySource() {
throw new RuntimeException("Load apollo config registry failed", e); CompositePropertySource composite = loadPropertySourceWithApolloRegistries(apolloRegistries);
} List<ApolloConfig>
previous =
return loadPropertySourceWithApolloRegistries(apolloRegistries); Lists.newArrayList(this.previousApolloRegistryConfigCache.values());
} List<ApolloConfig> current = Lists.newArrayList(this.currentApolloRegistryConfigCache.values());
return new PropertySourceReloadResult(composite, calcPropertyChanges(previous, current));
public PropertySourceReloadResult reloadPropertySource() { }
CompositePropertySource composite = loadPropertySourceWithApolloRegistries(apolloRegistries);
List<ApolloConfig> previous = Lists.newArrayList(this.previousApolloRegistryConfigCache.values()); /**
List<ApolloConfig> current = Lists.newArrayList(this.currentApolloRegistryConfigCache.values()); * Load property source with apollo registries provided
return new PropertySourceReloadResult(composite, calcPropertyChanges(previous, current)); * Should not be invoked in parallel since there are some operations like create/destroy cache,
* writing to files etc.
*/
private synchronized CompositePropertySource loadPropertySourceWithApolloRegistries(
List<ApolloRegistry> apolloRegistries) {
resetApolloRegistryConfigCache();
CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);
if (apolloRegistries == null || apolloRegistries.isEmpty()) {
logger.warn("No Apollo Registry found!");
return composite;
} }
try {
/** List<ApolloConfig> apolloConfigList = loadApolloConfigs(apolloRegistries);
* Load property source with apollo registries provided
* Should not be invoked in parallel since there are some operations like create/destroy cache, Collections.sort(apolloConfigList);
* writing to files etc. for (ApolloConfig apolloConfig : apolloConfigList) {
* @param apolloRegistries composite.addPropertySource(new MapPropertySource(assemblePropertySourceName(apolloConfig),
* @return apolloConfig.getConfigurations()));
*/ }
private synchronized CompositePropertySource loadPropertySourceWithApolloRegistries(List<ApolloRegistry> apolloRegistries) { return composite;
resetApolloRegistryConfigCache(); } catch (Throwable throwable) {
CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME); throw new RuntimeException("Load apollo configs failed", throwable);
if (apolloRegistries == null || apolloRegistries.isEmpty()) {
logger.warn("No Apollo Registry found!");
return composite;
}
try {
List<ApolloConfig> apolloConfigList = loadApolloConfigs(apolloRegistries);
Collections.sort(apolloConfigList);
for (ApolloConfig apolloConfig : apolloConfigList) {
composite.addPropertySource(new MapPropertySource(assemblePropertySourceName(apolloConfig), apolloConfig.getConfigurations()));
}
return composite;
} catch (Throwable throwable) {
throw new RuntimeException("Load apollo configs failed", throwable);
}
} }
}
List<PropertyChange> calcPropertyChanges(List<ApolloConfig> previous, List<ApolloConfig> current) { List<PropertyChange> calcPropertyChanges(List<ApolloConfig> previous,
Map<String, Object> previousMap = collectConfigurations(previous); List<ApolloConfig> current) {
Map<String, Object> currentMap = collectConfigurations(current); Map<String, Object> previousMap = collectConfigurations(previous);
Map<String, Object> currentMap = collectConfigurations(current);
Set<String> previousKeys = previousMap.keySet(); Set<String> previousKeys = previousMap.keySet();
Set<String> currentKeys = currentMap.keySet(); Set<String> currentKeys = currentMap.keySet();
Set<String> commonKeys = Sets.intersection(previousKeys, currentKeys); Set<String> commonKeys = Sets.intersection(previousKeys, currentKeys);
Set<String> newKeys = Sets.difference(currentKeys, commonKeys); Set<String> newKeys = Sets.difference(currentKeys, commonKeys);
Set<String> removedKeys = Sets.difference(previousKeys, commonKeys); Set<String> removedKeys = Sets.difference(previousKeys, commonKeys);
List<PropertyChange> changes = Lists.newArrayList(); List<PropertyChange> changes = Lists.newArrayList();
for (String newKey : newKeys) {
changes.add(new PropertyChange(newKey, null, currentMap.get(newKey), PropertyChangeType.NEW));
}
for (String removedKey : removedKeys) { for (String newKey : newKeys) {
changes.add(new PropertyChange(removedKey, previousMap.get(removedKey), null, PropertyChangeType.DELETED)); changes.add(new PropertyChange(newKey, null, currentMap.get(newKey), PropertyChangeType.NEW));
} }
for (String commonKey : commonKeys) {
if (previousMap.get(commonKey).equals(currentMap.get(commonKey))) {
continue;
}
changes.add(new PropertyChange(commonKey, previousMap.get(commonKey), currentMap.get(commonKey), PropertyChangeType.MODIFIED));
}
return changes; for (String removedKey : removedKeys) {
changes.add(new PropertyChange(removedKey, previousMap.get(removedKey), null,
PropertyChangeType.DELETED));
} }
Map<String, Object> collectConfigurations(List<ApolloConfig> apolloConfigs) { for (String commonKey : commonKeys) {
Collections.sort(apolloConfigs); if (previousMap.get(commonKey).equals(currentMap.get(commonKey))) {
Map<String, Object> configMap = Maps.newHashMap(); continue;
for (int i = apolloConfigs.size() - 1; i > -1; i--) { }
configMap.putAll(apolloConfigs.get(i).getConfigurations()); changes.add(
} new PropertyChange(commonKey, previousMap.get(commonKey), currentMap.get(commonKey),
return configMap; PropertyChangeType.MODIFIED));
} }
List<ApolloConfig> loadApolloConfigs(List<ApolloRegistry> apolloRegistries) throws Throwable { return changes;
List<Future<ApolloConfig>> futures = Lists.newArrayList(); }
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<ApolloConfig>() { Map<String, Object> collectConfigurations(List<ApolloConfig> apolloConfigs) {
@Override Collections.sort(apolloConfigs);
public ApolloConfig call() throws Exception { Map<String, Object> configMap = Maps.newHashMap();
return loadSingleApolloConfig(apolloRegistry); for (int i = apolloConfigs.size() - 1; i > -1; i--) {
} configMap.putAll(apolloConfigs.get(i).getConfigurations());
})); }
} return configMap;
List<ApolloConfig> apolloConfigList = Lists.newArrayList(); }
for (Future<ApolloConfig> future : futures) {
try { List<ApolloConfig> loadApolloConfigs(List<ApolloRegistry> apolloRegistries) throws Throwable {
ApolloConfig result = future.get(); List<Future<ApolloConfig>> futures = Lists.newArrayList();
if (result == null) { for (final ApolloRegistry apolloRegistry : apolloRegistries) {
continue; futures.add(executorService.submit(new Callable<ApolloConfig>() {
} @Override
apolloConfigList.add(result); public ApolloConfig call() throws Exception {
} catch (ExecutionException e) { return loadSingleApolloConfig(apolloRegistry);
throw e.getCause();
}
} }
return apolloConfigList; }));
} }
List<ApolloConfig> apolloConfigList = Lists.newArrayList();
ApolloConfig loadSingleApolloConfig(ApolloRegistry apolloRegistry) { for (Future<ApolloConfig> future : futures) {
ApolloConfig result = configLoader.loadApolloConfig(apolloRegistry, getPreviousApolloConfig(apolloRegistry)); try {
ApolloConfig result = future.get();
if (result == null) { if (result == null) {
logger.error("Loaded config null..."); continue;
return null;
} }
logger.info("Loaded config: {}", result); apolloConfigList.add(result);
updateCurrentApolloConfigCache(apolloRegistry, result); } catch (ExecutionException ex) {
throw ex.getCause();
return result; }
} }
return apolloConfigList;
void resetApolloRegistryConfigCache() { }
this.previousApolloRegistryConfigCache = currentApolloRegistryConfigCache;
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap(); ApolloConfig loadSingleApolloConfig(ApolloRegistry apolloRegistry) {
ApolloConfig
result =
configLoader.loadApolloConfig(apolloRegistry, getPreviousApolloConfig(apolloRegistry));
if (result == null) {
logger.error("Loaded config null...");
return null;
} }
logger.info("Loaded config: {}", result);
updateCurrentApolloConfigCache(apolloRegistry, result);
ApolloConfig getPreviousApolloConfig(ApolloRegistry apolloRegistry) { return result;
return previousApolloRegistryConfigCache.get(apolloRegistry); }
}
void updateCurrentApolloConfigCache(ApolloRegistry apolloRegistry, ApolloConfig apolloConfig) { void resetApolloRegistryConfigCache() {
currentApolloRegistryConfigCache.put(apolloRegistry, apolloConfig); this.previousApolloRegistryConfigCache = currentApolloRegistryConfigCache;
} this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
private String assemblePropertySourceName(ApolloConfig apolloConfig) { ApolloConfig getPreviousApolloConfig(ApolloRegistry apolloRegistry) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(), apolloConfig.getVersion(), apolloConfig.getReleaseId()); return previousApolloRegistryConfigCache.get(apolloRegistry);
} }
void updateCurrentApolloConfigCache(ApolloRegistry apolloRegistry, ApolloConfig apolloConfig) {
currentApolloRegistryConfigCache.put(apolloRegistry, apolloConfig);
}
private String assemblePropertySourceName(ApolloConfig apolloConfig) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(),
apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
} }
package com.ctrip.apollo.client.loader; package com.ctrip.apollo.client.loader;
import java.net.URI; import com.ctrip.apollo.client.env.ClientEnvironment;
import java.util.ArrayList; import com.ctrip.apollo.core.serivce.ApolloService;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.env.ClientEnvironment; import java.net.URI;
import com.ctrip.apollo.core.serivce.ApolloService; import java.util.ArrayList;
import java.util.List;
public class ConfigServiceLocator { public class ConfigServiceLocator {
...@@ -31,8 +31,8 @@ public class ConfigServiceLocator { ...@@ -31,8 +31,8 @@ public class ConfigServiceLocator {
serviceCaches.add(service); serviceCaches.add(service);
} }
} }
} catch (Exception e) { } catch (Exception ex) {
logger.warn(e.getMessage()); logger.warn(ex.getMessage());
} }
return serviceCaches; return serviceCaches;
} }
......
...@@ -3,6 +3,7 @@ package com.ctrip.apollo.client.loader.impl; ...@@ -3,6 +3,7 @@ package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader; import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -10,26 +11,29 @@ import org.slf4j.LoggerFactory; ...@@ -10,26 +11,29 @@ import org.slf4j.LoggerFactory;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public abstract class AbstractConfigLoader implements ConfigLoader { public abstract class AbstractConfigLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class); private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class);
private ConfigLoader fallback; private ConfigLoader fallback;
@Override @Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try { try {
return doLoadApolloConfig(apolloRegistry, previous); return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable e) { } catch (Throwable ex) {
if (this.fallback == null) { if (this.fallback == null) {
throw new RuntimeException(String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), e); throw new RuntimeException(
} String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), ex);
logger.error("Load Config via {} failed, try to use its fallback {} to load", getClass().getSimpleName(), fallback.getClass().getSimpleName(), e); }
return this.fallback.loadApolloConfig(apolloRegistry, previous); logger.error("Load Config via {} failed, try to use its fallback {} to load",
} getClass().getSimpleName(), fallback.getClass().getSimpleName(), ex);
return this.fallback.loadApolloConfig(apolloRegistry, previous);
} }
}
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous); protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry,
ApolloConfig previous);
@Override @Override
public void setFallBackLoader(ConfigLoader configLoader) { public void setFallBackLoader(ConfigLoader configLoader) {
this.fallback = configLoader; this.fallback = configLoader;
} }
} }
...@@ -6,10 +6,10 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -6,10 +6,10 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class InMemoryConfigLoader extends AbstractConfigLoader{ public class InMemoryConfigLoader extends AbstractConfigLoader {
@Override @Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null; return null;
} }
} }
...@@ -5,11 +5,12 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -5,11 +5,12 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
/** /**
* Load config from local backup file * Load config from local backup file
*
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class LocalFileConfigLoader extends AbstractConfigLoader{ public class LocalFileConfigLoader extends AbstractConfigLoader {
@Override @Override
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null; return null;
} }
} }
package com.ctrip.apollo.client.loader.impl; package com.ctrip.apollo.client.loader.impl;
import com.google.common.collect.Maps;
import com.ctrip.apollo.client.loader.ConfigServiceLocator; import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService; import com.ctrip.apollo.core.serivce.ApolloService;
import com.google.common.collect.Maps;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
...@@ -24,76 +26,80 @@ import java.util.Map; ...@@ -24,76 +26,80 @@ import java.util.Map;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class RemoteConfigLoader extends AbstractConfigLoader { public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class); private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final ConfigUtil configUtil; private final ConfigUtil configUtil;
private final ConfigServiceLocator serviceLocator; private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil, ConfigServiceLocator locator) { public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil,
this.restTemplate = restTemplate; ConfigServiceLocator locator) {
this.configUtil = configUtil; this.restTemplate = restTemplate;
this.serviceLocator = locator; this.configUtil = configUtil;
this.serviceLocator = locator;
}
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster,
ApolloRegistry apolloRegistry, ApolloConfig previousConfig) {
long appId = apolloRegistry.getAppId();
String version = apolloRegistry.getVersion();
logger.info("Loading config from {}, appId={}, cluster={}, version={}", uri, appId, cluster,
version);
String path = "/config/{appId}/{cluster}";
Map<String, Object> paramMap = Maps.newHashMap();
paramMap.put("appId", appId);
paramMap.put("cluster", cluster);
if (StringUtils.hasText(version)) {
path = path + "/{version}";
paramMap.put("version", version);
}
if (previousConfig != null) {
path = path + "?releaseId={releaseId}";
paramMap.put("releaseId", previousConfig.getReleaseId());
} }
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster, ApolloRegistry apolloRegistry, ApolloConfig previousConfig) { ResponseEntity<ApolloConfig> response;
long appId = apolloRegistry.getAppId();
String version = apolloRegistry.getVersion(); try {
// TODO retry
logger.info("Loading config from {}, appId={}, cluster={}, version={}", uri, appId, cluster, version); response = restTemplate.exchange(uri
String path = "/config/{appId}/{cluster}"; + path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, paramMap);
Map<String, Object> paramMap = Maps.newHashMap(); } catch (Throwable ex) {
paramMap.put("appId", appId); throw ex;
paramMap.put("cluster", cluster); }
if (StringUtils.hasText(version)) { if (response == null) {
path = path + "/{version}"; throw new RuntimeException("Load apollo config failed, response is null");
paramMap.put("version", version); }
}
if (previousConfig != null) { if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
path = path + "?releaseId={releaseId}"; return null;
paramMap.put("releaseId", previousConfig.getReleaseId());
}
ResponseEntity<ApolloConfig> response;
try {
// TODO retry
response = restTemplate.exchange(uri
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, paramMap);
} catch (Throwable e) {
throw e;
}
if (response == null) {
throw new RuntimeException("Load apollo config failed, response is null");
}
if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
return null;
}
if (response.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException(String.format("Load apollo config failed, response status %s", response.getStatusCode()));
}
ApolloConfig result = response.getBody();
return result;
} }
@Override if (response.getStatusCode() != HttpStatus.OK) {
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { throw new RuntimeException(
ApolloConfig result = this.getRemoteConfig(restTemplate, String.format("Load apollo config failed, response status %s", response.getStatusCode()));
getConfigServiceUrl(), configUtil.getCluster(), }
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result ApolloConfig result = response.getBody();
return result == null ? previous : result; return result;
}
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate,
getConfigServiceUrl(), configUtil.getCluster(),
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result
return result == null ? previous : result;
}
private String getConfigServiceUrl() {
List<ApolloService> services = serviceLocator.getConfigServices();
if (services.size() == 0) {
throw new RuntimeException("No available config service");
} }
return services.get(0).getHomepageUrl();
private String getConfigServiceUrl() { }
List<ApolloService> services = serviceLocator.getConfigServices(); }
if(services.size()==0){
throw new RuntimeException("No available config service");
}
return services.get(0).getHomepageUrl();
}
}
\ No newline at end of file
...@@ -6,31 +6,31 @@ import com.google.common.base.MoreObjects; ...@@ -6,31 +6,31 @@ import com.google.common.base.MoreObjects;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ApolloRegistry { public class ApolloRegistry {
private long appId; private long appId;
private String version; private String version;
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public String getVersion() { public String getVersion() {
return version; return version;
} }
public void setVersion(String version) { public void setVersion(String version) {
this.version = version; this.version = version;
} }
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.omitNullValues() .omitNullValues()
.add("appId", appId) .add("appId", appId)
.add("version", version) .add("version", version)
.toString(); .toString();
} }
} }
...@@ -6,47 +6,48 @@ import com.ctrip.apollo.client.enums.PropertyChangeType; ...@@ -6,47 +6,48 @@ import com.ctrip.apollo.client.enums.PropertyChangeType;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class PropertyChange { public class PropertyChange {
private String propertyName; private String propertyName;
private Object oldValue; private Object oldValue;
private Object newValue; private Object newValue;
private PropertyChangeType changeType; private PropertyChangeType changeType;
public PropertyChange(String propertyName, Object oldValue, Object newValue, PropertyChangeType changeType) { public PropertyChange(String propertyName, Object oldValue, Object newValue,
this.propertyName = propertyName; PropertyChangeType changeType) {
this.oldValue = oldValue; this.propertyName = propertyName;
this.newValue = newValue; this.oldValue = oldValue;
this.changeType = changeType; this.newValue = newValue;
} this.changeType = changeType;
}
public String getPropertyName() {
return propertyName; public String getPropertyName() {
} return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName; public void setPropertyName(String propertyName) {
} this.propertyName = propertyName;
}
public Object getOldValue() {
return oldValue; public Object getOldValue() {
} return oldValue;
}
public void setOldValue(Object oldValue) {
this.oldValue = oldValue; public void setOldValue(Object oldValue) {
} this.oldValue = oldValue;
}
public Object getNewValue() {
return newValue; public Object getNewValue() {
} return newValue;
}
public void setNewValue(Object newValue) {
this.newValue = newValue; public void setNewValue(Object newValue) {
} this.newValue = newValue;
}
public PropertyChangeType getChangeType() {
return changeType; public PropertyChangeType getChangeType() {
} return changeType;
}
public void setChangeType(PropertyChangeType changeType) {
this.changeType = changeType; public void setChangeType(PropertyChangeType changeType) {
} this.changeType = changeType;
}
} }
package com.ctrip.apollo.client.model; package com.ctrip.apollo.client.model;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
import java.util.List; import java.util.List;
...@@ -9,36 +10,37 @@ import java.util.List; ...@@ -9,36 +10,37 @@ import java.util.List;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class PropertySourceReloadResult { public class PropertySourceReloadResult {
private CompositePropertySource propertySource; private CompositePropertySource propertySource;
private List<PropertyChange> changes; private List<PropertyChange> changes;
public PropertySourceReloadResult(CompositePropertySource propertySource) { public PropertySourceReloadResult(CompositePropertySource propertySource) {
this.propertySource = propertySource; this.propertySource = propertySource;
changes = Lists.newArrayList(); changes = Lists.newArrayList();
} }
public PropertySourceReloadResult(CompositePropertySource propertySource, List<PropertyChange> changes) { public PropertySourceReloadResult(CompositePropertySource propertySource,
this.propertySource = propertySource; List<PropertyChange> changes) {
this.changes = changes; this.propertySource = propertySource;
} this.changes = changes;
}
public CompositePropertySource getPropertySource() {
return propertySource; public CompositePropertySource getPropertySource() {
} return propertySource;
}
public void setPropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource; public void setPropertySource(CompositePropertySource propertySource) {
} this.propertySource = propertySource;
}
public List<PropertyChange> getChanges() {
return changes; public List<PropertyChange> getChanges() {
} return changes;
}
public void setChanges(List<PropertyChange> changes) {
this.changes = changes; public void setChanges(List<PropertyChange> changes) {
} this.changes = changes;
}
public boolean hasChanges() {
return !changes.isEmpty(); public boolean hasChanges() {
} return !changes.isEmpty();
}
} }
...@@ -7,18 +7,18 @@ import org.slf4j.LoggerFactory; ...@@ -7,18 +7,18 @@ import org.slf4j.LoggerFactory;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ClassLoaderUtil { public class ClassLoaderUtil {
private static final Logger logger = LoggerFactory.getLogger(ClassLoaderUtil.class); private static final Logger logger = LoggerFactory.getLogger(ClassLoaderUtil.class);
private static ClassLoader loader = Thread.currentThread().getContextClassLoader(); private static ClassLoader loader = Thread.currentThread().getContextClassLoader();
static { static {
if (loader == null) { if (loader == null) {
logger.info("Using system class loader"); logger.info("Using system class loader");
loader = ClassLoader.getSystemClassLoader(); loader = ClassLoader.getSystemClassLoader();
}
} }
}
public static ClassLoader getLoader() { public static ClassLoader getLoader() {
return loader; return loader;
} }
} }
package com.ctrip.apollo.client.util; package com.ctrip.apollo.client.util;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable; import com.google.common.collect.FluentIterable;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
...@@ -21,68 +23,69 @@ import java.util.concurrent.TimeUnit; ...@@ -21,68 +23,69 @@ import java.util.concurrent.TimeUnit;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ConfigUtil { public class ConfigUtil {
public static final String APOLLO_PROPERTY = "apollo.properties"; public static final String APOLLO_PROPERTY = "apollo.properties";
//TODO read from config? //TODO read from config?
private static final int refreshInterval = 5; private static final int refreshInterval = 5;
private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES; private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
private static ConfigUtil configUtil = new ConfigUtil(); private static ConfigUtil configUtil = new ConfigUtil();
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private ConfigUtil() {
}
public static ConfigUtil getInstance() { private ConfigUtil() {
return configUtil; }
}
public String getCluster() { public static ConfigUtil getInstance() {
// TODO return the actual cluster return configUtil;
return "default"; }
}
public void setApplicationContext(ApplicationContext applicationContext) { public String getCluster() {
this.applicationContext = applicationContext; // TODO return the actual cluster
} return "default";
}
public int getRefreshInterval() { public void setApplicationContext(ApplicationContext applicationContext) {
return refreshInterval; this.applicationContext = applicationContext;
} }
public TimeUnit getRefreshTimeUnit() { public int getRefreshInterval() {
return refreshIntervalTimeUnit; return refreshInterval;
} }
public List<ApolloRegistry> loadApolloRegistries() throws IOException { public TimeUnit getRefreshTimeUnit() {
List<URL> resourceUrls = return refreshIntervalTimeUnit;
Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY)); }
List<ApolloRegistry> registries =
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() { public List<ApolloRegistry> loadApolloRegistries() throws IOException {
@Override List<URL> resourceUrls =
public ApolloRegistry apply(URL input) { Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY));
Properties properties = loadPropertiesFromResourceURL(input); List<ApolloRegistry> registries =
if (properties == null || !properties.containsKey(Constants.APP_ID)) { FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() {
return null; @Override
} public ApolloRegistry apply(URL input) {
ApolloRegistry registry = new ApolloRegistry(); Properties properties = loadPropertiesFromResourceURL(input);
registry.setAppId(Long.parseLong(properties.getProperty(Constants.APP_ID))); if (properties == null || !properties.containsKey(Constants.APP_ID)) {
registry.setVersion(properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME)); return null;
return registry; }
} ApolloRegistry registry = new ApolloRegistry();
}).filter(Predicates.notNull()).toList(); registry.setAppId(Long.parseLong(properties.getProperty(Constants.APP_ID)));
return registries; registry.setVersion(
} properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME));
return registry;
}
}).filter(Predicates.notNull()).toList();
return registries;
}
Properties loadPropertiesFromResourceURL(URL resourceUrl) { Properties loadPropertiesFromResourceURL(URL resourceUrl) {
Resource resource = applicationContext.getResource(resourceUrl.toExternalForm()); Resource resource = applicationContext.getResource(resourceUrl.toExternalForm());
if (resource == null || !resource.exists()) { if (resource == null || !resource.exists()) {
return null; return null;
} }
try { try {
return PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8")); return PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8"));
} catch (IOException e) { } catch (IOException ex) {
e.printStackTrace(); ex.printStackTrace();
}
return null;
} }
return null;
}
} }
...@@ -3,14 +3,15 @@ package com.ctrip.apollo.client; ...@@ -3,14 +3,15 @@ package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderManagerTest; import com.ctrip.apollo.client.loader.ConfigLoaderManagerTest;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest; import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest;
import com.ctrip.apollo.client.util.ConfigUtilTest; import com.ctrip.apollo.client.util.ConfigUtilTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({
ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class, ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class,
ConfigUtilTest.class ConfigUtilTest.class
}) })
public class AllTests { public class AllTests {
......
...@@ -3,6 +3,7 @@ package com.ctrip.apollo.client; ...@@ -3,6 +3,7 @@ package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderManager; import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange; import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult; import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -25,110 +26,126 @@ import java.util.List; ...@@ -25,110 +26,126 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ApolloConfigManagerTest { public class ApolloConfigManagerTest {
private ApolloConfigManager apolloConfigManager; private ApolloConfigManager apolloConfigManager;
@Mock @Mock
private ConfigLoaderManager configLoaderManager; private ConfigLoaderManager configLoaderManager;
@Mock @Mock
private ConfigurableApplicationContext applicationContext; private ConfigurableApplicationContext applicationContext;
@Mock @Mock
private ConfigurableEnvironment env; private ConfigurableEnvironment env;
@Mock @Mock
private MutablePropertySources mutablePropertySources; private MutablePropertySources mutablePropertySources;
@Mock @Mock
private BeanDefinitionRegistry beanDefinitionRegistry; private BeanDefinitionRegistry beanDefinitionRegistry;
@Mock @Mock
private RefreshScope scope; private RefreshScope scope;
@Before @Before
public void setUp() { public void setUp() {
apolloConfigManager = spy(new ApolloConfigManager()); apolloConfigManager = spy(new ApolloConfigManager());
when(applicationContext.getEnvironment()).thenReturn(env); when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources); when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfigManager.setApplicationContext(applicationContext); apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager); ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloConfigManager, "scope", scope); ReflectionTestUtils.setField(apolloConfigManager, "scope", scope);
} }
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
AtomicReference<ApolloConfigManager> singletonProtector = AtomicReference<ApolloConfigManager> singletonProtector =
(AtomicReference<ApolloConfigManager>)ReflectionTestUtils.getField(ApolloConfigManager.class, "singletonProtector"); (AtomicReference<ApolloConfigManager>) ReflectionTestUtils
singletonProtector.set(null); .getField(ApolloConfigManager.class, "singletonProtector");
} singletonProtector.set(null);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() { @Test(expected = RuntimeException.class)
ApplicationContext someInvalidApplication = mock(ApplicationContext.class); public void testInvalidApplicationContext() {
apolloConfigManager.setApplicationContext(someInvalidApplication); ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
} apolloConfigManager.setApplicationContext(someInvalidApplication);
}
@Test
public void testInitializePropertySourceSuccessfully() { @Test
CompositePropertySource somePropertySource = mock(CompositePropertySource.class); public void testInitializePropertySourceSuccessfully() {
final ArgumentCaptor<CompositePropertySource> captor = ArgumentCaptor.forClass(CompositePropertySource.class); CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource>
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource); captor =
ArgumentCaptor.forClass(CompositePropertySource.class);
apolloConfigManager.initializePropertySource();
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture()); apolloConfigManager.initializePropertySource();
final CompositePropertySource insertedPropertySource = captor.getValue(); verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
assertEquals(insertedPropertySource, somePropertySource);
} final CompositePropertySource insertedPropertySource = captor.getValue();
@Test assertEquals(insertedPropertySource, somePropertySource);
public void testPostProcessBeanDefinitionRegistry() { }
doNothing().when(apolloConfigManager).initializePropertySource();
@Test
apolloConfigManager.postProcessBeanDefinitionRegistry(beanDefinitionRegistry); public void testPostProcessBeanDefinitionRegistry() {
doNothing().when(apolloConfigManager).initializePropertySource();
verify(beanDefinitionRegistry, times(2)).registerBeanDefinition(anyString(), any(BeanDefinition.class));
} apolloConfigManager.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);
@Test verify(beanDefinitionRegistry, times(2))
public void testUpdatePropertySourceWithChanges() throws Exception { .registerBeanDefinition(anyString(), any(BeanDefinition.class));
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class); }
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = mock(List.class); @Test
public void testUpdatePropertySourceWithChanges() throws Exception {
when(somePropertySourceReloadResult.hasChanges()).thenReturn(true); PropertySourceReloadResult
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource); somePropertySourceReloadResult =
when(somePropertySourceReloadResult.getChanges()).thenReturn(someChanges); mock(PropertySourceReloadResult.class);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult); CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = mock(List.class);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
when(somePropertySourceReloadResult.hasChanges()).thenReturn(true);
assertEquals(someChanges, result); when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
verify(scope, times(1)).refreshAll(); when(somePropertySourceReloadResult.getChanges()).thenReturn(someChanges);
} when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
@Test List<PropertyChange> result = apolloConfigManager.updatePropertySource();
public void testUpdatePropertySourceWithNoChange() throws Exception {
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class); assertEquals(someChanges, result);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class); verify(scope, times(1)).refreshAll();
List<PropertyChange> emptyChanges = Collections.emptyList(); }
when(somePropertySourceReloadResult.hasChanges()).thenReturn(false); @Test
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource); public void testUpdatePropertySourceWithNoChange() throws Exception {
when(somePropertySourceReloadResult.getChanges()).thenReturn(emptyChanges); PropertySourceReloadResult
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult); somePropertySourceReloadResult =
mock(PropertySourceReloadResult.class);
List<PropertyChange> result = apolloConfigManager.updatePropertySource(); CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> emptyChanges = Collections.emptyList();
assertEquals(emptyChanges, result);
verify(scope, never()).refreshAll(); when(somePropertySourceReloadResult.hasChanges()).thenReturn(false);
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
} when(somePropertySourceReloadResult.getChanges()).thenReturn(emptyChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
assertEquals(emptyChanges, result);
verify(scope, never()).refreshAll();
}
} }
package com.ctrip.apollo.client.loader; package com.ctrip.apollo.client.loader;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ctrip.apollo.client.enums.PropertyChangeType; import com.ctrip.apollo.client.enums.PropertyChangeType;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange; import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult; import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.ObjectArrays;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -26,173 +26,192 @@ import java.util.Map; ...@@ -26,173 +26,192 @@ import java.util.Map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyList;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigLoaderManagerTest { public class ConfigLoaderManagerTest {
private ConfigLoaderManager configLoaderManager; private ConfigLoaderManager configLoaderManager;
@Mock @Mock
private ConfigLoader configLoader; private ConfigLoader configLoader;
@Mock @Mock
private ConfigUtil configUtil; private ConfigUtil configUtil;
@Before @Before
public void setUp() { public void setUp() {
configLoaderManager = spy(new ConfigLoaderManager(configLoader, configUtil)); configLoaderManager = spy(new ConfigLoaderManager(configLoader, configUtil));
} }
@Test @Test
public void testLoadPropertySource() throws Exception { public void testLoadPropertySource() throws Exception {
long someAppId = 100; long someAppId = 100;
long anotherAppId = 101; long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry(anotherAppId, "anotherVersion"); ApolloRegistry
ApolloConfig someApolloConfig = mock(ApolloConfig.class); anotherApolloRegistry =
ApolloConfig anotherApolloConfig = mock(ApolloConfig.class); assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
Map<String, Object> someMap = mock(Map.class); ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> anotherMap = mock(Map.class); ApolloConfig anotherApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
when(someApolloConfig.getAppId()).thenReturn(someAppId); Map<String, Object> anotherMap = mock(Map.class);
when(someApolloConfig.getAppId()).thenReturn(anotherAppId);
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry)); when(someApolloConfig.getAppId()).thenReturn(someAppId);
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry); when(someApolloConfig.getAppId()).thenReturn(anotherAppId);
doReturn(anotherApolloConfig).when(configLoaderManager).loadSingleApolloConfig(anotherApolloRegistry); when(configUtil.loadApolloRegistries())
when(someApolloConfig.getConfigurations()).thenReturn(someMap); .thenReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry));
when(anotherApolloConfig.getConfigurations()).thenReturn(anotherMap); doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
doReturn(anotherApolloConfig).when(configLoaderManager)
CompositePropertySource result = configLoaderManager.loadPropertySource(); .loadSingleApolloConfig(anotherApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
assertEquals(2, result.getPropertySources().size()); when(anotherApolloConfig.getConfigurations()).thenReturn(anotherMap);
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() { CompositePropertySource result = configLoaderManager.loadPropertySource();
@Override
public Map<String, Object> apply(PropertySource<?> input) { assertEquals(2, result.getPropertySources().size());
return (Map<String, Object>)input.getSource();
} List<Map<String, Object>>
}).toList(); resultMaps =
FluentIterable.from(result.getPropertySources())
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap, anotherMap))); .transform(new Function<PropertySource<?>, Map<String, Object>>() {
} @Override
public Map<String, Object> apply(PropertySource<?> input) {
@Test(expected = RuntimeException.class) return (Map<String, Object>) input.getSource();
public void testLoadPropertySourceWithError() throws Exception { }
Exception someException = mock(Exception.class); }).toList();
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap, anotherMap)));
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry)); }
doThrow(someException).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry); @Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
configLoaderManager.loadPropertySource(); Exception someException = mock(Exception.class);
} long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
@Test when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry));
public void testLoadApolloConfigsWithNoApolloRegistry() throws Exception {
when(configUtil.loadApolloRegistries()).thenReturn(null); doThrow(someException).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
CompositePropertySource result = configLoaderManager.loadPropertySource(); configLoaderManager.loadPropertySource();
}
assertTrue(result.getPropertySources().isEmpty());
} @Test
public void testLoadApolloConfigsWithNoApolloRegistry() throws Exception {
@Test when(configUtil.loadApolloRegistries()).thenReturn(null);
public void testLoadSingleApolloConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class); CompositePropertySource result = configLoaderManager.loadPropertySource();
Map<String, Object> someMap = Maps.newHashMap();
long someAppId = 100; assertTrue(result.getPropertySources().isEmpty());
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); }
ApolloConfig previousConfig = null;
@Test
doReturn(null).when(configLoaderManager).getPreviousApolloConfig(someApolloRegistry); public void testLoadSingleApolloConfig() throws Exception {
when(someApolloConfig.getConfigurations()).thenReturn(someMap); ApolloConfig someApolloConfig = mock(ApolloConfig.class);
when(configLoader.loadApolloConfig(someApolloRegistry, previousConfig)).thenReturn(someApolloConfig); Map<String, Object> someMap = Maps.newHashMap();
long someAppId = 100;
ApolloConfig result = configLoaderManager.loadSingleApolloConfig(someApolloRegistry); ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
assertEquals(someMap, result.getConfigurations());
} doReturn(null).when(configLoaderManager).getPreviousApolloConfig(someApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
@Test when(configLoader.loadApolloConfig(someApolloRegistry, previousConfig))
public void testReloadPropertySource() throws Exception { .thenReturn(someApolloConfig);
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); ApolloConfig result = configLoaderManager.loadSingleApolloConfig(someApolloRegistry);
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class); assertEquals(someMap, result.getConfigurations());
List<PropertyChange> someChanges = mock(List.class); }
ReflectionTestUtils.setField(configLoaderManager, "apolloRegistries", Lists.newArrayList(someApolloRegistry));
@Test
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry); public void testReloadPropertySource() throws Exception {
when(someApolloConfig.getAppId()).thenReturn(someAppId); long someAppId = 100;
when(someApolloConfig.getConfigurations()).thenReturn(someMap); ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
doReturn(someChanges).when(configLoaderManager).calcPropertyChanges(anyList(), anyList()); ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
PropertySourceReloadResult result = configLoaderManager.reloadPropertySource(); List<PropertyChange> someChanges = mock(List.class);
ReflectionTestUtils
assertEquals(1, result.getPropertySource().getPropertySources().size()); .setField(configLoaderManager, "apolloRegistries", Lists.newArrayList(someApolloRegistry));
assertEquals(someChanges, result.getChanges());
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySource().getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() { doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
@Override when(someApolloConfig.getAppId()).thenReturn(someAppId);
public Map<String, Object> apply(PropertySource<?> input) { when(someApolloConfig.getConfigurations()).thenReturn(someMap);
return (Map<String, Object>)input.getSource(); doReturn(someChanges).when(configLoaderManager).calcPropertyChanges(anyList(), anyList());
}
}).toList(); PropertySourceReloadResult result = configLoaderManager.reloadPropertySource();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap))); assertEquals(1, result.getPropertySource().getPropertySources().size());
} assertEquals(someChanges, result.getChanges());
List<Map<String, Object>>
@Test resultMaps =
public void testCalcPropertyChanges() throws Exception { FluentIterable.from(result.getPropertySource().getPropertySources())
long someAppId = 1; .transform(new Function<PropertySource<?>, Map<String, Object>>() {
Map<String, Object> someConfig = Maps.newHashMap(); @Override
someConfig.put("key1", "val1"); public Map<String, Object> apply(PropertySource<?> input) {
someConfig.put("key2", "val2"); return (Map<String, Object>) input.getSource();
}
Map<String, Object> anotherConfig = Maps.newHashMap(); }).toList();
anotherConfig.put("key1", "val11");
anotherConfig.put("key3", "val3"); assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap)));
}
List<ApolloConfig> previous = Lists.newArrayList(assembleApolloConfig(someAppId, someConfig));
List<ApolloConfig> current = Lists.newArrayList(assembleApolloConfig(someAppId, anotherConfig)); @Test
public void testCalcPropertyChanges() throws Exception {
List<PropertyChange> changes = configLoaderManager.calcPropertyChanges(previous, current); long someAppId = 1;
Map<String, Object> someConfig = Maps.newHashMap();
assertEquals(3, changes.size()); someConfig.put("key1", "val1");
someConfig.put("key2", "val2");
List<String> changeResult = FluentIterable.from(changes).transform(new Function<PropertyChange, String>() {
@Override Map<String, Object> anotherConfig = Maps.newHashMap();
public String apply(PropertyChange input) { anotherConfig.put("key1", "val11");
return String.format("%s-%s", input.getPropertyName(), input.getChangeType()); anotherConfig.put("key3", "val3");
}
List<ApolloConfig> previous = Lists.newArrayList(assembleApolloConfig(someAppId, someConfig));
List<ApolloConfig> current = Lists.newArrayList(assembleApolloConfig(someAppId, anotherConfig));
List<PropertyChange> changes = configLoaderManager.calcPropertyChanges(previous, current);
assertEquals(3, changes.size());
List<String>
changeResult =
FluentIterable.from(changes).transform(new Function<PropertyChange, String>() {
@Override
public String apply(PropertyChange input) {
return String.format("%s-%s", input.getPropertyName(), input.getChangeType());
}
}).toList(); }).toList();
assertTrue(changeResult.containsAll( assertTrue(changeResult.containsAll(
Lists.newArrayList( Lists.newArrayList(
"key1-" + PropertyChangeType.MODIFIED, "key1-" + PropertyChangeType.MODIFIED,
"key2-" + PropertyChangeType.DELETED, "key2-" + PropertyChangeType.DELETED,
"key3-" + PropertyChangeType.NEW "key3-" + PropertyChangeType.NEW
))); )));
} }
ApolloConfig assembleApolloConfig(long appId, Map<String, Object> configurations) { ApolloConfig assembleApolloConfig(long appId, Map<String, Object> configurations) {
String someCluster = "someCluster"; String someCluster = "someCluster";
String someVersion = "someVersion"; String someVersion = "someVersion";
long someReleaseId = 1; long someReleaseId = 1;
ApolloConfig config = new ApolloConfig(appId, someCluster, someVersion, someReleaseId); ApolloConfig config = new ApolloConfig(appId, someCluster, someVersion, someReleaseId);
config.setConfigurations(configurations); config.setConfigurations(configurations);
return config; return config;
} }
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) { private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry(); ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId); someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion); someApolloRegistry.setVersion(someVersion);
return someApolloRegistry; return someApolloRegistry;
} }
} }
package com.ctrip.apollo.client.loader.impl; package com.ctrip.apollo.client.loader.impl;
import static org.junit.Assert.assertEquals; import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import static org.junit.Assert.assertNull; import com.ctrip.apollo.client.model.ApolloRegistry;
import static org.mockito.Matchers.any; import com.ctrip.apollo.client.util.ConfigUtil;
import static org.mockito.Matchers.anyMap; import com.ctrip.apollo.core.dto.ApolloConfig;
import static org.mockito.Matchers.anyString; import com.ctrip.apollo.core.serivce.ApolloService;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -25,116 +17,132 @@ import org.springframework.http.HttpStatus; ...@@ -25,116 +17,132 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.loader.ConfigServiceLocator; import java.util.ArrayList;
import com.ctrip.apollo.client.model.ApolloRegistry; import java.util.List;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import static org.junit.Assert.assertEquals;
import com.ctrip.apollo.core.serivce.ApolloService; import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RemoteConfigLoaderTest { public class RemoteConfigLoaderTest {
private RemoteConfigLoader remoteConfigLoader; private RemoteConfigLoader remoteConfigLoader;
@Mock @Mock
private RestTemplate restTemplate; private RestTemplate restTemplate;
private ConfigUtil configUtil; private ConfigUtil configUtil;
@Mock @Mock
private ConfigServiceLocator serviceLocater; private ConfigServiceLocator serviceLocater;
@Mock @Mock
private ResponseEntity<ApolloConfig> someResponse; private ResponseEntity<ApolloConfig> someResponse;
@Before @Before
public void setUp() { public void setUp() {
configUtil = spy(ConfigUtil.getInstance()); configUtil = spy(ConfigUtil.getInstance());
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater)); remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater));
} }
@Test @Test
public void testLoadApolloConfig() throws Exception { public void testLoadApolloConfig() throws Exception {
String someServerUrl = "http://someUrl"; String someServerUrl = "http://someUrl";
String someCluster = "some cluster"; String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class); ApolloConfig apolloConfig = mock(ApolloConfig.class);
long someAppId = 1; long someAppId = 1;
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null; ApolloConfig previousConfig = null;
ApolloService someService = new ApolloService();
ApolloService someService = new ApolloService(); someService.setHomepageUrl(someServerUrl);
someService.setHomepageUrl(someServerUrl); List<ApolloService> someServices = new ArrayList<>();
List<ApolloService> someServices = new ArrayList<>(); someServices.add(someService);
someServices.add(someService); when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(serviceLocater.getConfigServices()).thenReturn(someServices); when(configUtil.getCluster()).thenReturn(someCluster);
when(configUtil.getCluster()).thenReturn(someCluster); doReturn(apolloConfig).when(remoteConfigLoader)
doReturn(apolloConfig).when(remoteConfigLoader) .getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
assertEquals(apolloConfig, result);
assertEquals(apolloConfig, result); }
}
@Test
@Test public void testGetRemoteConfig() throws Exception {
public void testGetRemoteConfig() throws Exception { long someAppId = 1;
long someAppId = 1; String someServerUrl = "http://someServer";
String someServerUrl = "http://someServer"; String someClusterName = "someCluster";
String someClusterName = "someCluster"; String someVersionName = "someVersion";
String someVersionName = "someVersion"; ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloConfig someApolloConfig = mock(ApolloConfig.class); ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); ApolloConfig previousConfig = null;
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK); when(someResponse.getBody()).thenReturn(someApolloConfig);
when(someResponse.getBody()).thenReturn(someApolloConfig); when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
ApolloConfig
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig); result =
remoteConfigLoader
assertEquals(someApolloConfig, result); .getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
} previousConfig);
@Test(expected = RuntimeException.class) assertEquals(someApolloConfig, result);
public void testGetRemoteConfigWithServerError() throws Exception { }
long someAppId = 1;
String someServerUrl = "http://someServer"; @Test(expected = RuntimeException.class)
String someClusterName = "someCluster"; public void testGetRemoteConfigWithServerError() throws Exception {
String someVersionName = "someVersion"; long someAppId = 1;
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); String someServerUrl = "http://someServer";
ApolloConfig previousConfig = null; String someClusterName = "someCluster";
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR; String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
when(someResponse.getStatusCode()).thenReturn(someErrorCode); ApolloConfig previousConfig = null;
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
when(someResponse.getStatusCode()).thenReturn(someErrorCode);
remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig); when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
} eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
@Test remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
public void testGetRemoteConfigWith304Response() throws Exception { previousConfig);
long someAppId = 1; }
String someServerUrl = "http://someServer";
String someClusterName = "someCluster"; @Test
String someVersionName = "someVersion"; public void testGetRemoteConfigWith304Response() throws Exception {
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); long someAppId = 1;
ApolloConfig previousConfig = null; String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED); String someVersionName = "someVersion";
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); ApolloConfig previousConfig = null;
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig); when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
assertNull(result); eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
}
ApolloConfig
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) { result =
ApolloRegistry someApolloRegistry = new ApolloRegistry(); remoteConfigLoader
someApolloRegistry.setAppId(someAppId); .getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
someApolloRegistry.setVersion(someVersion); previousConfig);
return someApolloRegistry; assertNull(result);
} }
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
return someApolloRegistry;
}
} }
package com.ctrip.apollo.client.util; package com.ctrip.apollo.client.util;
import com.google.common.collect.Lists;
import com.ctrip.apollo.client.constants.Constants; import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.google.common.collect.Lists;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -19,60 +21,65 @@ import java.util.Properties; ...@@ -19,60 +21,65 @@ import java.util.Properties;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigUtilTest { public class ConfigUtilTest {
private ConfigUtil configUtil; private ConfigUtil configUtil;
@Mock @Mock
private ConfigurableApplicationContext applicationContext; private ConfigurableApplicationContext applicationContext;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
configUtil = spy(ConfigUtil.getInstance()); configUtil = spy(ConfigUtil.getInstance());
configUtil.setApplicationContext(applicationContext); configUtil.setApplicationContext(applicationContext);
} }
@Test @Test
public void testLoadApolloRegistriesSuccessfully() throws Exception { public void testLoadApolloRegistriesSuccessfully() throws Exception {
Properties someProperties = mock(Properties.class); Properties someProperties = mock(Properties.class);
preparePropertiesFromLocalResource(someProperties); preparePropertiesFromLocalResource(someProperties);
String someAppId = "1"; String someAppId = "1";
String someVersionId = "someVersion"; String someVersionId = "someVersion";
when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true); when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true);
when(someProperties.getProperty(Constants.APP_ID)).thenReturn(someAppId); when(someProperties.getProperty(Constants.APP_ID)).thenReturn(someAppId);
when(someProperties.getProperty(eq(Constants.VERSION), anyString())).thenReturn(someVersionId); when(someProperties.getProperty(eq(Constants.VERSION), anyString())).thenReturn(someVersionId);
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries(); List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
ApolloRegistry apolloRegistry = apolloRegistries.get(0); ApolloRegistry apolloRegistry = apolloRegistries.get(0);
assertEquals(1, apolloRegistries.size()); assertEquals(1, apolloRegistries.size());
assertEquals(Long.parseLong(someAppId), apolloRegistry.getAppId()); assertEquals(Long.parseLong(someAppId), apolloRegistry.getAppId());
assertEquals(someVersionId, apolloRegistry.getVersion()); assertEquals(someVersionId, apolloRegistry.getVersion());
} }
@Test @Test
public void testLoadApolloRegistriesError() throws Exception { public void testLoadApolloRegistriesError() throws Exception {
preparePropertiesFromLocalResource(null); preparePropertiesFromLocalResource(null);
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries(); List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
assertTrue(apolloRegistries.isEmpty()); assertTrue(apolloRegistries.isEmpty());
} }
private void preparePropertiesFromLocalResource(Properties someProperties) throws IOException { private void preparePropertiesFromLocalResource(Properties someProperties) throws IOException {
ClassLoader someClassLoader = mock(ClassLoader.class); ClassLoader someClassLoader = mock(ClassLoader.class);
Thread.currentThread().setContextClassLoader(someClassLoader); Thread.currentThread().setContextClassLoader(someClassLoader);
URL someUrl = new URL("http", "somepath/", "someFile"); URL someUrl = new URL("http", "somepath/", "someFile");
Enumeration<URL> someResourceUrls = Collections.enumeration(Lists.newArrayList(someUrl)); Enumeration<URL> someResourceUrls = Collections.enumeration(Lists.newArrayList(someUrl));
when(someClassLoader.getResources(anyString())).thenReturn(someResourceUrls); when(someClassLoader.getResources(anyString())).thenReturn(someResourceUrls);
doReturn(someProperties).when(configUtil).loadPropertiesFromResourceURL(someUrl); doReturn(someProperties).when(configUtil).loadPropertiesFromResourceURL(someUrl);
} }
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<PatternLayout pattern="[apollo-client][%t]%d %-5p [%c] %m%n"/> <PatternLayout pattern="[apollo-client][%t]%d %-5p [%c] %m%n"/>
</Console> </Console>
<Async name="Async" includeLocation="true"> <Async name="Async" includeLocation="true">
<AppenderRef ref="Console" /> <AppenderRef ref="Console"/>
</Async> </Async>
</appenders> </appenders>
<loggers> <loggers>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId> <artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apollo-configservice</artifactId> <artifactId>apollo-configservice</artifactId>
<name>Apollo ConfigService</name> <name>Apollo ConfigService</name>
<dependencies> <dependencies>
<!-- apollo --> <!-- apollo -->
<dependency> <dependency>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId> <artifactId>apollo-biz</artifactId>
</dependency> </dependency>
<!-- end of apollo --> <!-- end of apollo -->
<!-- redis --> <!-- redis -->
<!-- <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <!-- <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId>
</dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId>
</dependency> --> </dependency> -->
<!-- end of redis --> <!-- end of redis -->
<!-- eureka --> <!-- eureka -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId> <artifactId>spring-cloud-starter-eureka-server</artifactId>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId> <artifactId>
spring-cloud-starter-archaius spring-cloud-starter-archaius
</artifactId> </artifactId>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>spring-cloud-starter-ribbon</artifactId> <artifactId>spring-cloud-starter-ribbon</artifactId>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>ribbon-eureka</artifactId> <artifactId>ribbon-eureka</artifactId>
<groupId>com.netflix.ribbon</groupId> <groupId>com.netflix.ribbon</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>aws-java-sdk-core</artifactId> <artifactId>aws-java-sdk-core</artifactId>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>aws-java-sdk-ec2</artifactId> <artifactId>aws-java-sdk-ec2</artifactId>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>aws-java-sdk-autoscaling</artifactId> <artifactId>aws-java-sdk-autoscaling</artifactId>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>aws-java-sdk-sts</artifactId> <artifactId>aws-java-sdk-sts</artifactId>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<artifactId>aws-java-sdk-route53</artifactId> <artifactId>aws-java-sdk-route53</artifactId>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- end of eureka --> <!-- end of eureka -->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<executable>true</executable> <executable>true</executable>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
...@@ -6,15 +6,15 @@ import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; ...@@ -6,15 +6,15 @@ import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/** /**
* Spring boot application entry point * Spring boot application entry point
* *
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@SpringBootApplication @SpringBootApplication
@EnableEurekaServer @EnableEurekaServer
public class ServerApplication { public class ServerApplication {
public static void main(String[] args) { public static void main(String[] args) {
new SpringApplicationBuilder(ServerApplication.class).web(true).run(args); new SpringApplicationBuilder(ServerApplication.class).web(true).run(args);
} }
} }
...@@ -5,13 +5,14 @@ import org.springframework.boot.context.web.SpringBootServletInitializer; ...@@ -5,13 +5,14 @@ import org.springframework.boot.context.web.SpringBootServletInitializer;
/** /**
* Entry point for traditional web app * Entry point for traditional web app
*
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ServletInitializer extends SpringBootServletInitializer { public class ServletInitializer extends SpringBootServletInitializer {
@Override @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ServerApplication.class); return application.sources(ServerApplication.class);
} }
} }
...@@ -3,11 +3,17 @@ package com.ctrip.apollo.configservice.controller; ...@@ -3,11 +3,17 @@ package com.ctrip.apollo.configservice.controller;
import com.ctrip.apollo.biz.entity.Version; import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.service.ConfigService; import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -15,36 +21,38 @@ import java.io.IOException; ...@@ -15,36 +21,38 @@ import java.io.IOException;
@RestController @RestController
@RequestMapping("/config") @RequestMapping("/config")
public class ConfigController { public class ConfigController {
@Resource(name = "configService") @Resource(name = "configService")
private ConfigService configService; private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}", method = RequestMethod.GET) @RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable long appId, public ApolloConfig queryConfig(@PathVariable long appId,
@PathVariable String clusterName, @PathVariable String clusterName,
@PathVariable String versionName, @PathVariable String versionName,
@RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId, @RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
Version version = configService.loadVersionByAppIdAndVersionName(appId, versionName); Version version = configService.loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) { if (version == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load version with appId: %d, versionName: %s", appId, versionName)); String.format("Could not load version with appId: %d, versionName: %s", appId,
return null; versionName));
} return null;
if (version.getReleaseId() == clientSideReleaseId) {
//Client side configuration is the same with server side, return 304
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return null;
}
ApolloConfig apolloConfig =
configService.loadConfigByVersionAndClusterName(version, clusterName);
if (apolloConfig == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load config with releaseId: %d, clusterName: %s", version.getReleaseId(), clusterName));
return null;
}
return apolloConfig;
} }
if (version.getReleaseId() == clientSideReleaseId) {
//Client side configuration is the same with server side, return 304
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return null;
}
ApolloConfig apolloConfig =
configService.loadConfigByVersionAndClusterName(version, clusterName);
if (apolloConfig == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load config with releaseId: %d, clusterName: %s",
version.getReleaseId(), clusterName));
return null;
}
return apolloConfig;
}
} }
package com.ctrip.apollo.metaservice.controller; package com.ctrip.apollo.metaservice.controller;
import java.util.ArrayList; import com.ctrip.apollo.core.serivce.ApolloService;
import java.util.List; import com.ctrip.apollo.metaservice.service.DiscoveryService;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.core.serivce.ApolloService; import java.util.ArrayList;
import com.ctrip.apollo.metaservice.service.DiscoveryService; import java.util.List;
import com.netflix.appinfo.InstanceInfo;
@RestController @RestController
@RequestMapping("/services") @RequestMapping("/services")
......
package com.ctrip.apollo.metaservice.service; package com.ctrip.apollo.metaservice.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.core.ServiceNameConsts; import com.ctrip.apollo.core.ServiceNameConsts;
import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service @Service
public class DiscoveryService { public class DiscoveryService {
...@@ -27,7 +27,7 @@ public class DiscoveryService { ...@@ -27,7 +27,7 @@ public class DiscoveryService {
return application != null ? application.getInstances() : new ArrayList<>(); return application != null ? application.getInstances() : new ArrayList<>();
} }
public List<InstanceInfo> getAdminServiceInstances(){ public List<InstanceInfo> getAdminServiceInstances() {
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_ADMINSERVICE); Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_ADMINSERVICE);
return application != null ? application.getInstances() : new ArrayList<>(); return application != null ? application.getInstances() : new ArrayList<>();
} }
......
package com.ctrip.apollo.configservice; package com.ctrip.apollo.configservice;
import com.ctrip.apollo.ServerApplication; import com.ctrip.apollo.ServerApplication;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
......
package com.ctrip.apollo.configservice; package com.ctrip.apollo.configservice;
import com.ctrip.apollo.configservice.controller.ConfigControllerTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import com.ctrip.apollo.configservice.controller.ConfigControllerTest;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({
ConfigControllerTest.class ConfigControllerTest.class
}) })
public class AllTests { public class AllTests {
......
...@@ -2,8 +2,8 @@ package com.ctrip.apollo.configservice.controller; ...@@ -2,8 +2,8 @@ package com.ctrip.apollo.configservice.controller;
import com.ctrip.apollo.biz.entity.Version; import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.service.ConfigService; import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.configservice.controller.ConfigController;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -15,98 +15,128 @@ import javax.servlet.http.HttpServletResponse; ...@@ -15,98 +15,128 @@ import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigControllerTest { public class ConfigControllerTest {
private ConfigController configController; private ConfigController configController;
@Mock @Mock
private ConfigService configService; private ConfigService configService;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
configController = new ConfigController(); configController = new ConfigController();
ReflectionTestUtils.setField(configController, "configService", configService); ReflectionTestUtils.setField(configController, "configService", configService);
} }
@Test @Test
public void testQueryConfig() throws Exception { public void testQueryConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class); ApolloConfig someApolloConfig = mock(ApolloConfig.class);
long someAppId = 1; long someAppId = 1;
String someClusterName = "someClusterName"; String someClusterName = "someClusterName";
String someVersionName = "someVersion"; String someVersionName = "someVersion";
long someClientSideReleaseId = 1; long someClientSideReleaseId = 1;
long someServerSideNewReleaseId = 2; long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class); HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class); Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(someVersion); when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId); .thenReturn(someVersion);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName)).thenReturn(someApolloConfig); when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName))
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse); .thenReturn(someApolloConfig);
assertEquals(someApolloConfig, result); ApolloConfig
verify(configService, times(1)).loadVersionByAppIdAndVersionName(someAppId, someVersionName); result =
verify(configService, times(1)).loadConfigByVersionAndClusterName(someVersion, someClusterName); configController
} .queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
someResponse);
@Test
public void testQueryConfigWithVersionNotFound() throws Exception { assertEquals(someApolloConfig, result);
long someAppId = 1; verify(configService, times(1)).loadVersionByAppIdAndVersionName(someAppId, someVersionName);
String someClusterName = "someClusterName"; verify(configService, times(1)).loadConfigByVersionAndClusterName(someVersion, someClusterName);
String someVersionName = "someVersion"; }
long someClientSideReleaseId = 1;
HttpServletResponse someResponse = mock(HttpServletResponse.class); @Test
public void testQueryConfigWithVersionNotFound() throws Exception {
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(null); long someAppId = 1;
String someClusterName = "someClusterName";
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse); String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
assertNull(result); HttpServletResponse someResponse = mock(HttpServletResponse.class);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
} when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
.thenReturn(null);
@Test
public void testQueryConfigWithApolloConfigNotFound() throws Exception { ApolloConfig
long someAppId = 1; result =
String someClusterName = "someClusterName"; configController
String someVersionName = "someVersion"; .queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
long someClientSideReleaseId = 1; someResponse);
long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class); assertNull(result);
Version someVersion = mock(Version.class); verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
}
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId); @Test
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName)).thenReturn(null); public void testQueryConfigWithApolloConfigNotFound() throws Exception {
long someAppId = 1;
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse); String someClusterName = "someClusterName";
String someVersionName = "someVersion";
assertNull(result); long someClientSideReleaseId = 1;
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString()); long someServerSideNewReleaseId = 2;
} HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
@Test
public void testQueryConfigWithApolloConfigNotModified() throws Exception { when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
long someAppId = 1; .thenReturn(someVersion);
String someClusterName = "someClusterName"; when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
String someVersionName = "someVersion"; when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName))
long someClientSideReleaseId = 1; .thenReturn(null);
long someServerSideReleaseId = someClientSideReleaseId;
HttpServletResponse someResponse = mock(HttpServletResponse.class); ApolloConfig
Version someVersion = mock(Version.class); result =
configController
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(someVersion); .queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
when(someVersion.getReleaseId()).thenReturn(someServerSideReleaseId); someResponse);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse); assertNull(result);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
assertNull(result); }
verify(someResponse, times(1)).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
verify(configService, never()).loadConfigByVersionAndClusterName(any(Version.class), anyString()); @Test
} public void testQueryConfigWithApolloConfigNotModified() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
long someServerSideReleaseId = someClientSideReleaseId;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
.thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideReleaseId);
ApolloConfig
result =
configController
.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
someResponse);
assertNull(result);
verify(someResponse, times(1)).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
verify(configService, never())
.loadConfigByVersionAndClusterName(any(Version.class), anyString());
}
} }
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId> <artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apollo-core</artifactId> <artifactId>apollo-core</artifactId>
<name>Apollo Core</name> <name>Apollo Core</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<dependencies> <dependencies>
<!-- json --> <!-- json -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId> <artifactId>jackson-annotations</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<!-- end of json --> <!-- end of json -->
<!-- util --> <!-- util -->
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
</dependency> </dependency>
<!-- end of util --> <!-- end of util -->
<!-- log --> <!-- log -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<!-- end of log --> <!-- end of log -->
</dependencies> </dependencies>
</project> </project>
package com.ctrip.apollo; package com.ctrip.apollo;
public class Apollo { public class Apollo {
public final static String VERSION = "java-0.0.1-SNAPSHOT"; public static final String VERSION = "java-0.0.1-SNAPSHOT";
private static Env m_env; private static Env m_env;
public enum Env { public enum Env {
LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS
} }
public static void initialize(Env env) { public static void initialize(Env env) {
m_env = env; m_env = env;
} }
public static Env getEnv() { public static Env getEnv() {
return m_env; return m_env;
} }
} }
...@@ -2,6 +2,6 @@ package com.ctrip.apollo.core; ...@@ -2,6 +2,6 @@ package com.ctrip.apollo.core;
public interface Constants { public interface Constants {
String DEFAULT_CLUSTER_NAME = "default-cluster-name"; String DEFAULT_CLUSTER_NAME = "default-cluster-name";
} }
package com.ctrip.apollo.core; package com.ctrip.apollo.core;
import com.ctrip.apollo.Apollo.Env;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.ctrip.apollo.Apollo.Env;
public class MetaDomainConsts { public class MetaDomainConsts {
public static final String DEFAULT_PORT = "8080"; public static final String DEFAULT_PORT = "8080";
......
...@@ -7,6 +7,6 @@ public class ServiceNameConsts { ...@@ -7,6 +7,6 @@ public class ServiceNameConsts {
public static final String APOLLO_CONFIGSERVICE = "apollo-configservice"; public static final String APOLLO_CONFIGSERVICE = "apollo-configservice";
public static final String APOLLO_ADMINSERVICE = "apollo-adminservice"; public static final String APOLLO_ADMINSERVICE = "apollo-adminservice";
public static final String APOLLO_PORTAL = "apollo-portal"; public static final String APOLLO_PORTAL = "apollo-portal";
} }
package com.ctrip.apollo.core.dto; package com.ctrip.apollo.core.dto;
import com.google.common.base.MoreObjects;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Comparator;
import java.util.Map; import java.util.Map;
/** /**
...@@ -12,82 +12,82 @@ import java.util.Map; ...@@ -12,82 +12,82 @@ import java.util.Map;
*/ */
public class ApolloConfig implements Comparable<ApolloConfig> { public class ApolloConfig implements Comparable<ApolloConfig> {
private long appId; private long appId;
private String cluster; private String cluster;
private String version; private String version;
private Map<String, Object> configurations; private Map<String, Object> configurations;
private long releaseId; private long releaseId;
private int order; private int order;
@JsonCreator @JsonCreator
public ApolloConfig(@JsonProperty("appId") long appId, public ApolloConfig(@JsonProperty("appId") long appId,
@JsonProperty("cluster") String cluster, @JsonProperty("cluster") String cluster,
@JsonProperty("version") String version, @JsonProperty("version") String version,
@JsonProperty("releaseId") long releaseId) { @JsonProperty("releaseId") long releaseId) {
super(); super();
this.appId = appId; this.appId = appId;
this.cluster = cluster; this.cluster = cluster;
this.version = version; this.version = version;
this.releaseId = releaseId; this.releaseId = releaseId;
} }
public Map<String, Object> getConfigurations() { public Map<String, Object> getConfigurations() {
return configurations; return configurations;
} }
public void setConfigurations(Map<String, Object> configurations) { public void setConfigurations(Map<String, Object> configurations) {
this.configurations = configurations; this.configurations = configurations;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public String getCluster() { public String getCluster() {
return cluster; return cluster;
} }
public String getVersion() { public String getVersion() {
return version; return version;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("cluster", cluster)
.add("version", version)
.add("releaseId", releaseId)
.add("configurations", configurations)
.toString();
}
@Override
public int compareTo(ApolloConfig toCompare) {
if (toCompare == null || this.getOrder() > toCompare.getOrder()) {
return 1;
} }
if (toCompare.getOrder() > this.getOrder()) {
public int getOrder() { return -1;
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("cluster", cluster)
.add("version", version)
.add("releaseId", releaseId)
.add("configurations", configurations)
.toString();
}
@Override
public int compareTo(ApolloConfig o) {
if (o == null || this.getOrder() > o.getOrder()) {
return 1;
}
if (o.getOrder() > this.getOrder()) {
return -1;
}
return 0;
} }
return 0;
}
} }
...@@ -2,33 +2,33 @@ package com.ctrip.apollo.core.dto; ...@@ -2,33 +2,33 @@ package com.ctrip.apollo.core.dto;
public class ClusterDTO { public class ClusterDTO {
private long id; private long id;
private String name; private String name;
private long appId; private long appId;
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
} }
...@@ -4,122 +4,122 @@ import java.util.Date; ...@@ -4,122 +4,122 @@ import java.util.Date;
public class ConfigItemDTO { public class ConfigItemDTO {
private long id; private long id;
private long clusterId; private long clusterId;
private String clusterName; private String clusterName;
private long appId; private long appId;
private String key; private String key;
private String value; private String value;
private String comment; private String comment;
private String dataChangeCreatedBy; private String dataChangeCreatedBy;
private Date dataChangeCreatedTime; private Date dataChangeCreatedTime;
private String DataChangeLastModifiedBy; private String dataChangeLastModifiedBy;
private Date dataChangeLastModifiedTime; private Date dataChangeLastModifiedTime;
public ConfigItemDTO(){ public ConfigItemDTO() {
} }
public ConfigItemDTO(String key, String value){ public ConfigItemDTO(String key, String value) {
this.key = key; this.key = key;
this.value = value; this.value = value;
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public long getClusterId() { public long getClusterId() {
return clusterId; return clusterId;
} }
public void setClusterId(long clusterId) { public void setClusterId(long clusterId) {
this.clusterId = clusterId; this.clusterId = clusterId;
} }
public String getClusterName() { public String getClusterName() {
return clusterName; return clusterName;
} }
public void setClusterName(String clusterName) { public void setClusterName(String clusterName) {
this.clusterName = clusterName; this.clusterName = clusterName;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public String getValue() { public String getValue() {
return value; return value;
} }
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
} }
public String getDataChangeCreatedBy() { public String getDataChangeCreatedBy() {
return dataChangeCreatedBy; return dataChangeCreatedBy;
} }
public void setDataChangeCreatedBy(String dataChangeCreatedBy) { public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy; this.dataChangeCreatedBy = dataChangeCreatedBy;
} }
public Date getDataChangeCreatedTime() { public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime; return dataChangeCreatedTime;
} }
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) { public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime; this.dataChangeCreatedTime = dataChangeCreatedTime;
} }
public String getDataChangeLastModifiedBy() { public String getDataChangeLastModifiedBy() {
return DataChangeLastModifiedBy; return dataChangeLastModifiedBy;
} }
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) { public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
DataChangeLastModifiedBy = dataChangeLastModifiedBy; this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
} }
public Date getDataChangeLastModifiedTime() { public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime; return dataChangeLastModifiedTime;
} }
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) { public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
} }
public String getComment() { public String getComment() {
return comment; return comment;
} }
public void setComment(String comment) { public void setComment(String comment) {
this.comment = comment; this.comment = comment;
} }
} }
...@@ -2,47 +2,47 @@ package com.ctrip.apollo.core.dto; ...@@ -2,47 +2,47 @@ package com.ctrip.apollo.core.dto;
public class ReleaseSnapshotDTO { public class ReleaseSnapshotDTO {
private long id; private long id;
private long releaseId; private long releaseId;
private String clusterName; private String clusterName;
private String configurations; private String configurations;
public ReleaseSnapshotDTO() { public ReleaseSnapshotDTO() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
} }
public void setReleaseId(long releaseId) { public void setReleaseId(long releaseId) {
this.releaseId = releaseId; this.releaseId = releaseId;
} }
public String getClusterName() { public String getClusterName() {
return clusterName; return clusterName;
} }
public void setClusterName(String clusterName) { public void setClusterName(String clusterName) {
this.clusterName = clusterName; this.clusterName = clusterName;
} }
public String getConfigurations() { public String getConfigurations() {
return configurations; return configurations;
} }
public void setConfigurations(String configurations) { public void setConfigurations(String configurations) {
this.configurations = configurations; this.configurations = configurations;
} }
} }
...@@ -2,57 +2,57 @@ package com.ctrip.apollo.core.dto; ...@@ -2,57 +2,57 @@ package com.ctrip.apollo.core.dto;
public class VersionDTO { public class VersionDTO {
private long id; private long id;
private String name; private String name;
private long appId; private long appId;
private long releaseId; private long releaseId;
private Long parentVersion; private Long parentVersion;
public VersionDTO(){ public VersionDTO() {
} }
public long getId() { public long getId() {
return id; return id;
} }
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
} }
public void setReleaseId(long releaseId) { public void setReleaseId(long releaseId) {
this.releaseId = releaseId; this.releaseId = releaseId;
} }
public Long getParentVersion() { public Long getParentVersion() {
return parentVersion; return parentVersion;
} }
public void setParentVersion(Long parentVersion) { public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion; this.parentVersion = parentVersion;
} }
} }
package com.ctrip.apollo.core.utils; package com.ctrip.apollo.core.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
...@@ -7,85 +10,84 @@ import java.util.concurrent.ThreadFactory; ...@@ -7,85 +10,84 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApolloThreadFactory implements ThreadFactory { public class ApolloThreadFactory implements ThreadFactory {
private static Logger log = LoggerFactory.getLogger(ApolloThreadFactory.class); private static Logger log = LoggerFactory.getLogger(ApolloThreadFactory.class);
private final AtomicLong m_threadNumber = new AtomicLong(1); private final AtomicLong threadNumber = new AtomicLong(1);
private final String m_namePrefix; private final String namePrefix;
private final boolean m_daemon; private final boolean daemon;
private final static ThreadGroup m_threadGroup = new ThreadGroup("Apollo"); private static final ThreadGroup threadGroup = new ThreadGroup("Apollo");
public static ThreadGroup getThreadGroup() { public static ThreadGroup getThreadGroup() {
return m_threadGroup; return threadGroup;
} }
public static ThreadFactory create(String namePrefix, boolean daemon) { public static ThreadFactory create(String namePrefix, boolean daemon) {
return new ApolloThreadFactory(namePrefix, daemon); return new ApolloThreadFactory(namePrefix, daemon);
} }
public static boolean waitAllShutdown(int timeoutInMillis) { public static boolean waitAllShutdown(int timeoutInMillis) {
ThreadGroup group = getThreadGroup(); ThreadGroup group = getThreadGroup();
Thread[] activeThreads = new Thread[group.activeCount()]; Thread[] activeThreads = new Thread[group.activeCount()];
group.enumerate(activeThreads); group.enumerate(activeThreads);
Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads)); Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
Set<Thread> dies = new HashSet<Thread>(); Set<Thread> dies = new HashSet<Thread>();
log.info("Current ACTIVE thread count is: {}", alives.size()); log.info("Current ACTIVE thread count is: {}", alives.size());
long expire = System.currentTimeMillis() + timeoutInMillis; long expire = System.currentTimeMillis() + timeoutInMillis;
while (System.currentTimeMillis() < expire) { while (System.currentTimeMillis() < expire) {
classify(alives, dies, new ClassifyStandard<Thread>() { classify(alives, dies, new ClassifyStandard<Thread>() {
@Override @Override
public boolean satisfy(Thread t) { public boolean satisfy(Thread thread) {
return !t.isAlive() || t.isInterrupted() || t.isDaemon(); return !thread.isAlive() || thread.isInterrupted() || thread.isDaemon();
} }
}); });
if (alives.size() > 0) { if (alives.size() > 0) {
log.info("Alive apollo threads: {}", alives); log.info("Alive apollo threads: {}", alives);
try { try {
TimeUnit.SECONDS.sleep(2); TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) { } catch (InterruptedException ex) {
// ignore // ignore
}
} else {
log.info("All apollo threads are shutdown.");
return true;
}
} }
log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}", alives); } else {
return false; log.info("All apollo threads are shutdown.");
return true;
}
} }
log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}",
alives);
return false;
}
private static interface ClassifyStandard<T> { private static interface ClassifyStandard<T> {
boolean satisfy(T t); boolean satisfy(T thread);
} }
private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) { private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
Set<T> s = new HashSet<>(); Set<T> set = new HashSet<>();
for (T t : src) { for (T t : src) {
if (standard.satisfy(t)) { if (standard.satisfy(t)) {
s.add(t); set.add(t);
} }
}
src.removeAll(s);
des.addAll(s);
} }
src.removeAll(set);
des.addAll(set);
}
private ApolloThreadFactory(String namePrefix, boolean daemon) { private ApolloThreadFactory(String namePrefix, boolean daemon) {
m_namePrefix = namePrefix; this.namePrefix = namePrefix;
m_daemon = daemon; this.daemon = daemon;
} }
public Thread newThread(Runnable r) { public Thread newThread(Runnable runnable) {
Thread t = new Thread(m_threadGroup, r,// Thread thread = new Thread(threadGroup, runnable,//
m_threadGroup.getName() + "-" + m_namePrefix + "-" + m_threadNumber.getAndIncrement()); threadGroup.getName() + "-" + namePrefix + "-" + threadNumber.getAndIncrement());
t.setDaemon(m_daemon); thread.setDaemon(daemon);
if (t.getPriority() != Thread.NORM_PRIORITY) if (thread.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY); thread.setPriority(Thread.NORM_PRIORITY);
return t;
} }
return thread;
}
} }
...@@ -7,17 +7,17 @@ import java.util.List; ...@@ -7,17 +7,17 @@ import java.util.List;
public class DNSUtil { public class DNSUtil {
public static List<String> resolve(String domainName) throws UnknownHostException { public static List<String> resolve(String domainName) throws UnknownHostException {
List<String> result = new ArrayList<String>(); List<String> result = new ArrayList<String>();
InetAddress[] addresses = InetAddress.getAllByName(domainName); InetAddress[] addresses = InetAddress.getAllByName(domainName);
if (addresses != null) { if (addresses != null) {
for (InetAddress addr : addresses) { for (InetAddress addr : addresses) {
result.add(addr.getHostAddress()); result.add(addr.getHostAddress());
} }
} }
return result; return result;
} }
} }
...@@ -5,369 +5,353 @@ import java.util.Iterator; ...@@ -5,369 +5,353 @@ import java.util.Iterator;
public class StringUtils { public class StringUtils {
public static final String EMPTY = ""; public static final String EMPTY = "";
/** /**
* <p> * <p>
* Checks if a String is empty ("") or null. * Checks if a String is empty ("") or null.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.isEmpty(null) = true * StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true * StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false * StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false * StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false * StringUtils.isEmpty(" bob ") = false
* </pre> * </pre>
* *
* <p> * <p>
* NOTE: This method changed in Lang version 2.0. It no longer trims the String. That functionality is available in isBlank(). * NOTE: This method changed in Lang version 2.0. It no longer trims the String. That functionality is available in isBlank().
* </p> * </p>
* *
* @param str * @param str the String to check, may be null
* the String to check, may be null * @return <code>true</code> if the String is empty or null
* @return <code>true</code> if the String is empty or null */
*/ public static boolean isEmpty(String str) {
public static boolean isEmpty(String str) { return str == null || str.length() == 0;
return str == null || str.length() == 0; }
}
/** /**
* <p> * <p>
* Checks if a String is whitespace, empty ("") or null. * Checks if a String is whitespace, empty ("") or null.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.isBlank(null) = true * StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true * StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true * StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false * StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false * StringUtils.isBlank(" bob ") = false
* </pre> * </pre>
* *
* @param str * @param str the String to check, may be null
* the String to check, may be null * @return <code>true</code> if the String is null, empty or whitespace
* @return <code>true</code> if the String is null, empty or whitespace */
*/ public static boolean isBlank(String str) {
public static boolean isBlank(String str) { int strLen;
int strLen; if (str == null || (strLen = str.length()) == 0) {
if (str == null || (strLen = str.length()) == 0) { return true;
return true; }
} for (int i = 0; i < strLen; i++) {
for (int i = 0; i < strLen; i++) { if ((Character.isWhitespace(str.charAt(i)) == false)) {
if ((Character.isWhitespace(str.charAt(i)) == false)) { return false;
return false; }
} }
} return true;
return true; }
}
/** /**
* <p> * <p>
* Removes control characters (char &lt;= 32) from both ends of this String returning <code>null</code> if the String is empty * Removes control characters (char &lt;= 32) from both ends of this String returning <code>null</code> if the String is empty
* ("") after the trim or if it is <code>null</code>. * ("") after the trim or if it is <code>null</code>.
* *
* <p> * <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use * The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToNull(String)}. * {@link #stripToNull(String)}.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.trimToNull(null) = null * StringUtils.trimToNull(null) = null
* StringUtils.trimToNull("") = null * StringUtils.trimToNull("") = null
* StringUtils.trimToNull(" ") = null * StringUtils.trimToNull(" ") = null
* StringUtils.trimToNull("abc") = "abc" * StringUtils.trimToNull("abc") = "abc"
* StringUtils.trimToNull(" abc ") = "abc" * StringUtils.trimToNull(" abc ") = "abc"
* </pre> * </pre>
* *
* @param str * @param str the String to be trimmed, may be null
* the String to be trimmed, may be null * @return the trimmed String, <code>null</code> if only chars &lt;= 32, empty or null String input
* @return the trimmed String, <code>null</code> if only chars &lt;= 32, empty or null String input * @since 2.0
* @since 2.0 */
*/ public static String trimToNull(String str) {
public static String trimToNull(String str) { String ts = trim(str);
String ts = trim(str); return isEmpty(ts) ? null : ts;
return isEmpty(ts) ? null : ts; }
}
/** /**
* <p> * <p>
* Removes control characters (char &lt;= 32) from both ends of this String returning an empty String ("") if the String is empty * Removes control characters (char &lt;= 32) from both ends of this String returning an empty String ("") if the String is empty
* ("") after the trim or if it is <code>null</code>. * ("") after the trim or if it is <code>null</code>.
* *
* <p> * <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use * The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToEmpty(String)}. * {@link #stripToEmpty(String)}.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.trimToEmpty(null) = "" * StringUtils.trimToEmpty(null) = ""
* StringUtils.trimToEmpty("") = "" * StringUtils.trimToEmpty("") = ""
* StringUtils.trimToEmpty(" ") = "" * StringUtils.trimToEmpty(" ") = ""
* StringUtils.trimToEmpty("abc") = "abc" * StringUtils.trimToEmpty("abc") = "abc"
* StringUtils.trimToEmpty(" abc ") = "abc" * StringUtils.trimToEmpty(" abc ") = "abc"
* </pre> * </pre>
* *
* @param str * @param str the String to be trimmed, may be null
* the String to be trimmed, may be null * @return the trimmed String, or an empty String if <code>null</code> input
* @return the trimmed String, or an empty String if <code>null</code> input * @since 2.0
* @since 2.0 */
*/ public static String trimToEmpty(String str) {
public static String trimToEmpty(String str) { return str == null ? EMPTY : str.trim();
return str == null ? EMPTY : str.trim(); }
}
/** /**
* <p> * <p>
* Removes control characters (char &lt;= 32) from both ends of this String, handling <code>null</code> by returning * Removes control characters (char &lt;= 32) from both ends of this String, handling <code>null</code> by returning
* <code>null</code>. * <code>null</code>.
* </p> * </p>
* *
* <p> * <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use * The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #strip(String)}. * {@link #strip(String)}.
* </p> * </p>
* *
* <p> * <p>
* To trim your choice of characters, use the {@link #strip(String, String)} methods. * To trim your choice of characters, use the {@link #strip(String, String)} methods.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.trim(null) = null * StringUtils.trim(null) = null
* StringUtils.trim("") = "" * StringUtils.trim("") = ""
* StringUtils.trim(" ") = "" * StringUtils.trim(" ") = ""
* StringUtils.trim("abc") = "abc" * StringUtils.trim("abc") = "abc"
* StringUtils.trim(" abc ") = "abc" * StringUtils.trim(" abc ") = "abc"
* </pre> * </pre>
* *
* @param str * @param str the String to be trimmed, may be null
* the String to be trimmed, may be null * @return the trimmed string, <code>null</code> if null String input
* @return the trimmed string, <code>null</code> if null String input */
*/ public static String trim(String str) {
public static String trim(String str) { return str == null ? null : str.trim();
return str == null ? null : str.trim(); }
}
/** /**
* <p> * <p>
* Compares two Strings, returning <code>true</code> if they are equal. * Compares two Strings, returning <code>true</code> if they are equal.
* </p> * </p>
* *
* <p> * <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison * <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive. * is case sensitive.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.equals(null, null) = true * StringUtils.equals(null, null) = true
* StringUtils.equals(null, "abc") = false * StringUtils.equals(null, "abc") = false
* StringUtils.equals("abc", null) = false * StringUtils.equals("abc", null) = false
* StringUtils.equals("abc", "abc") = true * StringUtils.equals("abc", "abc") = true
* StringUtils.equals("abc", "ABC") = false * StringUtils.equals("abc", "ABC") = false
* </pre> * </pre>
* *
* @see java.lang.String#equals(Object) * @param str1 the first String, may be null
* @param str1 * @param str2 the second String, may be null
* the first String, may be null * @return <code>true</code> if the Strings are equal, case sensitive, or both <code>null</code>
* @param str2 * @see java.lang.String#equals(Object)
* the second String, may be null */
* @return <code>true</code> if the Strings are equal, case sensitive, or both <code>null</code> public static boolean equals(String str1, String str2) {
*/ return str1 == null ? str2 == null : str1.equals(str2);
public static boolean equals(String str1, String str2) { }
return str1 == null ? str2 == null : str1.equals(str2);
}
/** /**
* <p> * <p>
* Compares two Strings, returning <code>true</code> if they are equal ignoring the case. * Compares two Strings, returning <code>true</code> if they are equal ignoring the case.
* </p> * </p>
* *
* <p> * <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered equal. Comparison is case * <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered equal. Comparison is case
* insensitive. * insensitive.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.equalsIgnoreCase(null, null) = true * StringUtils.equalsIgnoreCase(null, null) = true
* StringUtils.equalsIgnoreCase(null, "abc") = false * StringUtils.equalsIgnoreCase(null, "abc") = false
* StringUtils.equalsIgnoreCase("abc", null) = false * StringUtils.equalsIgnoreCase("abc", null) = false
* StringUtils.equalsIgnoreCase("abc", "abc") = true * StringUtils.equalsIgnoreCase("abc", "abc") = true
* StringUtils.equalsIgnoreCase("abc", "ABC") = true * StringUtils.equalsIgnoreCase("abc", "ABC") = true
* </pre> * </pre>
* *
* @see java.lang.String#equalsIgnoreCase(String) * @param str1 the first String, may be null
* @param str1 * @param str2 the second String, may be null
* the first String, may be null * @return <code>true</code> if the Strings are equal, case insensitive, or both <code>null</code>
* @param str2 * @see java.lang.String#equalsIgnoreCase(String)
* the second String, may be null */
* @return <code>true</code> if the Strings are equal, case insensitive, or both <code>null</code> public static boolean equalsIgnoreCase(String str1, String str2) {
*/ return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
public static boolean equalsIgnoreCase(String str1, String str2) { }
return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
}
/** /**
* <p> * <p>
* Check if a String starts with a specified prefix. * Check if a String starts with a specified prefix.
* </p> * </p>
* *
* <p> * <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison * <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive. * is case sensitive.
* </p> * </p>
* *
* <pre> * <pre>
* StringUtils.startsWith(null, null) = true * StringUtils.startsWith(null, null) = true
* StringUtils.startsWith(null, "abc") = false * StringUtils.startsWith(null, "abc") = false
* StringUtils.startsWith("abcdef", null) = false * StringUtils.startsWith("abcdef", null) = false
* StringUtils.startsWith("abcdef", "abc") = true * StringUtils.startsWith("abcdef", "abc") = true
* StringUtils.startsWith("ABCDEF", "abc") = false * StringUtils.startsWith("ABCDEF", "abc") = false
* </pre> * </pre>
* *
* @see java.lang.String#startsWith(String) * @param str the String to check, may be null
* @param str * @param prefix the prefix to find, may be null
* the String to check, may be null * @return <code>true</code> if the String starts with the prefix, case sensitive, or both <code>null</code>
* @param prefix * @see java.lang.String#startsWith(String)
* the prefix to find, may be null * @since 2.4
* @return <code>true</code> if the String starts with the prefix, case sensitive, or both <code>null</code> */
* @since 2.4 public static boolean startsWith(String str, String prefix) {
*/ return startsWith(str, prefix, false);
public static boolean startsWith(String str, String prefix) { }
return startsWith(str, prefix, false);
}
/** /**
* <p> * <p>
* Case insensitive check if a String starts with a specified prefix. * Check if a String starts with a specified prefix (optionally case insensitive).
* </p> * </p>
* *
* <p> * @param str the String to check, may be null
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison * @param prefix the prefix to find, may be null
* is case insensitive. * @param ignoreCase inidicates whether the compare should ignore case (case insensitive) or not.
* </p> * @return <code>true</code> if the String starts with the prefix or both <code>null</code>
* * @see java.lang.String#startsWith(String)
* <pre> */
* StringUtils.startsWithIgnoreCase(null, null) = true private static boolean startsWith(String str, String prefix, boolean ignoreCase) {
* StringUtils.startsWithIgnoreCase(null, "abc") = false if (str == null || prefix == null) {
* StringUtils.startsWithIgnoreCase("abcdef", null) = false return (str == null && prefix == null);
* StringUtils.startsWithIgnoreCase("abcdef", "abc") = true }
* StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true if (prefix.length() > str.length()) {
* </pre> return false;
* }
* @see java.lang.String#startsWith(String) return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
* @param str }
* the String to check, may be null
* @param prefix
* the prefix to find, may be null
* @return <code>true</code> if the String starts with the prefix, case insensitive, or both <code>null</code>
* @since 2.4
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
return startsWith(str, prefix, true);
}
/** /**
* <p> * <p>
* Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false. * Case insensitive check if a String starts with a specified prefix.
* </p> * </p>
* *
* <p> * <p>
* <code>null</code> will return <code>false</code>. An empty String (length()=0) will return <code>true</code>. * <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* </p> * is case insensitive.
* * </p>
* <pre> *
* StringUtils.isNumeric(null) = false * <pre>
* StringUtils.isNumeric("") = true * StringUtils.startsWithIgnoreCase(null, null) = true
* StringUtils.isNumeric(" ") = false * StringUtils.startsWithIgnoreCase(null, "abc") = false
* StringUtils.isNumeric("123") = true * StringUtils.startsWithIgnoreCase("abcdef", null) = false
* StringUtils.isNumeric("12 3") = false * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
* StringUtils.isNumeric("ab2c") = false * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
* StringUtils.isNumeric("12-3") = false * </pre>
* StringUtils.isNumeric("12.3") = false *
* </pre> * @param str the String to check, may be null
* * @param prefix the prefix to find, may be null
* @param str * @return <code>true</code> if the String starts with the prefix, case insensitive, or both <code>null</code>
* the String to check, may be null * @see java.lang.String#startsWith(String)
* @return <code>true</code> if only contains digits, and is non-null * @since 2.4
*/ */
public static boolean isNumeric(String str) { public static boolean startsWithIgnoreCase(String str, String prefix) {
if (str == null) { return startsWith(str, prefix, true);
return false; }
}
int sz = str.length();
for (int i = 0; i < sz; i++) {
if (Character.isDigit(str.charAt(i)) == false) {
return false;
}
}
return true;
}
/** /**
* <p> * <p>
* Check if a String starts with a specified prefix (optionally case insensitive). * Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false.
* </p> * </p>
* *
* @see java.lang.String#startsWith(String) * <p>
* @param str * <code>null</code> will return <code>false</code>. An empty String (length()=0) will return <code>true</code>.
* the String to check, may be null * </p>
* @param prefix *
* the prefix to find, may be null * <pre>
* @param ignoreCase * StringUtils.isNumeric(null) = false
* inidicates whether the compare should ignore case (case insensitive) or not. * StringUtils.isNumeric("") = true
* @return <code>true</code> if the String starts with the prefix or both <code>null</code> * StringUtils.isNumeric(" ") = false
*/ * StringUtils.isNumeric("123") = true
private static boolean startsWith(String str, String prefix, boolean ignoreCase) { * StringUtils.isNumeric("12 3") = false
if (str == null || prefix == null) { * StringUtils.isNumeric("ab2c") = false
return (str == null && prefix == null); * StringUtils.isNumeric("12-3") = false
} * StringUtils.isNumeric("12.3") = false
if (prefix.length() > str.length()) { * </pre>
return false; *
} * @param str the String to check, may be null
return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); * @return <code>true</code> if only contains digits, and is non-null
} */
public static boolean isNumeric(String str) {
if (str == null) {
return false;
}
int sz = str.length();
for (int i = 0; i < sz; i++) {
if (Character.isDigit(str.charAt(i)) == false) {
return false;
}
}
return true;
}
public static interface StringFormatter<T> { public static interface StringFormatter<T> {
public String format(T obj); public String format(T obj);
} }
public static <T> String join(Collection<T> collection, String separator) { public static <T> String join(Collection<T> collection, String separator) {
return join(collection, separator, new StringFormatter<T>() { return join(collection, separator, new StringFormatter<T>() {
@Override @Override
public String format(T obj) { public String format(T obj) {
return obj.toString(); return obj.toString();
} }
}); });
} }
public static <T> String join(Collection<T> collection, String separator, StringFormatter<T> formatter) { public static <T> String join(Collection<T> collection, String separator,
Iterator<T> iterator = collection.iterator(); StringFormatter<T> formatter) {
// handle null, zero and one elements before building a buffer Iterator<T> iterator = collection.iterator();
if (iterator == null) { // handle null, zero and one elements before building a buffer
return null; if (iterator == null) {
} return null;
if (!iterator.hasNext()) { }
return EMPTY; if (!iterator.hasNext()) {
} return EMPTY;
T first = iterator.next(); }
if (!iterator.hasNext()) { T first = iterator.next();
return first == null ? "" : formatter.format(first); if (!iterator.hasNext()) {
} return first == null ? "" : formatter.format(first);
}
// two or more elements // two or more elements
StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
if (first != null) { if (first != null) {
buf.append(formatter.format(first)); buf.append(formatter.format(first));
} }
while (iterator.hasNext()) { while (iterator.hasNext()) {
buf.append(separator); buf.append(separator);
T obj = iterator.next(); T obj = iterator.next();
if (obj != null) { if (obj != null) {
buf.append(formatter.format(obj)); buf.append(formatter.format(obj));
} }
} }
return buf.toString(); return buf.toString();
} }
} }
package com.ctrip.apollo.demo; package com.ctrip.apollo.demo;
import com.ctrip.apollo.client.ApolloConfigManager; import com.ctrip.apollo.client.ApolloConfigManager;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -11,10 +12,10 @@ import org.springframework.context.annotation.Configuration; ...@@ -11,10 +12,10 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@ComponentScan(value = "com.ctrip.apollo.demo") @ComponentScan(value = "com.ctrip.apollo.demo")
public class AppConfig { public class AppConfig {
@Bean @Bean
public ApolloConfigManager apolloConfigManager() { public ApolloConfigManager apolloConfigManager() {
return new ApolloConfigManager(); return new ApolloConfigManager();
} }
// @Bean // @Bean
// public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { // public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
......
...@@ -15,24 +15,24 @@ import org.springframework.web.servlet.view.JstlView; ...@@ -15,24 +15,24 @@ import org.springframework.web.servlet.view.JstlView;
@Configuration @Configuration
@EnableWebMvc @EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter { public class WebConfig extends WebMvcConfigurerAdapter {
@Override @Override
public void addViewControllers(ViewControllerRegistry registry) { public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome"); registry.addViewController("/").setViewName("welcome");
registry.addViewController("/index").setViewName("welcome"); registry.addViewController("/index").setViewName("welcome");
} }
@Bean @Bean
public InternalResourceViewResolver viewResolver() { public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver(); = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class); viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp"); viewResolver.setSuffix(".jsp");
return viewResolver; return viewResolver;
} }
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/"); registry.addResourceHandler("/**").addResourceLocations("/");
} }
} }
...@@ -6,8 +6,8 @@ import com.ctrip.apollo.client.model.PropertyChange; ...@@ -6,8 +6,8 @@ import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.util.ConfigUtil; import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.demo.model.Config; import com.ctrip.apollo.demo.model.Config;
import com.ctrip.apollo.demo.service.DemoService; import com.ctrip.apollo.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
...@@ -25,35 +25,35 @@ import java.util.List; ...@@ -25,35 +25,35 @@ import java.util.List;
@RequestMapping("/demo") @RequestMapping("/demo")
@PropertySource("classpath:application.properties") @PropertySource("classpath:application.properties")
public class DemoController { public class DemoController {
@Autowired @Autowired
private Environment env; private Environment env;
@Autowired @Autowired
private DemoService demoService; private DemoService demoService;
//Apollo config client internal impl, not intended to be used by application, only for this test page! //Apollo config client internal impl, not intended to be used by application, only for this test page!
private ConfigUtil configUtil = ConfigUtil.getInstance(); private ConfigUtil configUtil = ConfigUtil.getInstance();
//ApolloConfigManager, not intended to be used by application, only for this test page! //ApolloConfigManager, not intended to be used by application, only for this test page!
@Autowired @Autowired
private ApolloConfigManager apolloConfigManager; private ApolloConfigManager apolloConfigManager;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET) @RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public Config queryConfig(@PathVariable String configName) { public Config queryConfig(@PathVariable String configName) {
return new Config(configName, env.getProperty(configName, "undefined")); return new Config(configName, env.getProperty(configName, "undefined"));
} }
@RequestMapping(value = "/injected/config", method = RequestMethod.GET) @RequestMapping(value = "/injected/config", method = RequestMethod.GET)
public Config queryInjectedConfig() { public Config queryInjectedConfig() {
return new Config("apollo.foo", demoService.getFoo()); return new Config("apollo.foo", demoService.getFoo());
} }
@RequestMapping(value = "/client/registries", method = RequestMethod.GET) @RequestMapping(value = "/client/registries", method = RequestMethod.GET)
public List<ApolloRegistry> loadApolloRegistries() throws IOException { public List<ApolloRegistry> loadApolloRegistries() throws IOException {
return configUtil.loadApolloRegistries(); return configUtil.loadApolloRegistries();
} }
@RequestMapping(value = "/refresh", method = RequestMethod.POST) @RequestMapping(value = "/refresh", method = RequestMethod.POST)
public List<PropertyChange> refreshBeans() { public List<PropertyChange> refreshBeans() {
List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource(); List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource();
return changes; return changes;
} }
} }
...@@ -2,6 +2,7 @@ package com.ctrip.apollo.demo.exception; ...@@ -2,6 +2,7 @@ package com.ctrip.apollo.demo.exception;
import com.ctrip.apollo.demo.model.ErrorResult; import com.ctrip.apollo.demo.model.ErrorResult;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
...@@ -15,11 +16,12 @@ import org.springframework.web.context.request.WebRequest; ...@@ -15,11 +16,12 @@ import org.springframework.web.context.request.WebRequest;
@ControllerAdvice @ControllerAdvice
public class RestExceptionHandler { public class RestExceptionHandler {
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
ResponseEntity<ErrorResult> handleWebExceptions(Exception ex, ResponseEntity<ErrorResult> handleWebExceptions(Exception ex,
WebRequest request) throws JsonProcessingException { WebRequest request)
ErrorResult error = new ErrorResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage()); throws JsonProcessingException {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(error); ErrorResult error = new ErrorResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(error);
} }
} }
...@@ -4,19 +4,19 @@ package com.ctrip.apollo.demo.model; ...@@ -4,19 +4,19 @@ package com.ctrip.apollo.demo.model;
* Created by Jason on 2/25/16. * Created by Jason on 2/25/16.
*/ */
public class Config { public class Config {
private final String name; private final String name;
private final String value; private final String value;
public Config(String name, String value) { public Config(String name, String value) {
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
public String getName() { public String getName() {
return name; return name;
} }
public String getValue() { public String getValue() {
return value; return value;
} }
} }
...@@ -4,19 +4,19 @@ package com.ctrip.apollo.demo.model; ...@@ -4,19 +4,19 @@ package com.ctrip.apollo.demo.model;
* Created by Jason on 7/6/15. * Created by Jason on 7/6/15.
*/ */
public class ErrorResult { public class ErrorResult {
private final int code; private final int code;
private final String msg; private final String msg;
public ErrorResult(int code, String msg) { public ErrorResult(int code, String msg) {
this.code = code; this.code = code;
this.msg = msg; this.msg = msg;
} }
public int getCode() { public int getCode() {
return code; return code;
} }
public String getMsg() { public String getMsg() {
return msg; return msg;
} }
} }
...@@ -10,14 +10,14 @@ import org.springframework.stereotype.Service; ...@@ -10,14 +10,14 @@ import org.springframework.stereotype.Service;
@Service @Service
@RefreshScope @RefreshScope
public class DemoService { public class DemoService {
private String foo; private String foo;
@Value("${apollo.foo}") @Value("${apollo.foo}")
private void setFoo(String foo) { private void setFoo(String foo) {
this.foo = foo; this.foo = foo;
} }
public String getFoo() { public String getFoo() {
return foo; return foo;
} }
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<PatternLayout pattern="[apollo-demo][%t]%d %-5p [%c] %m%n"/> <PatternLayout pattern="[apollo-demo][%t]%d %-5p [%c] %m%n"/>
</Console> </Console>
<Async name="Async" includeLocation="true"> <Async name="Async" includeLocation="true">
<AppenderRef ref="Console" /> <AppenderRef ref="Console"/>
</Async> </Async>
</appenders> </appenders>
<loggers> <loggers>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<title>Apollo Config Client</title> <title>Apollo Config Client</title>
<link rel="stylesheet" type="text/css" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="/styles/angular-toastr-1.4.1.min.css"/> <link rel="stylesheet" type="text/css" href="/styles/angular-toastr-1.4.1.min.css"/>
<link rel='stylesheet' href='/styles/loading-bar.min.css' type='text/css' media='all' /> <link rel='stylesheet' href='/styles/loading-bar.min.css' type='text/css' media='all'/>
<link rel="stylesheet" type="text/css" href="/styles/app.css"/> <link rel="stylesheet" type="text/css" href="/styles/app.css"/>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script> <script type="text/javascript" src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script>
......
!function(){"use strict";function t(t,e,s,n,o,r,a){function i(t){if(t)d(t.toastId);else for(var e=0;e<O.length;e++)d(O[e].toastId)}function l(t,e,s){var n=m().iconClasses.error;return g(n,t,e,s)}function c(t,e,s){var n=m().iconClasses.info;return g(n,t,e,s)}function u(t,e,s){var n=m().iconClasses.success;return g(n,t,e,s)}function p(t,e,s){var n=m().iconClasses.warning;return g(n,t,e,s)}function d(e,s){function n(t){for(var e=0;e<O.length;e++)if(O[e].toastId===t)return O[e]}function o(){return!O.length}var i=n(e);i&&!i.deleting&&(i.deleting=!0,i.isOpened=!1,t.leave(i.el).then(function(){i.scope.options.onHidden&&i.scope.options.onHidden(s),i.scope.$destroy();var t=O.indexOf(i);delete B[i.scope.message],O.splice(t,1);var e=r.maxOpened;e&&O.length>=e&&O[e-1].open.resolve(),o()&&(h.remove(),h=null,T=a.defer())}))}function g(t,e,s,n){return angular.isObject(s)&&(n=s,s=null),v({iconClass:t,message:e,optionsOverride:n,title:s})}function m(){return angular.extend({},r)}function f(e){if(h)return T.promise;h=angular.element("<div></div>"),h.attr("id",e.containerId),h.addClass(e.positionClass),h.css({"pointer-events":"auto"});var s=angular.element(document.querySelector(e.target));if(!s||!s.length)throw"Target for toasts doesn't exist";return t.enter(h,s).then(function(){T.resolve()}),T.promise}function v(s){function r(t,e,s){s.allowHtml?(t.scope.allowHtml=!0,t.scope.title=o.trustAsHtml(e.title),t.scope.message=o.trustAsHtml(e.message)):(t.scope.title=e.title,t.scope.message=e.message),t.scope.toastType=t.iconClass,t.scope.toastId=t.toastId,t.scope.options={extendedTimeOut:s.extendedTimeOut,messageClass:s.messageClass,onHidden:s.onHidden,onShown:s.onShown,progressBar:s.progressBar,tapToDismiss:s.tapToDismiss,timeOut:s.timeOut,titleClass:s.titleClass,toastClass:s.toastClass},s.closeButton&&(t.scope.options.closeHtml=s.closeHtml)}function i(){function t(t){for(var e=["containerId","iconClasses","maxOpened","newestOnTop","positionClass","preventDuplicates","preventOpenDuplicates","templates"],s=0,n=e.length;n>s;s++)delete t[e[s]];return t}var e={toastId:C++,isOpened:!1,scope:n.$new(),open:a.defer()};return e.iconClass=s.iconClass,s.optionsOverride&&(p=angular.extend(p,t(s.optionsOverride)),e.iconClass=s.optionsOverride.iconClass||e.iconClass),r(e,s,p),e.el=l(e.scope),e}function l(t){var s=angular.element("<div toast></div>"),n=e.get("$compile");return n(s)(t)}function c(){return p.maxOpened&&O.length<=p.maxOpened||!p.maxOpened}function u(){var t=p.preventDuplicates&&s.message===w,e=p.preventOpenDuplicates&&B[s.message];return t||e?!0:(w=s.message,B[s.message]=!0,!1)}var p=m();if(!u()){var g=i();if(O.push(g),p.autoDismiss&&p.maxOpened>0)for(var v=O.slice(0,O.length-p.maxOpened),T=0,$=v.length;$>T;T++)d(v[T].toastId);return c()&&g.open.resolve(),g.open.promise.then(function(){f(p).then(function(){if(g.isOpened=!0,p.newestOnTop)t.enter(g.el,h).then(function(){g.scope.init()});else{var e=h[0].lastChild?angular.element(h[0].lastChild):null;t.enter(g.el,h,e).then(function(){g.scope.init()})}})}),g}}var h,C=0,O=[],w="",B={},T=a.defer(),$={clear:i,error:l,info:c,remove:d,success:u,warning:p};return $}angular.module("toastr",[]).factory("toastr",t),t.$inject=["$animate","$injector","$document","$rootScope","$sce","toastrConfig","$q"]}(),function(){"use strict";angular.module("toastr").constant("toastrConfig",{allowHtml:!1,autoDismiss:!1,closeButton:!1,closeHtml:"<button>&times;</button>",containerId:"toast-container",extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},maxOpened:0,messageClass:"toast-message",newestOnTop:!0,onHidden:null,onShown:null,positionClass:"toast-top-right",preventDuplicates:!1,preventOpenDuplicates:!1,progressBar:!1,tapToDismiss:!0,target:"body",templates:{toast:"directives/toast/toast.html",progressbar:"directives/progressbar/progressbar.html"},timeOut:5e3,titleClass:"toast-title",toastClass:"toast"})}(),function(){"use strict";function t(t){function e(t,e,s,n){function o(){var t=(i-(new Date).getTime())/a*100;e.css("width",t+"%")}var r,a,i;n.progressBar=t,t.start=function(t){r&&clearInterval(r),a=parseFloat(t),i=(new Date).getTime()+a,r=setInterval(o,10)},t.stop=function(){r&&clearInterval(r)},t.$on("$destroy",function(){clearInterval(r)})}return{replace:!0,require:"^toast",templateUrl:function(){return t.templates.progressbar},link:e}}angular.module("toastr").directive("progressBar",t),t.$inject=["toastrConfig"]}(),function(){"use strict";function t(){this.progressBar=null,this.startProgressBar=function(t){this.progressBar&&this.progressBar.start(t)},this.stopProgressBar=function(){this.progressBar&&this.progressBar.stop()}}angular.module("toastr").controller("ToastController",t)}(),function(){"use strict";function t(t,e,s,n){function o(s,o,r,a){function i(t){return a.startProgressBar(t),e(function(){a.stopProgressBar(),n.remove(s.toastId)},t,1)}function l(){s.progressBar=!1,a.stopProgressBar()}function c(){return s.options.closeHtml}var u;if(s.toastClass=s.options.toastClass,s.titleClass=s.options.titleClass,s.messageClass=s.options.messageClass,s.progressBar=s.options.progressBar,c()){var p=angular.element(s.options.closeHtml),d=t.get("$compile");p.addClass("toast-close-button"),p.attr("ng-click","close()"),d(p)(s),o.prepend(p)}s.init=function(){s.options.timeOut&&(u=i(s.options.timeOut)),s.options.onShown&&s.options.onShown()},o.on("mouseenter",function(){l(),u&&e.cancel(u)}),s.tapToast=function(){s.options.tapToDismiss&&s.close(!0)},s.close=function(t){n.remove(s.toastId,t)},o.on("mouseleave",function(){(0!==s.options.timeOut||0!==s.options.extendedTimeOut)&&(s.$apply(function(){s.progressBar=s.options.progressBar}),u=i(s.options.extendedTimeOut))})}return{replace:!0,templateUrl:function(){return s.templates.toast},controller:"ToastController",link:o}}angular.module("toastr").directive("toast",t),t.$inject=["$injector","$interval","toastrConfig","toastr"]}(),angular.module("toastr").run(["$templateCache",function(t){t.put("directives/progressbar/progressbar.html",'<div class="toast-progress"></div>\n'),t.put("directives/toast/toast.html",'<div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">\n <div ng-switch on="allowHtml">\n <div ng-switch-default ng-if="title" class="{{titleClass}}">{{title}}</div>\n <div ng-switch-default class="{{messageClass}}">{{message}}</div>\n <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>\n <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>\n </div>\n <progress-bar ng-if="progressBar"></progress-bar>\n</div>\n')}]); !function () {
\ No newline at end of file "use strict";
function t(t, e, s, n, o, r, a) {
function i(t) {
if (t)d(t.toastId); else for (var e = 0; e < O.length; e++)d(O[e].toastId)
}
function l(t, e, s) {
var n = m().iconClasses.error;
return g(n, t, e, s)
}
function c(t, e, s) {
var n = m().iconClasses.info;
return g(n, t, e, s)
}
function u(t, e, s) {
var n = m().iconClasses.success;
return g(n, t, e, s)
}
function p(t, e, s) {
var n = m().iconClasses.warning;
return g(n, t, e, s)
}
function d(e, s) {
function n(t) {
for (var e = 0; e < O.length; e++)if (O[e].toastId === t)return O[e]
}
function o() {
return !O.length
}
var i = n(e);
i && !i.deleting && (i.deleting = !0, i.isOpened = !1, t.leave(i.el).then(function () {
i.scope.options.onHidden && i.scope.options.onHidden(s), i.scope.$destroy();
var t = O.indexOf(i);
delete B[i.scope.message], O.splice(t, 1);
var e = r.maxOpened;
e && O.length >= e && O[e - 1].open.resolve(), o() && (h.remove(), h = null, T = a.defer())
}))
}
function g(t, e, s, n) {
return angular.isObject(s) && (n = s, s = null), v({iconClass: t, message: e, optionsOverride: n, title: s})
}
function m() {
return angular.extend({}, r)
}
function f(e) {
if (h)return T.promise;
h = angular.element("<div></div>"), h.attr("id", e.containerId), h.addClass(e.positionClass), h.css({"pointer-events": "auto"});
var s = angular.element(document.querySelector(e.target));
if (!s || !s.length)throw"Target for toasts doesn't exist";
return t.enter(h, s).then(function () {
T.resolve()
}), T.promise
}
function v(s) {
function r(t, e, s) {
s.allowHtml ? (t.scope.allowHtml = !0, t.scope.title = o.trustAsHtml(e.title), t.scope.message = o.trustAsHtml(e.message)) : (t.scope.title = e.title, t.scope.message = e.message), t.scope.toastType = t.iconClass, t.scope.toastId = t.toastId, t.scope.options = {
extendedTimeOut: s.extendedTimeOut,
messageClass: s.messageClass,
onHidden: s.onHidden,
onShown: s.onShown,
progressBar: s.progressBar,
tapToDismiss: s.tapToDismiss,
timeOut: s.timeOut,
titleClass: s.titleClass,
toastClass: s.toastClass
}, s.closeButton && (t.scope.options.closeHtml = s.closeHtml)
}
function i() {
function t(t) {
for (var e = ["containerId", "iconClasses", "maxOpened", "newestOnTop", "positionClass", "preventDuplicates", "preventOpenDuplicates", "templates"], s = 0, n = e.length; n > s; s++)delete t[e[s]];
return t
}
var e = {toastId: C++, isOpened: !1, scope: n.$new(), open: a.defer()};
return e.iconClass = s.iconClass, s.optionsOverride && (p = angular.extend(p, t(s.optionsOverride)), e.iconClass = s.optionsOverride.iconClass || e.iconClass), r(e, s, p), e.el = l(e.scope), e
}
function l(t) {
var s = angular.element("<div toast></div>"), n = e.get("$compile");
return n(s)(t)
}
function c() {
return p.maxOpened && O.length <= p.maxOpened || !p.maxOpened
}
function u() {
var t = p.preventDuplicates && s.message === w, e = p.preventOpenDuplicates && B[s.message];
return t || e ? !0 : (w = s.message, B[s.message] = !0, !1)
}
var p = m();
if (!u()) {
var g = i();
if (O.push(g), p.autoDismiss && p.maxOpened > 0)for (var v = O.slice(0, O.length - p.maxOpened), T = 0, $ = v.length; $ > T; T++)d(v[T].toastId);
return c() && g.open.resolve(), g.open.promise.then(function () {
f(p).then(function () {
if (g.isOpened = !0, p.newestOnTop)t.enter(g.el, h).then(function () {
g.scope.init()
}); else {
var e = h[0].lastChild ? angular.element(h[0].lastChild) : null;
t.enter(g.el, h, e).then(function () {
g.scope.init()
})
}
})
}), g
}
}
var h, C = 0, O = [], w = "", B = {}, T = a.defer(), $ = {
clear: i,
error: l,
info: c,
remove: d,
success: u,
warning: p
};
return $
}
angular.module("toastr", []).factory("toastr", t), t.$inject = ["$animate", "$injector", "$document", "$rootScope", "$sce", "toastrConfig", "$q"]
}(), function () {
"use strict";
angular.module("toastr").constant("toastrConfig", {
allowHtml: !1,
autoDismiss: !1,
closeButton: !1,
closeHtml: "<button>&times;</button>",
containerId: "toast-container",
extendedTimeOut: 1e3,
iconClasses: {error: "toast-error", info: "toast-info", success: "toast-success", warning: "toast-warning"},
maxOpened: 0,
messageClass: "toast-message",
newestOnTop: !0,
onHidden: null,
onShown: null,
positionClass: "toast-top-right",
preventDuplicates: !1,
preventOpenDuplicates: !1,
progressBar: !1,
tapToDismiss: !0,
target: "body",
templates: {toast: "directives/toast/toast.html", progressbar: "directives/progressbar/progressbar.html"},
timeOut: 5e3,
titleClass: "toast-title",
toastClass: "toast"
})
}(), function () {
"use strict";
function t(t) {
function e(t, e, s, n) {
function o() {
var t = (i - (new Date).getTime()) / a * 100;
e.css("width", t + "%")
}
var r, a, i;
n.progressBar = t, t.start = function (t) {
r && clearInterval(r), a = parseFloat(t), i = (new Date).getTime() + a, r = setInterval(o, 10)
}, t.stop = function () {
r && clearInterval(r)
}, t.$on("$destroy", function () {
clearInterval(r)
})
}
return {
replace: !0, require: "^toast", templateUrl: function () {
return t.templates.progressbar
}, link: e
}
}
angular.module("toastr").directive("progressBar", t), t.$inject = ["toastrConfig"]
}(), function () {
"use strict";
function t() {
this.progressBar = null, this.startProgressBar = function (t) {
this.progressBar && this.progressBar.start(t)
}, this.stopProgressBar = function () {
this.progressBar && this.progressBar.stop()
}
}
angular.module("toastr").controller("ToastController", t)
}(), function () {
"use strict";
function t(t, e, s, n) {
function o(s, o, r, a) {
function i(t) {
return a.startProgressBar(t), e(function () {
a.stopProgressBar(), n.remove(s.toastId)
}, t, 1)
}
function l() {
s.progressBar = !1, a.stopProgressBar()
}
function c() {
return s.options.closeHtml
}
var u;
if (s.toastClass = s.options.toastClass, s.titleClass = s.options.titleClass, s.messageClass = s.options.messageClass, s.progressBar = s.options.progressBar, c()) {
var p = angular.element(s.options.closeHtml), d = t.get("$compile");
p.addClass("toast-close-button"), p.attr("ng-click", "close()"), d(p)(s), o.prepend(p)
}
s.init = function () {
s.options.timeOut && (u = i(s.options.timeOut)), s.options.onShown && s.options.onShown()
}, o.on("mouseenter", function () {
l(), u && e.cancel(u)
}), s.tapToast = function () {
s.options.tapToDismiss && s.close(!0)
}, s.close = function (t) {
n.remove(s.toastId, t)
}, o.on("mouseleave", function () {
(0 !== s.options.timeOut || 0 !== s.options.extendedTimeOut) && (s.$apply(function () {
s.progressBar = s.options.progressBar
}), u = i(s.options.extendedTimeOut))
})
}
return {
replace: !0, templateUrl: function () {
return s.templates.toast
}, controller: "ToastController", link: o
}
}
angular.module("toastr").directive("toast", t), t.$inject = ["$injector", "$interval", "toastrConfig", "toastr"]
}(), angular.module("toastr").run(["$templateCache", function (t) {
t.put("directives/progressbar/progressbar.html", '<div class="toast-progress"></div>\n'), t.put("directives/toast/toast.html", '<div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">\n <div ng-switch on="allowHtml">\n <div ng-switch-default ng-if="title" class="{{titleClass}}">{{title}}</div>\n <div ng-switch-default class="{{messageClass}}">{{message}}</div>\n <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>\n <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>\n </div>\n <progress-bar ng-if="progressBar"></progress-bar>\n</div>\n')
}]);
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
var self = this; var self = this;
this.loadRegistries = function() { this.loadRegistries = function () {
$http.get("demo/client/registries") $http.get("demo/client/registries")
.success(function (data) { .success(function (data) {
self.registries = data; self.registries = data;
...@@ -26,43 +26,43 @@ ...@@ -26,43 +26,43 @@
}); });
}; };
this.queryConfig = function() { this.queryConfig = function () {
$http.get("demo/config/" + encodeURIComponent(this.configQuery.configName)) $http.get("demo/config/" + encodeURIComponent(this.configQuery.configName))
.success(function(data) { .success(function (data) {
self.configQuery.configValue = data.value; self.configQuery.configValue = data.value;
}) })
.error(function(data, status) { .error(function (data, status) {
toastr.error((data && data.msg) || 'Load config failed'); toastr.error((data && data.msg) || 'Load config failed');
}); });
}; };
this.queryInjectedConfig = function () { this.queryInjectedConfig = function () {
$http.get("demo/injected/config") $http.get("demo/injected/config")
.success(function(data) { .success(function (data) {
self.injectedConfigValue = data.value; self.injectedConfigValue = data.value;
}) })
.error(function(data, status) { .error(function (data, status) {
toastr.error((data && data.msg) || 'Load injected config failed'); toastr.error((data && data.msg) || 'Load injected config failed');
}); });
}; };
this.refreshConfig = function() { this.refreshConfig = function () {
$http.post("demo/refresh") $http.post("demo/refresh")
.success(function(data) { .success(function (data) {
self.assembleRefreshResult(data); self.assembleRefreshResult(data);
}) })
.error(function(data, status) { .error(function (data, status) {
toastr.error((data && data.msg) || 'Refresh config failed'); toastr.error((data && data.msg) || 'Refresh config failed');
}); });
}; };
this.assembleRefreshResult = function(changedPropertyArray) { this.assembleRefreshResult = function (changedPropertyArray) {
if(!changedPropertyArray || !changedPropertyArray.length) { if (!changedPropertyArray || !changedPropertyArray.length) {
this.refreshResult = NONE; this.refreshResult = NONE;
return; return;
} }
this.refreshResult = _.map(changedPropertyArray, function(propertyChange) { this.refreshResult = _.map(changedPropertyArray, function (propertyChange) {
return propertyChange.propertyName + '(' + propertyChange.changeType + ')'; return propertyChange.propertyName + '(' + propertyChange.changeType + ')';
}); });
}; };
......
...@@ -4,4 +4,95 @@ ...@@ -4,4 +4,95 @@
* Copyright (c) 2015 Wes Cruver * Copyright (c) 2015 Wes Cruver
* License: MIT * License: MIT
*/ */
!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(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),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(a,b,c,d){function e(){k||(k=a.get("$animate"));var e=b.find(n).eq(0);c.cancel(m),r||(d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,e,angular.element(e[0].lastChild)),u&&k.enter(q,e,angular.element(e[0].lastChild)),f(w))}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),d.$broadcast("cfpLoadingBar:completed"),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q)},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(); !function () {
\ No newline at end of file "use strict";
angular.module("angular-loading-bar", ["cfp.loadingBarInterceptor"]), angular.module("chieffancypants.loadingBar", ["cfp.loadingBarInterceptor"]), angular.module("cfp.loadingBarInterceptor", ["cfp.loadingBar"]).config(["$httpProvider", function (a) {
var b = ["$q", "$cacheFactory", "$timeout", "$rootScope", "$log", "cfpLoadingBar", function (b, c, d, e, f, g) {
function h() {
d.cancel(j), g.complete(), l = 0, k = 0
}
function i(b) {
var d, e = c.get("$http"), f = a.defaults;
!b.cache && !f.cache || b.cache === !1 || "GET" !== b.method && "JSONP" !== b.method || (d = angular.isObject(b.cache) ? b.cache : angular.isObject(f.cache) ? f.cache : e);
var g = void 0 !== d ? void 0 !== d.get(b.url) : !1;
return void 0 !== b.cached && g !== b.cached ? b.cached : (b.cached = g, g)
}
var j, k = 0, l = 0, m = g.latencyThreshold;
return {
request: function (a) {
return a.ignoreLoadingBar || i(a) || (e.$broadcast("cfpLoadingBar:loading", {url: a.url}), 0 === k && (j = d(function () {
g.start()
}, m)), k++, g.set(l / k)), a
}, response: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), a) : (f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), a)
}, responseError: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), b.reject(a)) : (f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), b.reject(a))
}
}
}];
a.interceptors.push(b)
}]), 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 (a, b, c, d) {
function e() {
k || (k = a.get("$animate"));
var e = b.find(n).eq(0);
c.cancel(m), r || (d.$broadcast("cfpLoadingBar:started"), r = !0, v && k.enter(o, e, angular.element(e[0].lastChild)), u && k.enter(q, e, angular.element(e[0].lastChild)), f(w))
}
function f(a) {
if (r) {
var b = 100 * a + "%";
p.css("width", b), s = a, t && (c.cancel(l), l = c(function () {
g()
}, 250))
}
}
function g() {
if (!(h() >= 1)) {
var a = 0, b = h();
a = b >= 0 && .25 > b ? (3 * Math.random() + 3) / 100 : b >= .25 && .65 > b ? 3 * Math.random() / 100 : b >= .65 && .9 > b ? 2 * Math.random() / 100 : b >= .9 && .99 > b ? .005 : 0;
var c = h() + a;
f(c)
}
}
function h() {
return s
}
function i() {
s = 0, r = !1
}
function j() {
k || (k = a.get("$animate")), d.$broadcast("cfpLoadingBar:completed"), f(1), c.cancel(m), m = c(function () {
var a = k.leave(o, i);
a && a.then && a.then(i), k.leave(q)
}, 500)
}
var k, l, m, n = this.parentSelector, o = angular.element(this.loadingBarTemplate), p = o.find("div").eq(0), q = angular.element(this.spinnerTemplate), r = !1, s = 0, t = this.autoIncrement, u = this.includeSpinner, v = this.includeBar, w = this.startSize;
return {
start: e,
set: f,
status: h,
inc: g,
complete: j,
autoIncrement: this.autoIncrement,
includeSpinner: this.includeSpinner,
latencyThreshold: this.latencyThreshold,
parentSelector: this.parentSelector,
startSize: this.startSize
}
}]
})
}();
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
.toast-title{font-weight:700}.toast-message{word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8}#toast-container>:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4}.toast{opacity:1!important}.toast.ng-enter{opacity:0!important;transition:opacity .3s linear}.toast.ng-enter.ng-enter-active{opacity:1!important}.toast.ng-leave{opacity:1;transition:opacity .3s linear}.toast.ng-leave.ng-leave-active{opacity:0!important}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:241px)and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:481px)and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}} .toast-title {
\ No newline at end of file font-weight: 700
}
.toast-message {
word-wrap: break-word
}
.toast-message a, .toast-message label {
color: #fff
}
.toast-message a:hover {
color: #ccc;
text-decoration: none
}
.toast-close-button {
position: relative;
right: -.3em;
top: -.3em;
float: right;
font-size: 20px;
font-weight: 700;
color: #fff;
-webkit-text-shadow: 0 1px 0 #fff;
text-shadow: 0 1px 0 #fff;
opacity: .8
}
.toast-close-button:focus, .toast-close-button:hover {
color: #000;
text-decoration: none;
cursor: pointer;
opacity: .4
}
button.toast-close-button {
padding: 0;
cursor: pointer;
background: 0 0;
border: 0;
-webkit-appearance: none
}
.toast-top-center {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-left {
top: 12px;
left: 12px
}
.toast-top-right {
top: 12px;
right: 12px
}
.toast-bottom-right {
right: 12px;
bottom: 12px
}
.toast-bottom-left {
bottom: 12px;
left: 12px
}
#toast-container {
position: fixed;
z-index: 999999
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
#toast-container > div {
position: relative;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999;
-webkit-box-shadow: 0 0 12px #999;
box-shadow: 0 0 12px #999;
color: #fff;
opacity: .8
}
#toast-container > :hover {
-moz-box-shadow: 0 0 12px #000;
-webkit-box-shadow: 0 0 12px #000;
box-shadow: 0 0 12px #000;
opacity: 1;
cursor: pointer
}
#toast-container > .toast-info {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-success {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==) !important
}
#toast-container > .toast-warning {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=) !important
}
#toast-container.toast-bottom-center > div, #toast-container.toast-top-center > div {
width: 300px;
margin: auto
}
#toast-container.toast-bottom-full-width > div, #toast-container.toast-top-full-width > div {
width: 96%;
margin: auto
}
.toast {
background-color: #030303
}
.toast-success {
background-color: #51a351
}
.toast-error {
background-color: #bd362f
}
.toast-info {
background-color: #2f96b4
}
.toast-warning {
background-color: #f89406
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000;
opacity: .4
}
.toast {
opacity: 1 !important
}
.toast.ng-enter {
opacity: 0 !important;
transition: opacity .3s linear
}
.toast.ng-enter.ng-enter-active {
opacity: 1 !important
}
.toast.ng-leave {
opacity: 1;
transition: opacity .3s linear
}
.toast.ng-leave.ng-leave-active {
opacity: 0 !important
}
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 241px)and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 481px)and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em
}
}
#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 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms 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)}} #loading-bar, #loading-bar-spinner {
\ No newline at end of file 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 400ms linear infinite;
-moz-animation: loading-bar-spinner 400ms linear infinite;
-ms-animation: loading-bar-spinner 400ms linear infinite;
-o-animation: loading-bar-spinner 400ms linear infinite;
animation: loading-bar-spinner 400ms 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)
}
}
...@@ -44,7 +44,8 @@ ...@@ -44,7 +44,8 @@
<div id="config-value-wrapper" class="clearfix"> <div id="config-value-wrapper" class="clearfix">
<label class="col-sm-2 control-label">Config Value</label> <label class="col-sm-2 control-label">Config Value</label>
<div class="col-sm-3"> <div class="col-sm-3">
<textarea rows="2" cols="38" readonly class="form-control">{{demoCtrl.configQuery.configValue}}</textarea> <textarea rows="2" cols="38" readonly
class="form-control">{{demoCtrl.configQuery.configValue}}</textarea>
</div> </div>
</div> </div>
</div> </div>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId> <artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apollo-portal</artifactId> <artifactId>apollo-portal</artifactId>
<name>Apollo Portal</name> <name>Apollo Portal</name>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.ctrip.apollo</groupId> <groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId> <artifactId>apollo-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId> <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId> <artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
...@@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; ...@@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class PortalApplication { public class PortalApplication {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
SpringApplication.run(PortalApplication.class, args); SpringApplication.run(PortalApplication.class, args);
} }
......
...@@ -7,11 +7,11 @@ import org.springframework.web.client.RestTemplate; ...@@ -7,11 +7,11 @@ import org.springframework.web.client.RestTemplate;
public class RestUtils { public class RestUtils {
private static RestTemplate restTemplate = new RestTemplate(); private static RestTemplate restTemplate = new RestTemplate();
public static <T> T exchangeInGET(String url, Class<T> responseType) { public static <T> T exchangeInGET(String url, Class<T> responseType) {
ResponseEntity<T> response = ResponseEntity<T> response =
restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<Void>((Void) null), responseType); restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<Void>((Void) null), responseType);
return response.getBody(); return response.getBody();
} }
} }
...@@ -2,5 +2,5 @@ package com.ctrip.apollo.portal.constants; ...@@ -2,5 +2,5 @@ package com.ctrip.apollo.portal.constants;
public interface PortalConstants { public interface PortalConstants {
long LASTEST_VERSION_ID = -1; long LASTEST_VERSION_ID = -1;
} }
package com.ctrip.apollo.portal.controller; package com.ctrip.apollo.portal.controller;
import java.util.List; import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.service.AppService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
...@@ -11,9 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -11,9 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.portal.entity.App; import java.util.List;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.service.AppService;
@RestController @RestController
@RequestMapping("/apps") @RequestMapping("/apps")
......
...@@ -4,6 +4,7 @@ import com.ctrip.apollo.portal.constants.PortalConstants; ...@@ -4,6 +4,7 @@ import com.ctrip.apollo.portal.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO; import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.exception.NotFoundException; import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.service.ConfigService; import com.ctrip.apollo.portal.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -13,22 +14,23 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -13,22 +14,23 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/configs") @RequestMapping("/configs")
public class ConfigController { public class ConfigController {
@Autowired @Autowired
private ConfigService configService; private ConfigService configService;
@RequestMapping("/{appId}/{env}/{versionId}") @RequestMapping("/{appId}/{env}/{versionId}")
public AppConfigVO detail(@PathVariable long appId, @PathVariable String env, @PathVariable long versionId) { public AppConfigVO detail(@PathVariable long appId, @PathVariable String env,
@PathVariable long versionId) {
if (appId <= 0) { if (appId <= 0) {
throw new NotFoundException(); throw new NotFoundException();
} }
if (versionId == PortalConstants.LASTEST_VERSION_ID) { if (versionId == PortalConstants.LASTEST_VERSION_ID) {
return configService.loadLatestConfig(appId); return configService.loadLatestConfig(appId);
} else if (versionId > 0) { } else if (versionId > 0) {
return configService.loadReleaseConfig(appId, versionId); return configService.loadReleaseConfig(appId, versionId);
} else { } else {
throw new NotFoundException(); throw new NotFoundException();
}
} }
}
} }
package com.ctrip.apollo.portal.controller; package com.ctrip.apollo.portal.controller;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import com.ctrip.apollo.portal.exception.NotFoundException;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
...@@ -19,7 +11,18 @@ import org.springframework.web.bind.annotation.ControllerAdvice; ...@@ -19,7 +11,18 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import com.ctrip.apollo.portal.exception.NotFoundException; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.MediaType.APPLICATION_JSON;
@ControllerAdvice @ControllerAdvice
public class GlobalDefaultExceptionHandler { public class GlobalDefaultExceptionHandler {
...@@ -29,12 +32,13 @@ public class GlobalDefaultExceptionHandler { ...@@ -29,12 +32,13 @@ public class GlobalDefaultExceptionHandler {
} }
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request, private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex) { HttpStatus status, Throwable ex) {
return handleError(request, status, ex, ex.getMessage()); return handleError(request, status, ex, ex.getMessage());
} }
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request, private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex, String message) { HttpStatus status, Throwable ex,
String message) {
ex = resolveError(ex); ex = resolveError(ex);
Map<String, Object> errorAttributes = new LinkedHashMap<>(); Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("status", status.value()); errorAttributes.put("status", status.value());
...@@ -49,13 +53,14 @@ public class GlobalDefaultExceptionHandler { ...@@ -49,13 +53,14 @@ public class GlobalDefaultExceptionHandler {
@ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeException.class}) @ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeException.class})
public ResponseEntity<Map<String, Object>> methodNotSupportedException(HttpServletRequest request, public ResponseEntity<Map<String, Object>> methodNotSupportedException(HttpServletRequest request,
ServletException ex) { ServletException ex) {
return handleError(request, BAD_REQUEST, ex); return handleError(request, BAD_REQUEST, ex);
} }
@ExceptionHandler(NotFoundException.class) @ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = NOT_FOUND) @ResponseStatus(value = NOT_FOUND)
public void notFound(HttpServletRequest req, NotFoundException ex) {} public void notFound(HttpServletRequest req, NotFoundException ex) {
}
private Throwable resolveError(Throwable ex) { private Throwable resolveError(Throwable ex) {
while (ex instanceof ServletException && ex.getCause() != null) { while (ex instanceof ServletException && ex.getCause() != null) {
......
...@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.controller; ...@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.core.dto.VersionDTO; import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.service.VersionService; import com.ctrip.apollo.portal.service.VersionService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -13,11 +14,11 @@ import java.util.List; ...@@ -13,11 +14,11 @@ import java.util.List;
@RequestMapping("/version") @RequestMapping("/version")
public class VersionController { public class VersionController {
@Autowired @Autowired
private VersionService versionService; private VersionService versionService;
@RequestMapping("/{appId}/{env}") @RequestMapping("/{appId}/{env}")
public List<VersionDTO> versions(@PathVariable long appId, @PathVariable String env){ public List<VersionDTO> versions(@PathVariable long appId, @PathVariable String env) {
return versionService.findVersionsByApp(appId, env); return versionService.findVersionsByApp(appId, env);
} }
} }
...@@ -11,7 +11,7 @@ import javax.persistence.Id; ...@@ -11,7 +11,7 @@ import javax.persistence.Id;
public class App implements Serializable { public class App implements Serializable {
/** /**
* *
*/ */
private static final long serialVersionUID = 7348554309210401557L; private static final long serialVersionUID = 7348554309210401557L;
......
...@@ -9,177 +9,174 @@ import java.util.List; ...@@ -9,177 +9,174 @@ import java.util.List;
public class AppConfigVO { public class AppConfigVO {
private long appId;
private Env env;
/**
* latest version if version is zero, or is release version
*/
private long versionId;
/**
* default cluster and app self’s configs
*/
private List<ConfigItemDTO> defaultClusterConfigs;
/**
* default cluster and override other app configs
*/
private List<OverrideAppConfig> overrideAppConfigs;
/**
* configs in different cluster maybe different.
* overrideClusterConfigs only save diff configs from default cluster.
* For example:
* default cluster has 3 configs:
* {a -> A, b -> B, c -> C}
*
* cluster1 has 1 config
* {b -> D}
*
* if client read cluster1 configs will return {a -> A, b -> D, c -> C}
*/
private List<OverrideClusterConfig> overrideClusterConfigs;
public AppConfigVO() {
}
public static AppConfigVO newInstance(long appId, long versionId) {
AppConfigVO instance = new AppConfigVO();
instance.setAppId(appId);
instance.setVersionId(versionId);
instance.setDefaultClusterConfigs(new LinkedList<>());
instance.setOverrideAppConfigs(new LinkedList<>());
instance.setOverrideClusterConfigs(new LinkedList<>());
return instance;
}
public boolean isLatestVersion() {
return versionId == 0;
}
public static class OverrideAppConfig {
private long appId; private long appId;
private List<ConfigItemDTO> configs;
private Env env; public OverrideAppConfig() {
/**
* latest version if version is zero, or is release version
*/
private long versionId;
/**
* default cluster and app self’s configs
*/
private List<ConfigItemDTO> defaultClusterConfigs;
/**
* default cluster and override other app configs
*/
private List<OverrideAppConfig> overrideAppConfigs;
/**
* configs in different cluster maybe different.
* overrideClusterConfigs only save diff configs from default cluster.
* For example:
* default cluster has 3 configs:
* {a -> A, b -> B, c -> C}
*
* cluster1 has 1 config
* {b -> D}
*
* if client read cluster1 configs will return {a -> A, b -> D, c -> C}
*
*
*/
private List<OverrideClusterConfig> overrideClusterConfigs;
public AppConfigVO(){
} }
public static AppConfigVO newInstance(long appId, long versionId){ public long getAppId() {
AppConfigVO instance = new AppConfigVO(); return appId;
instance.setAppId(appId);
instance.setVersionId(versionId);
instance.setDefaultClusterConfigs(new LinkedList<>());
instance.setOverrideAppConfigs(new LinkedList<>());
instance.setOverrideClusterConfigs(new LinkedList<>());
return instance;
} }
public boolean isLatestVersion() { public void setAppId(long appId) {
return versionId == 0; this.appId = appId;
} }
public static class OverrideAppConfig { public List<ConfigItemDTO> getConfigs() {
return configs;
private long appId;
private List<ConfigItemDTO> configs;
public OverrideAppConfig(){
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public List<ConfigItemDTO> getConfigs() {
return configs;
}
public void setConfigs(List<ConfigItemDTO> configs) {
this.configs = configs;
}
public void addConfig(ConfigItemDTO config){
if (configs == null){
configs = new LinkedList<>();
}
configs.add(config);
}
} }
public void setConfigs(List<ConfigItemDTO> configs) {
this.configs = configs;
}
public static class OverrideClusterConfig { public void addConfig(ConfigItemDTO config) {
if (configs == null) {
configs = new LinkedList<>();
}
configs.add(config);
}
}
private String clusterName;
private List<ConfigItemDTO> configs;
public OverrideClusterConfig(){} public static class OverrideClusterConfig {
public String getClusterName() { private String clusterName;
return clusterName; private List<ConfigItemDTO> configs;
}
public void setClusterName(String clusterName) { public OverrideClusterConfig() {
this.clusterName = clusterName; }
}
public List<ConfigItemDTO> getConfigs() { public String getClusterName() {
return configs; return clusterName;
} }
public void setConfigs(List<ConfigItemDTO> configs) { public void setClusterName(String clusterName) {
this.configs = configs; this.clusterName = clusterName;
}
} }
public List<ConfigItemDTO> getConfigs() {
return configs;
}
public void setConfigs(List<ConfigItemDTO> configs) {
this.configs = configs;
}
}
public long getAppId() { public long getAppId() {
return appId; return appId;
} }
public void setAppId(long appId) { public void setAppId(long appId) {
this.appId = appId; this.appId = appId;
} }
public Env getEnv() { public Env getEnv() {
return env; return env;
} }
public void setEnv(Env env) { public void setEnv(Env env) {
this.env = env; this.env = env;
} }
public long getVersionId() { public long getVersionId() {
return versionId; return versionId;
} }
public void setVersionId(long versionId) { public void setVersionId(long versionId) {
this.versionId = versionId; this.versionId = versionId;
} }
public List<ConfigItemDTO> getDefaultClusterConfigs() { public List<ConfigItemDTO> getDefaultClusterConfigs() {
return defaultClusterConfigs; return defaultClusterConfigs;
} }
public void setDefaultClusterConfigs(List<ConfigItemDTO> defaultClusterConfigs) { public void setDefaultClusterConfigs(List<ConfigItemDTO> defaultClusterConfigs) {
this.defaultClusterConfigs = defaultClusterConfigs; this.defaultClusterConfigs = defaultClusterConfigs;
} }
public List<OverrideAppConfig> getOverrideAppConfigs() { public List<OverrideAppConfig> getOverrideAppConfigs() {
return overrideAppConfigs; return overrideAppConfigs;
} }
public void setOverrideAppConfigs(List<OverrideAppConfig> overrideAppConfigs) { public void setOverrideAppConfigs(List<OverrideAppConfig> overrideAppConfigs) {
this.overrideAppConfigs = overrideAppConfigs; this.overrideAppConfigs = overrideAppConfigs;
} }
public List<OverrideClusterConfig> getOverrideClusterConfigs() { public List<OverrideClusterConfig> getOverrideClusterConfigs() {
return overrideClusterConfigs; return overrideClusterConfigs;
} }
public void setOverrideClusterConfigs(List<OverrideClusterConfig> overrideClusterConfigs) { public void setOverrideClusterConfigs(List<OverrideClusterConfig> overrideClusterConfigs) {
this.overrideClusterConfigs = overrideClusterConfigs; this.overrideClusterConfigs = overrideClusterConfigs;
} }
@Override @Override
public String toString() { public String toString() {
return "Config4PortalDTO{" + return "Config4PortalDTO{" +
"appId=" + appId + "appId=" + appId +
", env=" + env + ", env=" + env +
", versionId=" + versionId + ", versionId=" + versionId +
", defaultClusterConfigs=" + defaultClusterConfigs + ", defaultClusterConfigs=" + defaultClusterConfigs +
", overrideAppConfigs=" + overrideAppConfigs + ", overrideAppConfigs=" + overrideAppConfigs +
", overrideClusterConfigs=" + overrideClusterConfigs + ", overrideClusterConfigs=" + overrideClusterConfigs +
'}'; '}';
} }
} }
...@@ -11,7 +11,7 @@ import javax.persistence.Id; ...@@ -11,7 +11,7 @@ import javax.persistence.Id;
public class Privilege implements Serializable { public class Privilege implements Serializable {
/** /**
* *
*/ */
private static final long serialVersionUID = -430087307622435118L; private static final long serialVersionUID = -430087307622435118L;
......
...@@ -2,31 +2,32 @@ package com.ctrip.apollo.portal.enums; ...@@ -2,31 +2,32 @@ package com.ctrip.apollo.portal.enums;
public enum Env { public enum Env {
DEV("dev"), FWS("fws"), FAT("fat"), UAT("uat"), LPT("lpt"), PROD("prod"), TOOLS("tools"), UN_KNOW(""); DEV("dev"), FWS("fws"), FAT("fat"), UAT("uat"), LPT("lpt"), PROD("prod"), TOOLS("tools"), UN_KNOW(
"");
private String value; private String value;
Env(String value) { Env(String value) {
this.value = value; this.value = value;
} }
public static Env valueFrom(String env) { public static Env valueFrom(String env) {
if (env == null || "".equals(env)) { if (env == null || "".equals(env)) {
return UN_KNOW; return UN_KNOW;
} else if ("dev".equals(env)) { } else if ("dev".equals(env)) {
return DEV; return DEV;
} else if ("fws".equals(env)) { } else if ("fws".equals(env)) {
return FWS; return FWS;
} else if ("fat".equals(env)) { } else if ("fat".equals(env)) {
return FAT; return FAT;
} else if ("uat".equals(env)) { } else if ("uat".equals(env)) {
return UAT; return UAT;
} else if ("prod".equals(env)) { } else if ("prod".equals(env)) {
return PROD; return PROD;
} else if ("tools".equals(env)) { } else if ("tools".equals(env)) {
return TOOLS; return TOOLS;
} else{ } else {
return UN_KNOW; return UN_KNOW;
}
} }
}
} }
...@@ -2,7 +2,7 @@ package com.ctrip.apollo.portal.exception; ...@@ -2,7 +2,7 @@ package com.ctrip.apollo.portal.exception;
public class NotFoundException extends RuntimeException { public class NotFoundException extends RuntimeException {
/** /**
* *
*/ */
private static final long serialVersionUID = 7611357629749481796L; private static final long serialVersionUID = 7611357629749481796L;
} }
package com.ctrip.apollo.portal.repository; package com.ctrip.apollo.portal.repository;
import com.ctrip.apollo.portal.entity.App;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import com.ctrip.apollo.portal.entity.App;
public interface AppRepository extends PagingAndSortingRepository<App, String> { public interface AppRepository extends PagingAndSortingRepository<App, String> {
Page<App> findAll(Pageable pageable); Page<App> findAll(Pageable pageable);
......
package com.ctrip.apollo.portal.repository; package com.ctrip.apollo.portal.repository;
import java.util.List; import com.ctrip.apollo.portal.entity.Privilege;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import com.ctrip.apollo.portal.entity.Privilege; import java.util.List;
public interface PrivilegeRepository extends PagingAndSortingRepository<Privilege, Long> { public interface PrivilegeRepository extends PagingAndSortingRepository<Privilege, Long> {
......
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import java.util.Date; import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.repository.AppRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ctrip.apollo.portal.entity.App; import java.util.Date;
import com.ctrip.apollo.portal.repository.AppRepository;
@Service @Service
public class AppService { public class AppService {
......
...@@ -4,20 +4,17 @@ import com.ctrip.apollo.portal.entity.AppConfigVO; ...@@ -4,20 +4,17 @@ import com.ctrip.apollo.portal.entity.AppConfigVO;
public interface ConfigService { public interface ConfigService {
/** /**
* load config info by appId and versionId * load config info by appId and versionId
* @param appId */
* @param versionId AppConfigVO loadReleaseConfig(long appId, long versionId);
* @return
*/
AppConfigVO loadReleaseConfig(long appId, long versionId);
/** /**
* *
* @param appId * @param appId
* @return * @return
*/ */
AppConfigVO loadLatestConfig(long appId); AppConfigVO loadLatestConfig(long appId);
} }
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import java.util.List; import com.ctrip.apollo.portal.entity.Privilege;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.repository.PrivilegeRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ctrip.apollo.portal.entity.Privilege; import java.util.List;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.repository.PrivilegeRepository;
@Service @Service
public class PrivilegeService { public class PrivilegeService {
......
...@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.service; ...@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.service;
import com.ctrip.apollo.core.dto.VersionDTO; import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.RestUtils; import com.ctrip.apollo.portal.RestUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
...@@ -9,7 +10,7 @@ import java.util.List; ...@@ -9,7 +10,7 @@ import java.util.List;
@Service @Service
public class VersionService { public class VersionService {
public List<VersionDTO> findVersionsByApp(long appId, String env){ public List<VersionDTO> findVersionsByApp(long appId, String env) {
return RestUtils.exchangeInGET("http://localhost:8090/version/app/" + appId, List.class); return RestUtils.exchangeInGET("http://localhost:8090/version/app/" + appId, List.class);
} }
} }
package com.ctrip.apollo.portal.service.impl; package com.ctrip.apollo.portal.service.impl;
import com.google.common.collect.Maps;
import com.ctrip.apollo.core.Constants; import com.ctrip.apollo.core.Constants;
import com.ctrip.apollo.core.dto.*; import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.RestUtils; import com.ctrip.apollo.portal.RestUtils;
import com.ctrip.apollo.portal.constants.PortalConstants; import com.ctrip.apollo.portal.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO; import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.service.ConfigService; import com.ctrip.apollo.portal.service.ConfigService;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@Service @Service
public class ConfigServiceImpl implements ConfigService { public class ConfigServiceImpl implements ConfigService {
public static final String ADMIN_SERVICE_HOST = "http://localhost:8090"; public static final String ADMIN_SERVICE_HOST = "http://localhost:8090";
private ObjectMapper objectMapper = new ObjectMapper(); private ObjectMapper objectMapper = new ObjectMapper();
@Override @Override
public AppConfigVO loadReleaseConfig(long appId, long versionId) { public AppConfigVO loadReleaseConfig(long appId, long versionId) {
if (appId <= 0 || versionId <= 0) { if (appId <= 0 || versionId <= 0) {
return null; return null;
} }
long releaseId = getReleaseIdFromVersionId(versionId); long releaseId = getReleaseIdFromVersionId(versionId);
ReleaseSnapshotDTO[] releaseSnapShots = RestUtils.exchangeInGET( ReleaseSnapshotDTO[] releaseSnapShots = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/configs/release/" + releaseId, ReleaseSnapshotDTO[].class); ADMIN_SERVICE_HOST + "/configs/release/" + releaseId, ReleaseSnapshotDTO[].class);
if (releaseSnapShots == null || releaseSnapShots.length == 0) { if (releaseSnapShots == null || releaseSnapShots.length == 0) {
return null; return null;
} }
AppConfigVO appConfigVO = AppConfigVO.newInstance(appId, versionId); AppConfigVO appConfigVO = AppConfigVO.newInstance(appId, versionId);
for (ReleaseSnapshotDTO snapShot : releaseSnapShots) { for (ReleaseSnapshotDTO snapShot : releaseSnapShots) {
//default cluster //default cluster
if (Constants.DEFAULT_CLUSTER_NAME.equals(snapShot.getClusterName())) { if (Constants.DEFAULT_CLUSTER_NAME.equals(snapShot.getClusterName())) {
collectDefaultClusterConfigs(appId, snapShot, appConfigVO); collectDefaultClusterConfigs(appId, snapShot, appConfigVO);
} else {//cluster special configs } else {//cluster special configs
collectSpecialClusterConfigs(appId, snapShot, appConfigVO); collectSpecialClusterConfigs(appId, snapShot, appConfigVO);
} }
}
return appConfigVO;
} }
return appConfigVO;
private long getReleaseIdFromVersionId(long versionId) { }
VersionDTO version =
RestUtils.exchangeInGET(ADMIN_SERVICE_HOST + "/version/" + versionId, VersionDTO.class); private long getReleaseIdFromVersionId(long versionId) {
if (version == null) { VersionDTO version =
return -1; RestUtils.exchangeInGET(ADMIN_SERVICE_HOST + "/version/" + versionId, VersionDTO.class);
} if (version == null) {
return version.getReleaseId(); return -1;
} }
return version.getReleaseId();
}
private void collectDefaultClusterConfigs(long appId, ReleaseSnapshotDTO snapShot, AppConfigVO appConfigVO) { private void collectDefaultClusterConfigs(long appId, ReleaseSnapshotDTO snapShot,
AppConfigVO appConfigVO) {
Map<Long, List<ConfigItemDTO>> groupedConfigs =
groupConfigsByApp(snapShot.getConfigurations());
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs = Map<Long, List<ConfigItemDTO>> groupedConfigs =
appConfigVO.getOverrideAppConfigs(); groupConfigsByApp(snapShot.getConfigurations());
for (Map.Entry<Long, List<ConfigItemDTO>> entry : groupedConfigs.entrySet()) { List<AppConfigVO.OverrideAppConfig> overrideAppConfigs =
long configAppId = entry.getKey(); appConfigVO.getOverrideAppConfigs();
List<ConfigItemDTO> kvs = entry.getValue();
if (configAppId == appId) { for (Map.Entry<Long, List<ConfigItemDTO>> entry : groupedConfigs.entrySet()) {
appConfigVO.setDefaultClusterConfigs(kvs); long configAppId = entry.getKey();
} else { List<ConfigItemDTO> kvs = entry.getValue();
AppConfigVO.OverrideAppConfig overrideAppConfig = if (configAppId == appId) {
new AppConfigVO.OverrideAppConfig(); appConfigVO.setDefaultClusterConfigs(kvs);
overrideAppConfig.setAppId(configAppId); } else {
overrideAppConfig.setConfigs(kvs);
overrideAppConfigs.add(overrideAppConfig);
}
}
AppConfigVO.OverrideAppConfig overrideAppConfig =
new AppConfigVO.OverrideAppConfig();
overrideAppConfig.setAppId(configAppId);
overrideAppConfig.setConfigs(kvs);
overrideAppConfigs.add(overrideAppConfig);
}
} }
/** }
* appId -> List<KV>
*/
private Map<Long, List<ConfigItemDTO>> groupConfigsByApp(String configJson) {
if (configJson == null || "".equals(configJson)) {
return Maps.newHashMap();
}
Map<Long, List<ConfigItemDTO>> appIdMapKVs = new HashMap<>();
String key; /**
Object value; * appId -> List<KV>
Map<String, String> kvMaps = null; */
try { private Map<Long, List<ConfigItemDTO>> groupConfigsByApp(String configJson) {
kvMaps = objectMapper.readValue(configJson, Map.class); if (configJson == null || "".equals(configJson)) {
} catch (IOException e) { return Maps.newHashMap();
//todo log }
}
for (Map.Entry<String, String> entry : kvMaps.entrySet()) {
key = entry.getKey();
value = entry.getValue();
Long appId = getAppIdFromKey(key);
List<ConfigItemDTO> kvs = appIdMapKVs.get(appId);
if (kvs == null) {
kvs = new LinkedList<>();
appIdMapKVs.put(appId, kvs);
}
kvs.add(new ConfigItemDTO(key, value.toString()));
}
return appIdMapKVs; Map<Long, List<ConfigItemDTO>> appIdMapKVs = new HashMap<>();
String key;
Object value;
Map<String, String> kvMaps = null;
try {
kvMaps = objectMapper.readValue(configJson, Map.class);
} catch (IOException e) {
//todo log
} }
for (Map.Entry<String, String> entry : kvMaps.entrySet()) {
private Long getAppIdFromKey(String key) { key = entry.getKey();
return Long.valueOf(key.substring(0, key.indexOf("."))); value = entry.getValue();
Long appId = getAppIdFromKey(key);
List<ConfigItemDTO> kvs = appIdMapKVs.get(appId);
if (kvs == null) {
kvs = new LinkedList<>();
appIdMapKVs.put(appId, kvs);
}
kvs.add(new ConfigItemDTO(key, value.toString()));
} }
private void collectSpecialClusterConfigs(long appId, ReleaseSnapshotDTO snapShot, AppConfigVO appConfigVO) { return appIdMapKVs;
List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs =
appConfigVO.getOverrideClusterConfigs(); }
AppConfigVO.OverrideClusterConfig overrideClusterConfig =
new AppConfigVO.OverrideClusterConfig(); private Long getAppIdFromKey(String key) {
overrideClusterConfig.setClusterName(snapShot.getClusterName()); return Long.valueOf(key.substring(0, key.indexOf(".")));
//todo step1: cluster special config can't override other app config }
overrideClusterConfig.setConfigs(groupConfigsByApp(snapShot.getConfigurations()).get(appId));
overrideClusterConfigs.add(overrideClusterConfig); private void collectSpecialClusterConfigs(long appId, ReleaseSnapshotDTO snapShot,
AppConfigVO appConfigVO) {
List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs =
appConfigVO.getOverrideClusterConfigs();
AppConfigVO.OverrideClusterConfig overrideClusterConfig =
new AppConfigVO.OverrideClusterConfig();
overrideClusterConfig.setClusterName(snapShot.getClusterName());
//todo step1: cluster special config can't override other app config
overrideClusterConfig.setConfigs(groupConfigsByApp(snapShot.getConfigurations()).get(appId));
overrideClusterConfigs.add(overrideClusterConfig);
}
@Override
public AppConfigVO loadLatestConfig(long appId) {
if (appId <= 0) {
return null;
} }
@Override ClusterDTO[] clusters = RestUtils.exchangeInGET(
public AppConfigVO loadLatestConfig(long appId) { ADMIN_SERVICE_HOST + "/cluster/app/" + appId, ClusterDTO[].class);
if (appId <= 0) { if (clusters == null || clusters.length == 0) {
return null; return null;
}
ClusterDTO[] clusters = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/cluster/app/" + appId, ClusterDTO[].class);
if (clusters == null || clusters.length == 0) {
return null;
}
StringBuilder sb = new StringBuilder();
for (ClusterDTO cluster : clusters) {
sb.append(cluster.getId()).append(",");
}
ConfigItemDTO[] configItems = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/configs/latest?clusterIds=" + sb.substring(0,
sb.length() - 1), ConfigItemDTO[].class);
return buildAPPConfigVO(appId, Arrays.asList(configItems));
} }
private AppConfigVO buildAPPConfigVO(long appId, List<ConfigItemDTO> configItems) { StringBuilder sb = new StringBuilder();
if (configItems == null || configItems.size() == 0) { for (ClusterDTO cluster : clusters) {
return null; sb.append(cluster.getId()).append(",");
} }
Map<String, List<ConfigItemDTO>> groupedClusterConfigs = groupConfigByCluster(configItems);
AppConfigVO appConfigVO =
AppConfigVO.newInstance(appId, PortalConstants.LASTEST_VERSION_ID);
groupConfigByAppAndEnrichDTO(groupedClusterConfigs, appConfigVO); ConfigItemDTO[] configItems = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/configs/latest?clusterIds=" + sb.substring(0,
sb.length() - 1), ConfigItemDTO[].class);
return appConfigVO; return buildAPPConfigVO(appId, Arrays.asList(configItems));
}
private AppConfigVO buildAPPConfigVO(long appId, List<ConfigItemDTO> configItems) {
if (configItems == null || configItems.size() == 0) {
return null;
} }
private Map<String, List<ConfigItemDTO>> groupConfigByCluster(List<ConfigItemDTO> configItems) { Map<String, List<ConfigItemDTO>> groupedClusterConfigs = groupConfigByCluster(configItems);
Map<String, List<ConfigItemDTO>> groupedClusterConfigs = new HashMap<>();
String clusterName;
for (ConfigItemDTO configItem : configItems) {
clusterName = configItem.getClusterName();
List<ConfigItemDTO> clusterConfigs = groupedClusterConfigs.get(clusterName);
if (clusterConfigs == null) {
clusterConfigs = new LinkedList<>();
groupedClusterConfigs.put(clusterName, clusterConfigs);
}
clusterConfigs.add(configItem);
}
return groupedClusterConfigs;
}
private void groupConfigByAppAndEnrichDTO(Map<String, List<ConfigItemDTO>> groupedClusterConfigs, AppConfigVO appConfigVO) { AppConfigVO appConfigVO =
long appId = appConfigVO.getAppId(); AppConfigVO.newInstance(appId, PortalConstants.LASTEST_VERSION_ID);
List<ConfigItemDTO> defaultClusterConfigs = appConfigVO.getDefaultClusterConfigs(); groupConfigByAppAndEnrichDTO(groupedClusterConfigs, appConfigVO);
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs = return appConfigVO;
appConfigVO.getOverrideAppConfigs();
List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs = }
appConfigVO.getOverrideClusterConfigs();
String clusterName; private Map<String, List<ConfigItemDTO>> groupConfigByCluster(List<ConfigItemDTO> configItems) {
List<ConfigItemDTO> clusterConfigs; Map<String, List<ConfigItemDTO>> groupedClusterConfigs = new HashMap<>();
for (Map.Entry<String, List<ConfigItemDTO>> entry : groupedClusterConfigs.entrySet()) {
clusterName = entry.getKey();
clusterConfigs = entry.getValue();
if (Constants.DEFAULT_CLUSTER_NAME.equals(clusterName)) { String clusterName;
//default cluster configs for (ConfigItemDTO configItem : configItems) {
collectDefaultClusterConfigs(appId, clusterConfigs, defaultClusterConfigs, overrideAppConfigs); clusterName = configItem.getClusterName();
} else { List<ConfigItemDTO> clusterConfigs = groupedClusterConfigs.get(clusterName);
//override cluster configs if (clusterConfigs == null) {
collectSpecialClusterConfigs(clusterName, clusterConfigs, overrideClusterConfigs); clusterConfigs = new LinkedList<>();
} groupedClusterConfigs.put(clusterName, clusterConfigs);
} }
clusterConfigs.add(configItem);
} }
return groupedClusterConfigs;
}
private void groupConfigByAppAndEnrichDTO(Map<String, List<ConfigItemDTO>> groupedClusterConfigs,
AppConfigVO appConfigVO) {
long appId = appConfigVO.getAppId();
List<ConfigItemDTO> defaultClusterConfigs = appConfigVO.getDefaultClusterConfigs();
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs =
appConfigVO.getOverrideAppConfigs();
List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs =
appConfigVO.getOverrideClusterConfigs();
String clusterName;
List<ConfigItemDTO> clusterConfigs;
for (Map.Entry<String, List<ConfigItemDTO>> entry : groupedClusterConfigs.entrySet()) {
clusterName = entry.getKey();
clusterConfigs = entry.getValue();
if (Constants.DEFAULT_CLUSTER_NAME.equals(clusterName)) {
//default cluster configs
collectDefaultClusterConfigs(appId, clusterConfigs, defaultClusterConfigs,
overrideAppConfigs);
} else {
//override cluster configs
collectSpecialClusterConfigs(clusterName, clusterConfigs, overrideClusterConfigs);
}
}
}
private void collectDefaultClusterConfigs(long appId, List<ConfigItemDTO> clusterConfigs, List<ConfigItemDTO> defaultClusterConfigs, List<AppConfigVO.OverrideAppConfig> overrideAppConfigs) { private void collectDefaultClusterConfigs(long appId, List<ConfigItemDTO> clusterConfigs,
List<ConfigItemDTO> defaultClusterConfigs,
Map<Long, AppConfigVO.OverrideAppConfig> appIdMapOverrideAppConfig = null; List<AppConfigVO.OverrideAppConfig> overrideAppConfigs) {
for (ConfigItemDTO config : clusterConfigs) { Map<Long, AppConfigVO.OverrideAppConfig> appIdMapOverrideAppConfig = null;
long targetAppId = config.getAppId();
if (appId == targetAppId) {//app self's configs
defaultClusterConfigs.add(config);
} else {//override other app configs
if (appIdMapOverrideAppConfig == null) {
appIdMapOverrideAppConfig = new HashMap<>();
}
AppConfigVO.OverrideAppConfig overrideAppConfig = for (ConfigItemDTO config : clusterConfigs) {
appIdMapOverrideAppConfig.get(targetAppId); long targetAppId = config.getAppId();
if (appId == targetAppId) {//app self's configs
defaultClusterConfigs.add(config);
} else {//override other app configs
if (appIdMapOverrideAppConfig == null) {
appIdMapOverrideAppConfig = new HashMap<>();
}
if (overrideAppConfig == null) { AppConfigVO.OverrideAppConfig overrideAppConfig =
overrideAppConfig = new AppConfigVO.OverrideAppConfig(); appIdMapOverrideAppConfig.get(targetAppId);
appIdMapOverrideAppConfig.put(targetAppId, overrideAppConfig);
overrideAppConfigs.add(overrideAppConfig);
}
overrideAppConfig.setAppId(targetAppId); if (overrideAppConfig == null) {
overrideAppConfig.addConfig(config); overrideAppConfig = new AppConfigVO.OverrideAppConfig();
} appIdMapOverrideAppConfig.put(targetAppId, overrideAppConfig);
overrideAppConfigs.add(overrideAppConfig);
} }
}
private void collectSpecialClusterConfigs(String clusterName, List<ConfigItemDTO> clusterConfigs, List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs) { overrideAppConfig.setAppId(targetAppId);
AppConfigVO.OverrideClusterConfig overrideClusterConfig = overrideAppConfig.addConfig(config);
new AppConfigVO.OverrideClusterConfig(); }
overrideClusterConfig.setClusterName(clusterName);
overrideClusterConfig.setConfigs(clusterConfigs);
overrideClusterConfigs.add(overrideClusterConfig);
} }
}
private void collectSpecialClusterConfigs(String clusterName, List<ConfigItemDTO> clusterConfigs,
List<AppConfigVO.OverrideClusterConfig> overrideClusterConfigs) {
AppConfigVO.OverrideClusterConfig overrideClusterConfig =
new AppConfigVO.OverrideClusterConfig();
overrideClusterConfig.setClusterName(clusterName);
overrideClusterConfig.setConfigs(clusterConfigs);
overrideClusterConfigs.add(overrideClusterConfig);
}
} }
...@@ -16,11 +16,11 @@ application_module.config(['$stateProvider', ...@@ -16,11 +16,11 @@ application_module.config(['$stateProvider',
templateUrl: '../../views/app/config.html', templateUrl: '../../views/app/config.html',
controller: 'AppConfigController' controller: 'AppConfigController'
}).state('info', { }).state('info', {
templateUrl: '../../views/app/info.html', templateUrl: '../../views/app/info.html',
controller: 'AppInfoController' controller: 'AppInfoController'
}).state('setting', { }).state('setting', {
templateUrl: '../../views/app/setting.html' templateUrl: '../../views/app/setting.html'
}); });
}]).run(function ($state) { }]).run(function ($state) {
$state.go('config'); $state.go('config');
}); });
......
create_app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService', create_app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService',
function ($scope, $window, toastr, AppService) { function ($scope, $window, toastr, AppService) {
//todo 便于测试,后续删掉 //todo 便于测试,后续删掉
$scope.app = { $scope.app = {
appId: 1001, appId: 1001,
name: 'lepdou', name: 'lepdou',
ownerPhone: '1111', ownerPhone: '1111',
ownerMail: 'qqq@qq.com', ownerMail: 'qqq@qq.com',
owner: 'le' owner: 'le'
}; };
$scope.save = function(){ $scope.save = function () {
AppService.add($scope.app).then(function(result){ AppService.add($scope.app).then(function (result) {
toastr.success('添加成功!'); toastr.success('添加成功!');
setInterval(function(){ setInterval(function () {
$window.location.href = '/views/app/index.html?#appid=' + result.appId; $window.location.href = '/views/app/index.html?#appid=' + result.appId;
},1000); }, 1000);
},function(result){ }, function (result) {
toastr.error('添加失败!'); toastr.error('添加失败!');
}); });
}; };
}]); }]);
...@@ -4,7 +4,7 @@ application_module.controller("AppPageController", ['$rootScope', '$location', ...@@ -4,7 +4,7 @@ application_module.controller("AppPageController", ['$rootScope', '$location',
$rootScope.appId = $location.$$url.split("=")[1]; $rootScope.appId = $location.$$url.split("=")[1];
if(!$rootScope.appId){ if (!$rootScope.appId) {
$rootScope.appId = 6666; $rootScope.appId = 6666;
} }
......
...@@ -4,7 +4,7 @@ body { ...@@ -4,7 +4,7 @@ body {
padding: 0px !important; padding: 0px !important;
margin: 0px !important; margin: 0px !important;
font-size: 13px; font-size: 13px;
padding-bottom:50px; padding-bottom: 50px;
} }
.container { .container {
......
...@@ -2,8 +2,64 @@ ...@@ -2,8 +2,64 @@
AngularJS v1.4.3 AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org (c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(p,g,l){'use strict';function m(b,a,f){var c=f.baseHref(),k=b[0];return function(b,d,e){var f,h;e=e||{};h=e.expires;f=g.isDefined(e.path)?e.path:c;d===l&&(h="Thu, 01 Jan 1970 00:00:00 GMT",d="");g.isString(h)&&(h=new Date(h));d=encodeURIComponent(b)+"="+encodeURIComponent(d);d=d+(f?";path="+f:"")+(e.domain?";domain="+e.domain:"");d+=h?";expires="+h.toUTCString():"";d+=e.secure?";secure":"";e=d.length+1;4096<e&&a.warn("Cookie '"+b+"' possibly not set or overflowed because it was too large ("+ (function (p, g, l) {
e+" > 4096 bytes)!");k.cookie=d}}g.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,f){return{get:function(c){return a()[c]},getObject:function(c){return(c=this.get(c))?g.fromJson(c):c},getAll:function(){return a()},put:function(c,a,n){f(c,a,n?g.extend({},b,n):b)},putObject:function(c,b,a){this.put(c,g.toJson(b),a)},remove:function(a,k){f(a,l,k?g.extend({},b,k):b)}}}]}]);g.module("ngCookies").factory("$cookieStore", 'use strict';
["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,f){b.putObject(a,f)},remove:function(a){b.remove(a)}}}]);m.$inject=["$document","$log","$browser"];g.module("ngCookies").provider("$$cookieWriter",function(){this.$get=m})})(window,window.angular); function m(b, a, f) {
var c = f.baseHref(), k = b[0];
return function (b, d, e) {
var f, h;
e = e || {};
h = e.expires;
f = g.isDefined(e.path) ? e.path : c;
d === l && (h = "Thu, 01 Jan 1970 00:00:00 GMT", d = "");
g.isString(h) && (h = new Date(h));
d = encodeURIComponent(b) + "=" + encodeURIComponent(d);
d = d + (f ? ";path=" + f : "") + (e.domain ? ";domain=" + e.domain : "");
d += h ? ";expires=" + h.toUTCString() : "";
d += e.secure ? ";secure" : "";
e = d.length + 1;
4096 < e && a.warn("Cookie '" + b + "' possibly not set or overflowed because it was too large (" +
e + " > 4096 bytes)!");
k.cookie = d
}
}
g.module("ngCookies", ["ng"]).provider("$cookies", [function () {
var b = this.defaults = {};
this.$get = ["$$cookieReader", "$$cookieWriter", function (a, f) {
return {
get: function (c) {
return a()[c]
}, getObject: function (c) {
return (c = this.get(c)) ? g.fromJson(c) : c
}, getAll: function () {
return a()
}, put: function (c, a, n) {
f(c, a, n ? g.extend({}, b, n) : b)
}, putObject: function (c, b, a) {
this.put(c, g.toJson(b), a)
}, remove: function (a, k) {
f(a, l, k ? g.extend({}, b, k) : b)
}
}
}]
}]);
g.module("ngCookies").factory("$cookieStore",
["$cookies", function (b) {
return {
get: function (a) {
return b.getObject(a)
}, put: function (a, f) {
b.putObject(a, f)
}, remove: function (a) {
b.remove(a)
}
}
}]);
m.$inject = ["$document", "$log", "$browser"];
g.module("ngCookies").provider("$$cookieWriter", function () {
this.$get = m
})
})(window, window.angular);
//# sourceMappingURL=angular-cookies.min.js.map //# sourceMappingURL=angular-cookies.min.js.map
...@@ -2,12 +2,180 @@ ...@@ -2,12 +2,180 @@
AngularJS v1.4.3 AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org (c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var x=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}}; (function (I, d, B) {
this.$get=["$http","$q",function(q,h){function u(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function w(y,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){v(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw x("badmember",e);for(var e=e.split("."),n=0,g=e.length;n<g&&d!==B;n++){var h=e[n];d=null!==d?d[h]:B}}else d=a;c[k]=d});return c}function F(b){return b.resource}function e(b){D(b|| 'use strict';
{},this)}var G=new u(y,m);l=s({},f.defaults.actions,l);e.prototype.toJSON=function(){var b=s({},this);delete b.$promise;delete b.$resolved;return b};r(l,function(b,k){var g=/^(POST|PUT|PATCH)$/i.test(b.method);e[k]=function(a,z,m,y){var n={},f,l,A;switch(arguments.length){case 4:A=y,l=m;case 3:case 2:if(v(z)){if(v(a)){l=a;A=z;break}l=z;A=m}else{n=a;f=z;l=m;break}case 1:v(a)?l=a:g?f=a:n=a;break;case 0:break;default:throw x("badargs",arguments.length);}var u=this instanceof e,p=u?f:b.isArray?[]:new e(f), function D(f, q) {
t={},w=b.interceptor&&b.interceptor.response||F,C=b.interceptor&&b.interceptor.responseError||B;r(b,function(b,a){"params"!=a&&"isArray"!=a&&"interceptor"!=a&&(t[a]=H(b))});g&&(t.data=f);G.setUrlParams(t,s({},c(f,b.params||{}),n),b.url);n=q(t).then(function(a){var c=a.data,g=p.$promise;if(c){if(d.isArray(c)!==!!b.isArray)throw x("badcfg",k,b.isArray?"array":"object",d.isArray(c)?"array":"object",t.method,t.url);b.isArray?(p.length=0,r(c,function(a){"object"===typeof a?p.push(new e(a)):p.push(a)})): q = q || {};
(D(c,p),p.$promise=g)}p.$resolved=!0;a.resource=p;return a},function(a){p.$resolved=!0;(A||E)(a);return h.reject(a)});n=n.then(function(a){var b=w(a);(l||E)(b,a.headers);return b},C);return u?n:(p.$promise=n,p.$resolved=!1,p)};e.prototype["$"+k]=function(a,b,c){v(a)&&(c=b,b=a,a={});a=e[k].call(this,a,this,b,c);return a.$promise||a}});e.bind=function(b){return w(y,s({},g,b),l)};return e}var E=d.noop,r=d.forEach,s=d.extend,H=d.copy,v=d.isFunction;u.prototype={setUrlParams:function(f,g,l){var m=this, d.forEach(q, function (d, h) {
c=l||m.template,h,e,q=m.urlParams={};r(c.split(/\W/),function(b){if("hasOwnProperty"===b)throw x("badname");!/^\d+$/.test(b)&&b&&(new RegExp("(^|[^\\\\]):"+b+"(\\W|$)")).test(c)&&(q[b]=!0)});c=c.replace(/\\:/g,":");g=g||{};r(m.urlParams,function(b,k){h=g.hasOwnProperty(k)?g[k]:m.defaults[k];d.isDefined(h)&&null!==h?(e=encodeURIComponent(h).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi, delete q[h]
"+"),c=c.replace(new RegExp(":"+k+"(\\W|$)","g"),function(b,a){return e+a})):c=c.replace(new RegExp("(/?):"+k+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");f.url=c.replace(/\/\\\./,"/.");r(g,function(b,c){m.urlParams[c]||(f.params=f.params||{},f.params[c]=b)})}};return w}]})})(window,window.angular); });
for (var h in f)!f.hasOwnProperty(h) || "$" === h.charAt(0) && "$" === h.charAt(1) || (q[h] = f[h]);
return q
}
var x = d.$$minErr("$resource"), C = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
d.module("ngResource", ["ng"]).provider("$resource", function () {
var f = this;
this.defaults = {
stripTrailingSlashes: !0,
actions: {
get: {method: "GET"},
save: {method: "POST"},
query: {method: "GET", isArray: !0},
remove: {method: "DELETE"},
"delete": {method: "DELETE"}
}
};
this.$get = ["$http", "$q", function (q, h) {
function u(d, g) {
this.template = d;
this.defaults = s({}, f.defaults, g);
this.urlParams = {}
}
function w(y, g, l, m) {
function c(b, k) {
var c = {};
k = s({}, g, k);
r(k, function (a, k) {
v(a) && (a = a());
var d;
if (a && a.charAt && "@" == a.charAt(0)) {
d = b;
var e = a.substr(1);
if (null == e || "" === e || "hasOwnProperty" === e || !C.test("." + e))throw x("badmember", e);
for (var e = e.split("."), n = 0, g = e.length; n < g && d !== B; n++) {
var h = e[n];
d = null !== d ? d[h] : B
}
} else d = a;
c[k] = d
});
return c
}
function F(b) {
return b.resource
}
function e(b) {
D(b ||
{}, this)
}
var G = new u(y, m);
l = s({}, f.defaults.actions, l);
e.prototype.toJSON = function () {
var b = s({}, this);
delete b.$promise;
delete b.$resolved;
return b
};
r(l, function (b, k) {
var g = /^(POST|PUT|PATCH)$/i.test(b.method);
e[k] = function (a, z, m, y) {
var n = {}, f, l, A;
switch (arguments.length) {
case 4:
A = y, l = m;
case 3:
case 2:
if (v(z)) {
if (v(a)) {
l = a;
A = z;
break
}
l = z;
A = m
} else {
n = a;
f = z;
l = m;
break
}
case 1:
v(a) ? l = a : g ? f = a : n = a;
break;
case 0:
break;
default:
throw x("badargs", arguments.length);
}
var u = this instanceof e, p = u ? f : b.isArray ? [] : new e(f),
t = {}, w = b.interceptor && b.interceptor.response || F, C = b.interceptor && b.interceptor.responseError || B;
r(b, function (b, a) {
"params" != a && "isArray" != a && "interceptor" != a && (t[a] = H(b))
});
g && (t.data = f);
G.setUrlParams(t, s({}, c(f, b.params || {}), n), b.url);
n = q(t).then(function (a) {
var c = a.data, g = p.$promise;
if (c) {
if (d.isArray(c) !== !!b.isArray)throw x("badcfg", k, b.isArray ? "array" : "object", d.isArray(c) ? "array" : "object", t.method, t.url);
b.isArray ? (p.length = 0, r(c, function (a) {
"object" === typeof a ? p.push(new e(a)) : p.push(a)
})) :
(D(c, p), p.$promise = g)
}
p.$resolved = !0;
a.resource = p;
return a
}, function (a) {
p.$resolved = !0;
(A || E)(a);
return h.reject(a)
});
n = n.then(function (a) {
var b = w(a);
(l || E)(b, a.headers);
return b
}, C);
return u ? n : (p.$promise = n, p.$resolved = !1, p)
};
e.prototype["$" + k] = function (a, b, c) {
v(a) && (c = b, b = a, a = {});
a = e[k].call(this, a, this, b, c);
return a.$promise || a
}
});
e.bind = function (b) {
return w(y, s({}, g, b), l)
};
return e
}
var E = d.noop, r = d.forEach, s = d.extend, H = d.copy, v = d.isFunction;
u.prototype = {
setUrlParams: function (f, g, l) {
var m = this,
c = l || m.template, h, e, q = m.urlParams = {};
r(c.split(/\W/), function (b) {
if ("hasOwnProperty" === b)throw x("badname");
!/^\d+$/.test(b) && b && (new RegExp("(^|[^\\\\]):" + b + "(\\W|$)")).test(c) && (q[b] = !0)
});
c = c.replace(/\\:/g, ":");
g = g || {};
r(m.urlParams, function (b, k) {
h = g.hasOwnProperty(k) ? g[k] : m.defaults[k];
d.isDefined(h) && null !== h ? (e = encodeURIComponent(h).replace(/%40/gi, "@").replace(/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(/%20/g, "%20").replace(/%26/gi, "&").replace(/%3D/gi, "=").replace(/%2B/gi,
"+"), c = c.replace(new RegExp(":" + k + "(\\W|$)", "g"), function (b, a) {
return e + a
})) : c = c.replace(new RegExp("(/?):" + k + "(\\W|$)", "g"), function (b, a, c) {
return "/" == c.charAt(0) ? c : a + c
})
});
m.defaults.stripTrailingSlashes && (c = c.replace(/\/+$/, "") || "/");
c = c.replace(/\/\.(?=\w+($|\?))/, ".");
f.url = c.replace(/\/\\\./, "/.");
r(g, function (b, c) {
m.urlParams[c] || (f.params = f.params || {}, f.params[c] = b)
})
}
};
return w
}]
})
})(window, window.angular);
//# sourceMappingURL=angular-resource.min.js.map //# sourceMappingURL=angular-resource.min.js.map
...@@ -2,14 +2,180 @@ ...@@ -2,14 +2,180 @@
AngularJS v1.4.3 AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org (c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(p,c,C){'use strict';function v(r,h,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,d,y){function z(){k&&(g.cancel(k),k=null);l&&(l.$destroy(),l=null);m&&(k=g.leave(m),k.then(function(){k=null}),m=null)}function x(){var b=r.current&&r.current.locals;if(c.isDefined(b&&b.$template)){var b=a.$new(),d=r.current;m=y(b,function(b){g.enter(b,null,m||f).then(function(){!c.isDefined(t)||t&&!a.$eval(t)||h()});z()});l=d.scope=b;l.$emit("$viewContentLoaded"); (function (p, c, C) {
l.$eval(w)}else z()}var l,m,k,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(c,h,g){return{restrict:"ECA",priority:-400,link:function(a,f){var b=g.current,d=b.locals;f.html(d.$template);var y=c(f.contents());b.controller&&(d.$scope=a,d=h(b.controller,d),b.controllerAs&&(a[b.controllerAs]=d),f.data("$ngControllerController",d),f.children().data("$ngControllerController",d));y(a)}}}p=c.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return c.extend(Object.create(a), 'use strict';
f)}function h(a,c){var b=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,c,b,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:b,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=new RegExp("^"+a+"$",b?"i":"");return d}var g={};this.when=function(a,f){var b=c.copy(f);c.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0); function v(r, h, g) {
c.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);g[a]=c.extend(b,a&&h(a,b));if(a){var d="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[d]=c.extend({redirectTo:a},h(d,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,d,h,p,x){function l(b){var e=s.current; return {
(v=(n=k())&&e&&n.$$route===e.$$route&&c.equals(n.pathParams,e.pathParams)&&!n.reloadOnSearch&&!w)||!e&&!n||a.$broadcast("$routeChangeStart",n,e).defaultPrevented&&b&&b.preventDefault()}function m(){var u=s.current,e=n;if(v)u.params=e.params,c.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(c.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),d.when(e).then(function(){if(e){var a= restrict: "ECA", terminal: !0, priority: 400, transclude: "element", link: function (a, f, b, d, y) {
c.extend({},e.resolve),b,f;c.forEach(a,function(b,e){a[e]=c.isString(b)?h.get(b):h.invoke(b,null,null,e)});c.isDefined(b=e.template)?c.isFunction(b)&&(b=b(e.params)):c.isDefined(f=e.templateUrl)&&(c.isFunction(f)&&(f=f(e.params)),c.isDefined(f)&&(e.loadedTemplateUrl=x.valueOf(f),b=p(f)));c.isDefined(b)&&(a.$template=b);return d.all(a)}}).then(function(f){e==s.current&&(e&&(e.locals=f,c.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError", function z() {
e,u,b)})}function k(){var a,b;c.forEach(g,function(d,g){var q;if(q=!b){var h=f.path();q=d.keys;var l={};if(d.regexp)if(h=d.regexp.exec(h)){for(var k=1,m=h.length;k<m;++k){var n=q[k-1],p=h[k];n&&p&&(l[n.name]=p)}q=l}else q=null;else q=null;q=a=q}q&&(b=r(d,{params:c.extend({},f.search(),a),pathParams:a}),b.$$route=d)});return b||g[null]&&r(g[null],{params:{},pathParams:{}})}function t(a,b){var d=[];c.forEach((a||"").split(":"),function(a,c){if(0===c)d.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/), k && (g.cancel(k), k = null);
g=f[1];d.push(b[g]);d.push(f[2]||"");delete b[g]}});return d.join("")}var w=!1,n,v,s={routes:g,reload:function(){w=!0;a.$evalAsync(function(){l();m()})},updateParams:function(a){if(this.current&&this.current.$$route)a=c.extend({},this.current.params,a),f.path(t(this.current.$$route.originalPath,a)),f.search(a);else throw B("norout");}};a.$on("$locationChangeStart",l);a.$on("$locationChangeSuccess",m);return s}]});var B=c.$$minErr("ngRoute");p.provider("$routeParams",function(){this.$get=function(){return{}}}); l && (l.$destroy(), l = null);
p.directive("ngView",v);p.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular); m && (k = g.leave(m), k.then(function () {
k = null
}), m = null)
}
function x() {
var b = r.current && r.current.locals;
if (c.isDefined(b && b.$template)) {
var b = a.$new(), d = r.current;
m = y(b, function (b) {
g.enter(b, null, m || f).then(function () {
!c.isDefined(t) || t && !a.$eval(t) || h()
});
z()
});
l = d.scope = b;
l.$emit("$viewContentLoaded");
l.$eval(w)
} else z()
}
var l, m, k, t = b.autoscroll, w = b.onload || "";
a.$on("$routeChangeSuccess", x);
x()
}
}
}
function A(c, h, g) {
return {
restrict: "ECA", priority: -400, link: function (a, f) {
var b = g.current, d = b.locals;
f.html(d.$template);
var y = c(f.contents());
b.controller && (d.$scope = a, d = h(b.controller, d), b.controllerAs && (a[b.controllerAs] = d), f.data("$ngControllerController", d), f.children().data("$ngControllerController", d));
y(a)
}
}
}
p = c.module("ngRoute", ["ng"]).provider("$route", function () {
function r(a, f) {
return c.extend(Object.create(a),
f)
}
function h(a, c) {
var b = c.caseInsensitiveMatch, d = {originalPath: a, regexp: a}, g = d.keys = [];
a = a.replace(/([().])/g, "\\$1").replace(/(\/)?:(\w+)([\?\*])?/g, function (a, c, b, d) {
a = "?" === d ? d : null;
d = "*" === d ? d : null;
g.push({name: b, optional: !!a});
c = c || "";
return "" + (a ? "" : c) + "(?:" + (a ? c : "") + (d && "(.+?)" || "([^/]+)") + (a || "") + ")" + (a || "")
}).replace(/([\/$\*])/g, "\\$1");
d.regexp = new RegExp("^" + a + "$", b ? "i" : "");
return d
}
var g = {};
this.when = function (a, f) {
var b = c.copy(f);
c.isUndefined(b.reloadOnSearch) && (b.reloadOnSearch = !0);
c.isUndefined(b.caseInsensitiveMatch) && (b.caseInsensitiveMatch = this.caseInsensitiveMatch);
g[a] = c.extend(b, a && h(a, b));
if (a) {
var d = "/" == a[a.length - 1] ? a.substr(0, a.length - 1) : a + "/";
g[d] = c.extend({redirectTo: a}, h(d, b))
}
return this
};
this.caseInsensitiveMatch = !1;
this.otherwise = function (a) {
"string" === typeof a && (a = {redirectTo: a});
this.when(null, a);
return this
};
this.$get = ["$rootScope", "$location", "$routeParams", "$q", "$injector", "$templateRequest", "$sce", function (a, f, b, d, h, p, x) {
function l(b) {
var e = s.current;
(v = (n = k()) && e && n.$$route === e.$$route && c.equals(n.pathParams, e.pathParams) && !n.reloadOnSearch && !w) || !e && !n || a.$broadcast("$routeChangeStart", n, e).defaultPrevented && b && b.preventDefault()
}
function m() {
var u = s.current, e = n;
if (v)u.params = e.params, c.copy(u.params, b), a.$broadcast("$routeUpdate", u); else if (e || u)w = !1, (s.current = e) && e.redirectTo && (c.isString(e.redirectTo) ? f.path(t(e.redirectTo, e.params)).search(e.params).replace() : f.url(e.redirectTo(e.pathParams, f.path(), f.search())).replace()), d.when(e).then(function () {
if (e) {
var a =
c.extend({}, e.resolve), b, f;
c.forEach(a, function (b, e) {
a[e] = c.isString(b) ? h.get(b) : h.invoke(b, null, null, e)
});
c.isDefined(b = e.template) ? c.isFunction(b) && (b = b(e.params)) : c.isDefined(f = e.templateUrl) && (c.isFunction(f) && (f = f(e.params)), c.isDefined(f) && (e.loadedTemplateUrl = x.valueOf(f), b = p(f)));
c.isDefined(b) && (a.$template = b);
return d.all(a)
}
}).then(function (f) {
e == s.current && (e && (e.locals = f, c.copy(e.params, b)), a.$broadcast("$routeChangeSuccess", e, u))
}, function (b) {
e == s.current && a.$broadcast("$routeChangeError",
e, u, b)
})
}
function k() {
var a, b;
c.forEach(g, function (d, g) {
var q;
if (q = !b) {
var h = f.path();
q = d.keys;
var l = {};
if (d.regexp)if (h = d.regexp.exec(h)) {
for (var k = 1, m = h.length; k < m; ++k) {
var n = q[k - 1], p = h[k];
n && p && (l[n.name] = p)
}
q = l
} else q = null; else q = null;
q = a = q
}
q && (b = r(d, {params: c.extend({}, f.search(), a), pathParams: a}), b.$$route = d)
});
return b || g[null] && r(g[null], {params: {}, pathParams: {}})
}
function t(a, b) {
var d = [];
c.forEach((a || "").split(":"), function (a, c) {
if (0 === c)d.push(a); else {
var f = a.match(/(\w+)(?:[?*])?(.*)/),
g = f[1];
d.push(b[g]);
d.push(f[2] || "");
delete b[g]
}
});
return d.join("")
}
var w = !1, n, v, s = {
routes: g, reload: function () {
w = !0;
a.$evalAsync(function () {
l();
m()
})
}, updateParams: function (a) {
if (this.current && this.current.$$route)a = c.extend({}, this.current.params, a), f.path(t(this.current.$$route.originalPath, a)), f.search(a); else throw B("norout");
}
};
a.$on("$locationChangeStart", l);
a.$on("$locationChangeSuccess", m);
return s
}]
});
var B = c.$$minErr("ngRoute");
p.provider("$routeParams", function () {
this.$get = function () {
return {}
}
});
p.directive("ngView", v);
p.directive("ngView", A);
v.$inject = ["$route", "$anchorScroll", "$animate"];
A.$inject = ["$compile", "$controller", "$route"]
})(window, window.angular);
//# sourceMappingURL=angular-route.min.js.map //# sourceMappingURL=angular-route.min.js.map
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -5,4 +5,29 @@ ...@@ -5,4 +5,29 @@
* @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea) * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
* @license MIT License, http://www.opensource.org/licenses/MIT * @license MIT License, http://www.opensource.org/licenses/MIT
*/ */
!function(t,e,n){'use strict';angular.module('mgcrea.ngStrap.alert').run(['$templateCache',function(t){t.put('alert/alert.tpl.html','<div class="alert" ng-class="[type ? \'alert-\' + type : null]"><button type="button" class="close" ng-if="dismissable" ng-click="$hide()">&times;</button> <strong ng-bind="title"></strong>&nbsp;<span ng-bind-html="content"></span></div>')}]),angular.module('mgcrea.ngStrap.aside').run(['$templateCache',function(t){t.put('aside/aside.tpl.html','<div class="aside" tabindex="-1" role="dialog"><div class="aside-dialog"><div class="aside-content"><div class="aside-header" ng-show="title"><button type="button" class="close" ng-click="$hide()">&times;</button><h4 class="aside-title" ng-bind="title"></h4></div><div class="aside-body" ng-bind="content"></div><div class="aside-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>')}]),angular.module('mgcrea.ngStrap.datepicker').run(['$templateCache',function(t){t.put('datepicker/datepicker.tpl.html','<div class="dropdown-menu datepicker" ng-class="\'datepicker-mode-\' + $mode" style="max-width: 320px"><table style="table-layout: fixed; height: 100%; width: 100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$selectPane(-1)"><i class="{{$iconLeft}}"></i></button></th><th colspan="{{ rows[0].length - 2 }}"><button tabindex="-1" type="button" class="btn btn-default btn-block text-strong" ng-click="$toggleMode()"><strong style="text-transform: capitalize" ng-bind="title"></strong></button></th><th><button tabindex="-1" type="button" class="btn btn-default pull-right" ng-click="$selectPane(+1)"><i class="{{$iconRight}}"></i></button></th></tr><tr ng-show="showLabels" ng-bind-html="labels"></tr></thead><tbody><tr ng-repeat="(i, row) in rows" height="{{ 100 / rows.length }}%"><td class="text-center" ng-repeat="(j, el) in row"><button tabindex="-1" type="button" class="btn btn-default" style="width: 100%" ng-class="{\'btn-primary\': el.selected, \'btn-info btn-today\': el.isToday && !el.selected}" ng-click="$select(el.date)" ng-disabled="el.disabled"><span ng-class="{\'text-muted\': el.muted}" ng-bind="el.label"></span></button></td></tr></tbody></table></div>')}]),angular.module('mgcrea.ngStrap.dropdown').run(['$templateCache',function(t){t.put('dropdown/dropdown.tpl.html','<ul tabindex="-1" class="dropdown-menu" role="menu"><li role="presentation" ng-class="{divider: item.divider}" ng-repeat="item in content"><a role="menuitem" tabindex="-1" ng-href="{{item.href}}" ng-if="!item.divider && item.href" target="{{item.target || \'\'}}" ng-bind="item.text"></a> <a role="menuitem" tabindex="-1" href="javascript:void(0)" ng-if="!item.divider && item.click" ng-click="$eval(item.click);$hide()" ng-bind="item.text"></a></li></ul>')}]),angular.module('mgcrea.ngStrap.modal').run(['$templateCache',function(t){t.put('modal/modal.tpl.html','<div class="modal" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header" ng-show="title"><button type="button" class="close" aria-label="Close" ng-click="$hide()"><span aria-hidden="true">&times;</span></button><h4 class="modal-title" ng-bind="title"></h4></div><div class="modal-body" ng-bind="content"></div><div class="modal-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>')}]),angular.module('mgcrea.ngStrap.popover').run(['$templateCache',function(t){t.put('popover/popover.tpl.html','<div class="popover"><div class="arrow"></div><h3 class="popover-title" ng-bind="title" ng-show="title"></h3><div class="popover-content" ng-bind="content"></div></div>')}]),angular.module('mgcrea.ngStrap.select').run(['$templateCache',function(t){t.put('select/select.tpl.html','<ul tabindex="-1" class="select dropdown-menu" ng-show="$isVisible()" role="select"><li ng-if="$showAllNoneButtons"><div class="btn-group" style="margin-bottom: 5px; margin-left: 5px"><button type="button" class="btn btn-default btn-xs" ng-click="$selectAll()">{{$allText}}</button> <button type="button" class="btn btn-default btn-xs" ng-click="$selectNone()">{{$noneText}}</button></div></li><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $isActive($index)}"><a style="cursor: default" role="menuitem" tabindex="-1" ng-click="$select($index, $event)"><i class="{{$iconCheckmark}} pull-right" ng-if="$isMultiple && $isActive($index)"></i> <span ng-bind="match.label"></span></a></li></ul>')}]),angular.module('mgcrea.ngStrap.timepicker').run(['$templateCache',function(t){t.put('timepicker/timepicker.tpl.html','<div class="dropdown-menu timepicker" style="min-width: 0px;width: auto"><table height="100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 0)"><i class="{{ $iconUp }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 1)"><i class="{{ $iconUp }}"></i></button></th><th>&nbsp;</th><th><button ng-if="showSeconds" tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 2)"><i class="{{ $iconUp }}"></i></button></th></tr></thead><tbody><tr ng-repeat="(i, row) in rows"><td class="text-center"><button tabindex="-1" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[0].selected}" ng-click="$select(row[0].date, 0)" ng-disabled="row[0].disabled"><span ng-class="{\'text-muted\': row[0].muted}" ng-bind="row[0].label"></span></button></td><td><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td class="text-center"><button tabindex="-1" ng-if="row[1].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[1].selected}" ng-click="$select(row[1].date, 1)" ng-disabled="row[1].disabled"><span ng-class="{\'text-muted\': row[1].muted}" ng-bind="row[1].label"></span></button></td><td><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td class="text-center"><button tabindex="-1" ng-if="showSeconds && row[2].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[2].selected}" ng-click="$select(row[2].date, 2)" ng-disabled="row[2].disabled"><span ng-class="{\'text-muted\': row[2].muted}" ng-bind="row[2].label"></span></button></td><td ng-if="showAM">&nbsp;</td><td ng-if="showAM"><button tabindex="-1" ng-show="i == midIndex - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !!isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">AM</button> <button tabindex="-1" ng-show="i == midIndex + 1 - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">PM</button></td></tr></tbody><tfoot><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 0)"><i class="{{ $iconDown }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 1)"><i class="{{ $iconDown }}"></i></button></th><th>&nbsp;</th><th><button ng-if="showSeconds" tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 2)"><i class="{{ $iconDown }}"></i></button></th></tr></tfoot></table></div>')}]),angular.module('mgcrea.ngStrap.tab').run(['$templateCache',function(t){t.put('tab/tab.tpl.html','<ul class="nav" ng-class="$navClass" role="tablist"><li role="presentation" ng-repeat="$pane in $panes track by $index" ng-class="[ $index == $panes.$active ? $activeClass : \'\', $pane.disabled ? \'disabled\' : \'\' ]"><a role="tab" data-toggle="tab" ng-click="!$pane.disabled && $setActive($index)" data-index="{{ $index }}" ng-bind-html="$pane.title" aria-controls="$pane.title"></a></li></ul><div ng-transclude class="tab-content"></div>')}]),angular.module('mgcrea.ngStrap.tooltip').run(['$templateCache',function(t){t.put('tooltip/tooltip.tpl.html','<div class="tooltip in" ng-show="title"><div class="tooltip-arrow"></div><div class="tooltip-inner" ng-bind="title"></div></div>')}]),angular.module('mgcrea.ngStrap.typeahead').run(['$templateCache',function(t){t.put('typeahead/typeahead.tpl.html','<ul tabindex="-1" class="typeahead dropdown-menu" ng-show="$isVisible()" role="select"><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $index == $activeIndex}"><a role="menuitem" tabindex="-1" ng-click="$select($index, $event)" ng-bind="match.label"></a></li></ul>')}])}(window,document); !function (t, e, n) {
\ No newline at end of file 'use strict';
angular.module('mgcrea.ngStrap.alert').run(['$templateCache', function (t) {
t.put('alert/alert.tpl.html', '<div class="alert" ng-class="[type ? \'alert-\' + type : null]"><button type="button" class="close" ng-if="dismissable" ng-click="$hide()">&times;</button> <strong ng-bind="title"></strong>&nbsp;<span ng-bind-html="content"></span></div>')
}]), angular.module('mgcrea.ngStrap.aside').run(['$templateCache', function (t) {
t.put('aside/aside.tpl.html', '<div class="aside" tabindex="-1" role="dialog"><div class="aside-dialog"><div class="aside-content"><div class="aside-header" ng-show="title"><button type="button" class="close" ng-click="$hide()">&times;</button><h4 class="aside-title" ng-bind="title"></h4></div><div class="aside-body" ng-bind="content"></div><div class="aside-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>')
}]), angular.module('mgcrea.ngStrap.datepicker').run(['$templateCache', function (t) {
t.put('datepicker/datepicker.tpl.html', '<div class="dropdown-menu datepicker" ng-class="\'datepicker-mode-\' + $mode" style="max-width: 320px"><table style="table-layout: fixed; height: 100%; width: 100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$selectPane(-1)"><i class="{{$iconLeft}}"></i></button></th><th colspan="{{ rows[0].length - 2 }}"><button tabindex="-1" type="button" class="btn btn-default btn-block text-strong" ng-click="$toggleMode()"><strong style="text-transform: capitalize" ng-bind="title"></strong></button></th><th><button tabindex="-1" type="button" class="btn btn-default pull-right" ng-click="$selectPane(+1)"><i class="{{$iconRight}}"></i></button></th></tr><tr ng-show="showLabels" ng-bind-html="labels"></tr></thead><tbody><tr ng-repeat="(i, row) in rows" height="{{ 100 / rows.length }}%"><td class="text-center" ng-repeat="(j, el) in row"><button tabindex="-1" type="button" class="btn btn-default" style="width: 100%" ng-class="{\'btn-primary\': el.selected, \'btn-info btn-today\': el.isToday && !el.selected}" ng-click="$select(el.date)" ng-disabled="el.disabled"><span ng-class="{\'text-muted\': el.muted}" ng-bind="el.label"></span></button></td></tr></tbody></table></div>')
}]), angular.module('mgcrea.ngStrap.dropdown').run(['$templateCache', function (t) {
t.put('dropdown/dropdown.tpl.html', '<ul tabindex="-1" class="dropdown-menu" role="menu"><li role="presentation" ng-class="{divider: item.divider}" ng-repeat="item in content"><a role="menuitem" tabindex="-1" ng-href="{{item.href}}" ng-if="!item.divider && item.href" target="{{item.target || \'\'}}" ng-bind="item.text"></a> <a role="menuitem" tabindex="-1" href="javascript:void(0)" ng-if="!item.divider && item.click" ng-click="$eval(item.click);$hide()" ng-bind="item.text"></a></li></ul>')
}]), angular.module('mgcrea.ngStrap.modal').run(['$templateCache', function (t) {
t.put('modal/modal.tpl.html', '<div class="modal" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header" ng-show="title"><button type="button" class="close" aria-label="Close" ng-click="$hide()"><span aria-hidden="true">&times;</span></button><h4 class="modal-title" ng-bind="title"></h4></div><div class="modal-body" ng-bind="content"></div><div class="modal-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>')
}]), angular.module('mgcrea.ngStrap.popover').run(['$templateCache', function (t) {
t.put('popover/popover.tpl.html', '<div class="popover"><div class="arrow"></div><h3 class="popover-title" ng-bind="title" ng-show="title"></h3><div class="popover-content" ng-bind="content"></div></div>')
}]), angular.module('mgcrea.ngStrap.select').run(['$templateCache', function (t) {
t.put('select/select.tpl.html', '<ul tabindex="-1" class="select dropdown-menu" ng-show="$isVisible()" role="select"><li ng-if="$showAllNoneButtons"><div class="btn-group" style="margin-bottom: 5px; margin-left: 5px"><button type="button" class="btn btn-default btn-xs" ng-click="$selectAll()">{{$allText}}</button> <button type="button" class="btn btn-default btn-xs" ng-click="$selectNone()">{{$noneText}}</button></div></li><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $isActive($index)}"><a style="cursor: default" role="menuitem" tabindex="-1" ng-click="$select($index, $event)"><i class="{{$iconCheckmark}} pull-right" ng-if="$isMultiple && $isActive($index)"></i> <span ng-bind="match.label"></span></a></li></ul>')
}]), angular.module('mgcrea.ngStrap.timepicker').run(['$templateCache', function (t) {
t.put('timepicker/timepicker.tpl.html', '<div class="dropdown-menu timepicker" style="min-width: 0px;width: auto"><table height="100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 0)"><i class="{{ $iconUp }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 1)"><i class="{{ $iconUp }}"></i></button></th><th>&nbsp;</th><th><button ng-if="showSeconds" tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 2)"><i class="{{ $iconUp }}"></i></button></th></tr></thead><tbody><tr ng-repeat="(i, row) in rows"><td class="text-center"><button tabindex="-1" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[0].selected}" ng-click="$select(row[0].date, 0)" ng-disabled="row[0].disabled"><span ng-class="{\'text-muted\': row[0].muted}" ng-bind="row[0].label"></span></button></td><td><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td class="text-center"><button tabindex="-1" ng-if="row[1].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[1].selected}" ng-click="$select(row[1].date, 1)" ng-disabled="row[1].disabled"><span ng-class="{\'text-muted\': row[1].muted}" ng-bind="row[1].label"></span></button></td><td><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td class="text-center"><button tabindex="-1" ng-if="showSeconds && row[2].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[2].selected}" ng-click="$select(row[2].date, 2)" ng-disabled="row[2].disabled"><span ng-class="{\'text-muted\': row[2].muted}" ng-bind="row[2].label"></span></button></td><td ng-if="showAM">&nbsp;</td><td ng-if="showAM"><button tabindex="-1" ng-show="i == midIndex - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !!isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">AM</button> <button tabindex="-1" ng-show="i == midIndex + 1 - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">PM</button></td></tr></tbody><tfoot><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 0)"><i class="{{ $iconDown }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 1)"><i class="{{ $iconDown }}"></i></button></th><th>&nbsp;</th><th><button ng-if="showSeconds" tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 2)"><i class="{{ $iconDown }}"></i></button></th></tr></tfoot></table></div>')
}]), angular.module('mgcrea.ngStrap.tab').run(['$templateCache', function (t) {
t.put('tab/tab.tpl.html', '<ul class="nav" ng-class="$navClass" role="tablist"><li role="presentation" ng-repeat="$pane in $panes track by $index" ng-class="[ $index == $panes.$active ? $activeClass : \'\', $pane.disabled ? \'disabled\' : \'\' ]"><a role="tab" data-toggle="tab" ng-click="!$pane.disabled && $setActive($index)" data-index="{{ $index }}" ng-bind-html="$pane.title" aria-controls="$pane.title"></a></li></ul><div ng-transclude class="tab-content"></div>')
}]), angular.module('mgcrea.ngStrap.tooltip').run(['$templateCache', function (t) {
t.put('tooltip/tooltip.tpl.html', '<div class="tooltip in" ng-show="title"><div class="tooltip-arrow"></div><div class="tooltip-inner" ng-bind="title"></div></div>')
}]), angular.module('mgcrea.ngStrap.typeahead').run(['$templateCache', function (t) {
t.put('typeahead/typeahead.tpl.html', '<ul tabindex="-1" class="typeahead dropdown-menu" ng-show="$isVisible()" role="select"><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $index == $activeIndex}"><a role="menuitem" tabindex="-1" ng-click="$select($index, $event)" ng-bind="match.label"></a></li></ul>')
}])
}(window, document);
.toast-title{font-weight:700}.toast-message{word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8}#toast-container>:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4}.toast{opacity:1!important}.toast.ng-enter{opacity:0!important;transition:opacity .3s linear}.toast.ng-enter.ng-enter-active{opacity:1!important}.toast.ng-leave{opacity:1;transition:opacity .3s linear}.toast.ng-leave.ng-leave-active{opacity:0!important}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:241px)and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:481px)and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}} .toast-title {
\ No newline at end of file font-weight: 700
}
.toast-message {
word-wrap: break-word
}
.toast-message a, .toast-message label {
color: #fff
}
.toast-message a:hover {
color: #ccc;
text-decoration: none
}
.toast-close-button {
position: relative;
right: -.3em;
top: -.3em;
float: right;
font-size: 20px;
font-weight: 700;
color: #fff;
-webkit-text-shadow: 0 1px 0 #fff;
text-shadow: 0 1px 0 #fff;
opacity: .8
}
.toast-close-button:focus, .toast-close-button:hover {
color: #000;
text-decoration: none;
cursor: pointer;
opacity: .4
}
button.toast-close-button {
padding: 0;
cursor: pointer;
background: 0 0;
border: 0;
-webkit-appearance: none
}
.toast-top-center {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-left {
top: 12px;
left: 12px
}
.toast-top-right {
top: 12px;
right: 12px
}
.toast-bottom-right {
right: 12px;
bottom: 12px
}
.toast-bottom-left {
bottom: 12px;
left: 12px
}
#toast-container {
position: fixed;
z-index: 999999
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
#toast-container > div {
position: relative;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999;
-webkit-box-shadow: 0 0 12px #999;
box-shadow: 0 0 12px #999;
color: #fff;
opacity: .8
}
#toast-container > :hover {
-moz-box-shadow: 0 0 12px #000;
-webkit-box-shadow: 0 0 12px #000;
box-shadow: 0 0 12px #000;
opacity: 1;
cursor: pointer
}
#toast-container > .toast-info {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-success {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==) !important
}
#toast-container > .toast-warning {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=) !important
}
#toast-container.toast-bottom-center > div, #toast-container.toast-top-center > div {
width: 300px;
margin: auto
}
#toast-container.toast-bottom-full-width > div, #toast-container.toast-top-full-width > div {
width: 96%;
margin: auto
}
.toast {
background-color: #030303
}
.toast-success {
background-color: #51a351
}
.toast-error {
background-color: #bd362f
}
.toast-info {
background-color: #2f96b4
}
.toast-warning {
background-color: #f89406
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000;
opacity: .4
}
.toast {
opacity: 1 !important
}
.toast.ng-enter {
opacity: 0 !important;
transition: opacity .3s linear
}
.toast.ng-enter.ng-enter-active {
opacity: 1 !important
}
.toast.ng-leave {
opacity: 1;
transition: opacity .3s linear
}
.toast.ng-leave.ng-leave-active {
opacity: 0 !important
}
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 241px)and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 481px)and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em
}
}
!function(){"use strict";function t(t,e,s,n,o,r,a){function i(t){if(t)d(t.toastId);else for(var e=0;e<O.length;e++)d(O[e].toastId)}function l(t,e,s){var n=m().iconClasses.error;return g(n,t,e,s)}function c(t,e,s){var n=m().iconClasses.info;return g(n,t,e,s)}function u(t,e,s){var n=m().iconClasses.success;return g(n,t,e,s)}function p(t,e,s){var n=m().iconClasses.warning;return g(n,t,e,s)}function d(e,s){function n(t){for(var e=0;e<O.length;e++)if(O[e].toastId===t)return O[e]}function o(){return!O.length}var i=n(e);i&&!i.deleting&&(i.deleting=!0,i.isOpened=!1,t.leave(i.el).then(function(){i.scope.options.onHidden&&i.scope.options.onHidden(s),i.scope.$destroy();var t=O.indexOf(i);delete B[i.scope.message],O.splice(t,1);var e=r.maxOpened;e&&O.length>=e&&O[e-1].open.resolve(),o()&&(h.remove(),h=null,T=a.defer())}))}function g(t,e,s,n){return angular.isObject(s)&&(n=s,s=null),v({iconClass:t,message:e,optionsOverride:n,title:s})}function m(){return angular.extend({},r)}function f(e){if(h)return T.promise;h=angular.element("<div></div>"),h.attr("id",e.containerId),h.addClass(e.positionClass),h.css({"pointer-events":"auto"});var s=angular.element(document.querySelector(e.target));if(!s||!s.length)throw"Target for toasts doesn't exist";return t.enter(h,s).then(function(){T.resolve()}),T.promise}function v(s){function r(t,e,s){s.allowHtml?(t.scope.allowHtml=!0,t.scope.title=o.trustAsHtml(e.title),t.scope.message=o.trustAsHtml(e.message)):(t.scope.title=e.title,t.scope.message=e.message),t.scope.toastType=t.iconClass,t.scope.toastId=t.toastId,t.scope.options={extendedTimeOut:s.extendedTimeOut,messageClass:s.messageClass,onHidden:s.onHidden,onShown:s.onShown,progressBar:s.progressBar,tapToDismiss:s.tapToDismiss,timeOut:s.timeOut,titleClass:s.titleClass,toastClass:s.toastClass},s.closeButton&&(t.scope.options.closeHtml=s.closeHtml)}function i(){function t(t){for(var e=["containerId","iconClasses","maxOpened","newestOnTop","positionClass","preventDuplicates","preventOpenDuplicates","templates"],s=0,n=e.length;n>s;s++)delete t[e[s]];return t}var e={toastId:C++,isOpened:!1,scope:n.$new(),open:a.defer()};return e.iconClass=s.iconClass,s.optionsOverride&&(p=angular.extend(p,t(s.optionsOverride)),e.iconClass=s.optionsOverride.iconClass||e.iconClass),r(e,s,p),e.el=l(e.scope),e}function l(t){var s=angular.element("<div toast></div>"),n=e.get("$compile");return n(s)(t)}function c(){return p.maxOpened&&O.length<=p.maxOpened||!p.maxOpened}function u(){var t=p.preventDuplicates&&s.message===w,e=p.preventOpenDuplicates&&B[s.message];return t||e?!0:(w=s.message,B[s.message]=!0,!1)}var p=m();if(!u()){var g=i();if(O.push(g),p.autoDismiss&&p.maxOpened>0)for(var v=O.slice(0,O.length-p.maxOpened),T=0,$=v.length;$>T;T++)d(v[T].toastId);return c()&&g.open.resolve(),g.open.promise.then(function(){f(p).then(function(){if(g.isOpened=!0,p.newestOnTop)t.enter(g.el,h).then(function(){g.scope.init()});else{var e=h[0].lastChild?angular.element(h[0].lastChild):null;t.enter(g.el,h,e).then(function(){g.scope.init()})}})}),g}}var h,C=0,O=[],w="",B={},T=a.defer(),$={clear:i,error:l,info:c,remove:d,success:u,warning:p};return $}angular.module("toastr",[]).factory("toastr",t),t.$inject=["$animate","$injector","$document","$rootScope","$sce","toastrConfig","$q"]}(),function(){"use strict";angular.module("toastr").constant("toastrConfig",{allowHtml:!1,autoDismiss:!1,closeButton:!1,closeHtml:"<button>&times;</button>",containerId:"toast-container",extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},maxOpened:0,messageClass:"toast-message",newestOnTop:!0,onHidden:null,onShown:null,positionClass:"toast-top-right",preventDuplicates:!1,preventOpenDuplicates:!1,progressBar:!1,tapToDismiss:!0,target:"body",templates:{toast:"directives/toast/toast.html",progressbar:"directives/progressbar/progressbar.html"},timeOut:5e3,titleClass:"toast-title",toastClass:"toast"})}(),function(){"use strict";function t(t){function e(t,e,s,n){function o(){var t=(i-(new Date).getTime())/a*100;e.css("width",t+"%")}var r,a,i;n.progressBar=t,t.start=function(t){r&&clearInterval(r),a=parseFloat(t),i=(new Date).getTime()+a,r=setInterval(o,10)},t.stop=function(){r&&clearInterval(r)},t.$on("$destroy",function(){clearInterval(r)})}return{replace:!0,require:"^toast",templateUrl:function(){return t.templates.progressbar},link:e}}angular.module("toastr").directive("progressBar",t),t.$inject=["toastrConfig"]}(),function(){"use strict";function t(){this.progressBar=null,this.startProgressBar=function(t){this.progressBar&&this.progressBar.start(t)},this.stopProgressBar=function(){this.progressBar&&this.progressBar.stop()}}angular.module("toastr").controller("ToastController",t)}(),function(){"use strict";function t(t,e,s,n){function o(s,o,r,a){function i(t){return a.startProgressBar(t),e(function(){a.stopProgressBar(),n.remove(s.toastId)},t,1)}function l(){s.progressBar=!1,a.stopProgressBar()}function c(){return s.options.closeHtml}var u;if(s.toastClass=s.options.toastClass,s.titleClass=s.options.titleClass,s.messageClass=s.options.messageClass,s.progressBar=s.options.progressBar,c()){var p=angular.element(s.options.closeHtml),d=t.get("$compile");p.addClass("toast-close-button"),p.attr("ng-click","close()"),d(p)(s),o.prepend(p)}s.init=function(){s.options.timeOut&&(u=i(s.options.timeOut)),s.options.onShown&&s.options.onShown()},o.on("mouseenter",function(){l(),u&&e.cancel(u)}),s.tapToast=function(){s.options.tapToDismiss&&s.close(!0)},s.close=function(t){n.remove(s.toastId,t)},o.on("mouseleave",function(){(0!==s.options.timeOut||0!==s.options.extendedTimeOut)&&(s.$apply(function(){s.progressBar=s.options.progressBar}),u=i(s.options.extendedTimeOut))})}return{replace:!0,templateUrl:function(){return s.templates.toast},controller:"ToastController",link:o}}angular.module("toastr").directive("toast",t),t.$inject=["$injector","$interval","toastrConfig","toastr"]}(),angular.module("toastr").run(["$templateCache",function(t){t.put("directives/progressbar/progressbar.html",'<div class="toast-progress"></div>\n'),t.put("directives/toast/toast.html",'<div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">\n <div ng-switch on="allowHtml">\n <div ng-switch-default ng-if="title" class="{{titleClass}}">{{title}}</div>\n <div ng-switch-default class="{{messageClass}}">{{message}}</div>\n <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>\n <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>\n </div>\n <progress-bar ng-if="progressBar"></progress-bar>\n</div>\n')}]); !function () {
\ No newline at end of file "use strict";
function t(t, e, s, n, o, r, a) {
function i(t) {
if (t)d(t.toastId); else for (var e = 0; e < O.length; e++)d(O[e].toastId)
}
function l(t, e, s) {
var n = m().iconClasses.error;
return g(n, t, e, s)
}
function c(t, e, s) {
var n = m().iconClasses.info;
return g(n, t, e, s)
}
function u(t, e, s) {
var n = m().iconClasses.success;
return g(n, t, e, s)
}
function p(t, e, s) {
var n = m().iconClasses.warning;
return g(n, t, e, s)
}
function d(e, s) {
function n(t) {
for (var e = 0; e < O.length; e++)if (O[e].toastId === t)return O[e]
}
function o() {
return !O.length
}
var i = n(e);
i && !i.deleting && (i.deleting = !0, i.isOpened = !1, t.leave(i.el).then(function () {
i.scope.options.onHidden && i.scope.options.onHidden(s), i.scope.$destroy();
var t = O.indexOf(i);
delete B[i.scope.message], O.splice(t, 1);
var e = r.maxOpened;
e && O.length >= e && O[e - 1].open.resolve(), o() && (h.remove(), h = null, T = a.defer())
}))
}
function g(t, e, s, n) {
return angular.isObject(s) && (n = s, s = null), v({iconClass: t, message: e, optionsOverride: n, title: s})
}
function m() {
return angular.extend({}, r)
}
function f(e) {
if (h)return T.promise;
h = angular.element("<div></div>"), h.attr("id", e.containerId), h.addClass(e.positionClass), h.css({"pointer-events": "auto"});
var s = angular.element(document.querySelector(e.target));
if (!s || !s.length)throw"Target for toasts doesn't exist";
return t.enter(h, s).then(function () {
T.resolve()
}), T.promise
}
function v(s) {
function r(t, e, s) {
s.allowHtml ? (t.scope.allowHtml = !0, t.scope.title = o.trustAsHtml(e.title), t.scope.message = o.trustAsHtml(e.message)) : (t.scope.title = e.title, t.scope.message = e.message), t.scope.toastType = t.iconClass, t.scope.toastId = t.toastId, t.scope.options = {
extendedTimeOut: s.extendedTimeOut,
messageClass: s.messageClass,
onHidden: s.onHidden,
onShown: s.onShown,
progressBar: s.progressBar,
tapToDismiss: s.tapToDismiss,
timeOut: s.timeOut,
titleClass: s.titleClass,
toastClass: s.toastClass
}, s.closeButton && (t.scope.options.closeHtml = s.closeHtml)
}
function i() {
function t(t) {
for (var e = ["containerId", "iconClasses", "maxOpened", "newestOnTop", "positionClass", "preventDuplicates", "preventOpenDuplicates", "templates"], s = 0, n = e.length; n > s; s++)delete t[e[s]];
return t
}
var e = {toastId: C++, isOpened: !1, scope: n.$new(), open: a.defer()};
return e.iconClass = s.iconClass, s.optionsOverride && (p = angular.extend(p, t(s.optionsOverride)), e.iconClass = s.optionsOverride.iconClass || e.iconClass), r(e, s, p), e.el = l(e.scope), e
}
function l(t) {
var s = angular.element("<div toast></div>"), n = e.get("$compile");
return n(s)(t)
}
function c() {
return p.maxOpened && O.length <= p.maxOpened || !p.maxOpened
}
function u() {
var t = p.preventDuplicates && s.message === w, e = p.preventOpenDuplicates && B[s.message];
return t || e ? !0 : (w = s.message, B[s.message] = !0, !1)
}
var p = m();
if (!u()) {
var g = i();
if (O.push(g), p.autoDismiss && p.maxOpened > 0)for (var v = O.slice(0, O.length - p.maxOpened), T = 0, $ = v.length; $ > T; T++)d(v[T].toastId);
return c() && g.open.resolve(), g.open.promise.then(function () {
f(p).then(function () {
if (g.isOpened = !0, p.newestOnTop)t.enter(g.el, h).then(function () {
g.scope.init()
}); else {
var e = h[0].lastChild ? angular.element(h[0].lastChild) : null;
t.enter(g.el, h, e).then(function () {
g.scope.init()
})
}
})
}), g
}
}
var h, C = 0, O = [], w = "", B = {}, T = a.defer(), $ = {
clear: i,
error: l,
info: c,
remove: d,
success: u,
warning: p
};
return $
}
angular.module("toastr", []).factory("toastr", t), t.$inject = ["$animate", "$injector", "$document", "$rootScope", "$sce", "toastrConfig", "$q"]
}(), function () {
"use strict";
angular.module("toastr").constant("toastrConfig", {
allowHtml: !1,
autoDismiss: !1,
closeButton: !1,
closeHtml: "<button>&times;</button>",
containerId: "toast-container",
extendedTimeOut: 1e3,
iconClasses: {error: "toast-error", info: "toast-info", success: "toast-success", warning: "toast-warning"},
maxOpened: 0,
messageClass: "toast-message",
newestOnTop: !0,
onHidden: null,
onShown: null,
positionClass: "toast-top-right",
preventDuplicates: !1,
preventOpenDuplicates: !1,
progressBar: !1,
tapToDismiss: !0,
target: "body",
templates: {toast: "directives/toast/toast.html", progressbar: "directives/progressbar/progressbar.html"},
timeOut: 5e3,
titleClass: "toast-title",
toastClass: "toast"
})
}(), function () {
"use strict";
function t(t) {
function e(t, e, s, n) {
function o() {
var t = (i - (new Date).getTime()) / a * 100;
e.css("width", t + "%")
}
var r, a, i;
n.progressBar = t, t.start = function (t) {
r && clearInterval(r), a = parseFloat(t), i = (new Date).getTime() + a, r = setInterval(o, 10)
}, t.stop = function () {
r && clearInterval(r)
}, t.$on("$destroy", function () {
clearInterval(r)
})
}
return {
replace: !0, require: "^toast", templateUrl: function () {
return t.templates.progressbar
}, link: e
}
}
angular.module("toastr").directive("progressBar", t), t.$inject = ["toastrConfig"]
}(), function () {
"use strict";
function t() {
this.progressBar = null, this.startProgressBar = function (t) {
this.progressBar && this.progressBar.start(t)
}, this.stopProgressBar = function () {
this.progressBar && this.progressBar.stop()
}
}
angular.module("toastr").controller("ToastController", t)
}(), function () {
"use strict";
function t(t, e, s, n) {
function o(s, o, r, a) {
function i(t) {
return a.startProgressBar(t), e(function () {
a.stopProgressBar(), n.remove(s.toastId)
}, t, 1)
}
function l() {
s.progressBar = !1, a.stopProgressBar()
}
function c() {
return s.options.closeHtml
}
var u;
if (s.toastClass = s.options.toastClass, s.titleClass = s.options.titleClass, s.messageClass = s.options.messageClass, s.progressBar = s.options.progressBar, c()) {
var p = angular.element(s.options.closeHtml), d = t.get("$compile");
p.addClass("toast-close-button"), p.attr("ng-click", "close()"), d(p)(s), o.prepend(p)
}
s.init = function () {
s.options.timeOut && (u = i(s.options.timeOut)), s.options.onShown && s.options.onShown()
}, o.on("mouseenter", function () {
l(), u && e.cancel(u)
}), s.tapToast = function () {
s.options.tapToDismiss && s.close(!0)
}, s.close = function (t) {
n.remove(s.toastId, t)
}, o.on("mouseleave", function () {
(0 !== s.options.timeOut || 0 !== s.options.extendedTimeOut) && (s.$apply(function () {
s.progressBar = s.options.progressBar
}), u = i(s.options.extendedTimeOut))
})
}
return {
replace: !0, templateUrl: function () {
return s.templates.toast
}, controller: "ToastController", link: o
}
}
angular.module("toastr").directive("toast", t), t.$inject = ["$injector", "$interval", "toastrConfig", "toastr"]
}(), angular.module("toastr").run(["$templateCache", function (t) {
t.put("directives/progressbar/progressbar.html", '<div class="toast-progress"></div>\n'), t.put("directives/toast/toast.html", '<div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">\n <div ng-switch on="allowHtml">\n <div ng-switch-default ng-if="title" class="{{titleClass}}">{{title}}</div>\n <div ng-switch-default class="{{messageClass}}">{{message}}</div>\n <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>\n <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>\n </div>\n <progress-bar ng-if="progressBar"></progress-bar>\n</div>\n')
}]);
This source diff could not be displayed because it is too large. You can view the blob instead.
"use strict";angular.module("lr.upload",["lr.upload.formdata","lr.upload.iframe","lr.upload.directives"]),angular.module("lr.upload.directives",[]),angular.module("lr.upload.directives").directive("uploadButton",["upload",function(a){return{restrict:"EA",scope:{data:"=?data",url:"@",id:"@",param:"@",method:"@",onUpload:"&",onSuccess:"&",onError:"&",onComplete:"&"},link:function(b,c,d){var e=angular.element(c),f=angular.element('<input id="'+b.id+'" type="file" />');if(e.append(f),f.on("change",function(){var c=angular.element(this);if(!c[0].files||0!==c[0].files.length){var e={url:b.url,method:b.method||"POST",forceIFrameUpload:b.$eval(d.forceIframeUpload)||!1,data:b.data||{}};e.data[b.param||"file"]=c,b.$apply(function(){b.onUpload({files:c[0].files})}),a(e).then(function(a){b.onSuccess({response:a}),b.onComplete({response:a})},function(a){b.onError({response:a}),b.onComplete({response:a})})}}),"required"in d&&d.$observe("required",function(a){var d=""===a?!0:b.$eval(a);f.attr("required",d),c.toggleClass("ng-valid",!d),c.toggleClass("ng-invalid ng-invalid-required",d)}),"accept"in d&&d.$observe("accept",function(a){f.attr("accept",a)}),a.support.formData){var g=function(){f.attr("multiple",!(!b.$eval(d.multiple)||b.$eval(d.forceIframeUpload)))};d.$observe("multiple",g),d.$observe("forceIframeUpload",g)}}}}]),angular.module("lr.upload.formdata",[]).factory("formDataTransform",function(){return function(a){var b=new FormData;return angular.forEach(a,function(a,c){if(angular.isElement(a)){var d=[];angular.forEach(a,function(a){angular.forEach(a.files,function(a){d.push(a)}),a.value=""}),0!==d.length&&(d.length>1?angular.forEach(d,function(a,d){b.append(c+"["+d+"]",a)}):b.append(c,d[0]))}else b.append(c,a)}),b}}).factory("formDataUpload",["$http","formDataTransform",function(a,b){return function(c){return c.transformRequest=b,c.method=c.method||"POST",c.headers=angular.extend(c.headers||{},{"Content-Type":void 0}),a(c)}}]),angular.module("lr.upload.iframe",[]).factory("iFrameUpload",["$q","$http","$document","$rootScope",function(a,b,c,d){function e(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0;c<a.length;c++)if(b===a[c])return c;return-1}function f(f){var g=[],h=a.defer(),i=h.promise;angular.forEach(f.data||{},function(a,b){angular.isElement(a)&&(delete f.data[b],a.attr("name",b),g.push(a))});var j=/\?/.test(f.url)?"&":"?";"DELETE"===f.method?(f.url=f.url+j+"_method=DELETE",f.method="POST"):"PUT"===f.method?(f.url=f.url+j+"_method=PUT",f.method="POST"):"PATCH"===f.method&&(f.url=f.url+j+"_method=PATCH",f.method="POST");var k=angular.element(c[0].body),l=d.$new(),m="iframe-transport-"+l.$id;l.$destroy();var n=angular.element("<form></form>");n.attr("target",m),n.attr("action",f.url),n.attr("method",f.method||"POST"),n.css("display","none"),g.length&&(n.attr("enctype","multipart/form-data"),n.attr("encoding","multipart/form-data"));var o=angular.element('<iframe name="'+m+'" src="javascript:false;"></iframe>');return o.on("load",function(){function a(a,b){var c=[];return angular.isFunction(b)?b(a,c):(angular.forEach(b,function(b){a=b(a,c)}),a)}function c(){var a=e(b.pendingRequests,f);-1!==a&&(b.pendingRequests.splice(a,1),f.$iframeTransportForm.remove(),delete f.$iframeTransportForm)}o.off("load").on("load",function(){var c;try{var d=this.contentWindow?this.contentWindow.document:this.contentDocument;if(c=angular.element(d.body).text(),!c.length)throw new Error}catch(e){}n.append(angular.element('<iframe src="javascript:false;"></iframe>'));try{c=a(c,b.defaults.transformResponse)}catch(e){}h.resolve({data:c,status:200,headers:[],config:f})}),angular.forEach(f.data,function(a,b){var c=angular.element('<input type="hidden" />');c.attr("name",b),c.val(a),n.append(c)}),angular.forEach(g,function(a){var b=a.clone(!0);a.after(b),n.append(a)}),f.$iframeTransportForm=n,b.pendingRequests.push(f),n[0].submit(),i.then(c,c)}),n.append(o),k.append(n),i}return f}]),angular.module("lr.upload").factory("upload",["$window","formDataUpload","iFrameUpload",function(a,b,c){function d(a){return e.formData&&!a.forceIFrameUpload?b(a):c(a)}var e={fileInput:!(new RegExp("(Android (1\\.[0156]|2\\.[01]))|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)|(w(eb)?OSBrowser)|(webOS)|(Kindle/(1\\.0|2\\.[05]|3\\.0))").test(a.navigator.userAgent)||angular.element('<input type="file">').prop("disabled")),fileUpload:!(!a.XMLHttpRequestUpload||!a.FileReader),formData:!!a.FormData};return d.support=e,d}]); "use strict";
\ No newline at end of file angular.module("lr.upload", ["lr.upload.formdata", "lr.upload.iframe", "lr.upload.directives"]), angular.module("lr.upload.directives", []), angular.module("lr.upload.directives").directive("uploadButton", ["upload", function (a) {
return {
restrict: "EA",
scope: {
data: "=?data",
url: "@",
id: "@",
param: "@",
method: "@",
onUpload: "&",
onSuccess: "&",
onError: "&",
onComplete: "&"
},
link: function (b, c, d) {
var e = angular.element(c), f = angular.element('<input id="' + b.id + '" type="file" />');
if (e.append(f), f.on("change", function () {
var c = angular.element(this);
if (!c[0].files || 0 !== c[0].files.length) {
var e = {
url: b.url,
method: b.method || "POST",
forceIFrameUpload: b.$eval(d.forceIframeUpload) || !1,
data: b.data || {}
};
e.data[b.param || "file"] = c, b.$apply(function () {
b.onUpload({files: c[0].files})
}), a(e).then(function (a) {
b.onSuccess({response: a}), b.onComplete({response: a})
}, function (a) {
b.onError({response: a}), b.onComplete({response: a})
})
}
}), "required" in d && d.$observe("required", function (a) {
var d = "" === a ? !0 : b.$eval(a);
f.attr("required", d), c.toggleClass("ng-valid", !d), c.toggleClass("ng-invalid ng-invalid-required", d)
}), "accept" in d && d.$observe("accept", function (a) {
f.attr("accept", a)
}), a.support.formData) {
var g = function () {
f.attr("multiple", !(!b.$eval(d.multiple) || b.$eval(d.forceIframeUpload)))
};
d.$observe("multiple", g), d.$observe("forceIframeUpload", g)
}
}
}
}]), angular.module("lr.upload.formdata", []).factory("formDataTransform", function () {
return function (a) {
var b = new FormData;
return angular.forEach(a, function (a, c) {
if (angular.isElement(a)) {
var d = [];
angular.forEach(a, function (a) {
angular.forEach(a.files, function (a) {
d.push(a)
}), a.value = ""
}), 0 !== d.length && (d.length > 1 ? angular.forEach(d, function (a, d) {
b.append(c + "[" + d + "]", a)
}) : b.append(c, d[0]))
} else b.append(c, a)
}), b
}
}).factory("formDataUpload", ["$http", "formDataTransform", function (a, b) {
return function (c) {
return c.transformRequest = b, c.method = c.method || "POST", c.headers = angular.extend(c.headers || {}, {"Content-Type": void 0}), a(c)
}
}]), angular.module("lr.upload.iframe", []).factory("iFrameUpload", ["$q", "$http", "$document", "$rootScope", function (a, b, c, d) {
function e(a, b) {
if (a.indexOf)return a.indexOf(b);
for (var c = 0; c < a.length; c++)if (b === a[c])return c;
return -1
}
function f(f) {
var g = [], h = a.defer(), i = h.promise;
angular.forEach(f.data || {}, function (a, b) {
angular.isElement(a) && (delete f.data[b], a.attr("name", b), g.push(a))
});
var j = /\?/.test(f.url) ? "&" : "?";
"DELETE" === f.method ? (f.url = f.url + j + "_method=DELETE", f.method = "POST") : "PUT" === f.method ? (f.url = f.url + j + "_method=PUT", f.method = "POST") : "PATCH" === f.method && (f.url = f.url + j + "_method=PATCH", f.method = "POST");
var k = angular.element(c[0].body), l = d.$new(), m = "iframe-transport-" + l.$id;
l.$destroy();
var n = angular.element("<form></form>");
n.attr("target", m), n.attr("action", f.url), n.attr("method", f.method || "POST"), n.css("display", "none"), g.length && (n.attr("enctype", "multipart/form-data"), n.attr("encoding", "multipart/form-data"));
var o = angular.element('<iframe name="' + m + '" src="javascript:false;"></iframe>');
return o.on("load", function () {
function a(a, b) {
var c = [];
return angular.isFunction(b) ? b(a, c) : (angular.forEach(b, function (b) {
a = b(a, c)
}), a)
}
function c() {
var a = e(b.pendingRequests, f);
-1 !== a && (b.pendingRequests.splice(a, 1), f.$iframeTransportForm.remove(), delete f.$iframeTransportForm)
}
o.off("load").on("load", function () {
var c;
try {
var d = this.contentWindow ? this.contentWindow.document : this.contentDocument;
if (c = angular.element(d.body).text(), !c.length)throw new Error
} catch (e) {
}
n.append(angular.element('<iframe src="javascript:false;"></iframe>'));
try {
c = a(c, b.defaults.transformResponse)
} catch (e) {
}
h.resolve({data: c, status: 200, headers: [], config: f})
}), angular.forEach(f.data, function (a, b) {
var c = angular.element('<input type="hidden" />');
c.attr("name", b), c.val(a), n.append(c)
}), angular.forEach(g, function (a) {
var b = a.clone(!0);
a.after(b), n.append(a)
}), f.$iframeTransportForm = n, b.pendingRequests.push(f), n[0].submit(), i.then(c, c)
}), n.append(o), k.append(n), i
}
return f
}]), angular.module("lr.upload").factory("upload", ["$window", "formDataUpload", "iFrameUpload", function (a, b, c) {
function d(a) {
return e.formData && !a.forceIFrameUpload ? b(a) : c(a)
}
var e = {
fileInput: !(new RegExp("(Android (1\\.[0156]|2\\.[01]))|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)|(w(eb)?OSBrowser)|(webOS)|(Kindle/(1\\.0|2\\.[05]|3\\.0))").test(a.navigator.userAgent) || angular.element('<input type="file">').prop("disabled")),
fileUpload: !(!a.XMLHttpRequestUpload || !a.FileReader),
formData: !!a.FormData
};
return d.support = e, d
}]);
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,4 +3,265 @@ ...@@ -3,4 +3,265 @@
* *
* http://bootboxjs.com/license.txt * http://bootboxjs.com/license.txt
*/ */
!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-success":"btn-danger")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"<div class='bootbox modal' tabindex='-1' role='dialog'><div class='modal-dialog'><div class='modal-content'><div class='modal-body'><div class='bootbox-body'></div></div></div></div></div>",header:"<div class='modal-header'><h4 class='modal-title'></h4></div>",footer:"<div class='modal-footer'></div>",closeButton:"<button type='button' class='bootbox-close-button close' data-dismiss='modal' aria-hidden='true'>&times;</button>",form:"<form class='bootbox-form'></form>",inputs:{text:"<input class='bootbox-input bootbox-input-text form-control' autocomplete=off type=text />",textarea:"<textarea class='bootbox-input bootbox-input-textarea form-control'></textarea>",email:"<input class='bootbox-input bootbox-input-email form-control' autocomplete='off' type='email' />",select:"<select class='bootbox-input bootbox-input-select form-control'></select>",checkbox:"<div class='checkbox'><label><input class='bootbox-input bootbox-input-checkbox' type='checkbox' /></label></div>",date:"<input class='bootbox-input bootbox-input-date form-control' autocomplete=off type='date' />",time:"<input class='bootbox-input bootbox-input-time form-control' autocomplete=off type='time' />",number:"<input class='bootbox-input bootbox-input-number form-control' autocomplete=off type='number' />",password:"<input class='bootbox-input bootbox-input-password form-control' autocomplete='off' type='password' />"}},o={locale:"zh_CN",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("<optgroup/>").attr("label",d.group)),e=o[d.group]),e.append("<option value='"+d.value+"'>"+d.text+"</option>")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("<div/>"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="<button data-bb-handler='"+a+"' type='button' class='btn "+b.className+"'>"+b.label+"</button>&emsp;",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"确定",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); !function (a, b) {
\ No newline at end of file "use strict";
"function" == typeof define && define.amd ? define(["jquery"], b) : "object" == typeof exports ? module.exports = b(require("jquery")) : a.bootbox = b(a.jQuery)
}(this, function a(b, c) {
"use strict";
function d(a) {
var b = q[o.locale];
return b ? b[a] : q.en[a]
}
function e(a, c, d) {
a.stopPropagation(), a.preventDefault();
var e = b.isFunction(d) && d.call(c, a) === !1;
e || c.modal("hide")
}
function f(a) {
var b, c = 0;
for (b in a)c++;
return c
}
function g(a, c) {
var d = 0;
b.each(a, function (a, b) {
c(a, b, d++)
})
}
function h(a) {
var c, d;
if ("object" != typeof a)throw new Error("Please supply an object of options");
if (!a.message)throw new Error("Please specify a message");
return a = b.extend({}, o, a), a.buttons || (a.buttons = {}), c = a.buttons, d = f(c), g(c, function (a, e, f) {
if (b.isFunction(e) && (e = c[a] = {callback: e}), "object" !== b.type(e))throw new Error("button with key " + a + " must be an object");
e.label || (e.label = a), e.className || (e.className = 2 >= d && f === d - 1 ? "btn-success" : "btn-danger")
}), a
}
function i(a, b) {
var c = a.length, d = {};
if (1 > c || c > 2)throw new Error("Invalid argument length");
return 2 === c || "string" == typeof a[0] ? (d[b[0]] = a[0], d[b[1]] = a[1]) : d = a[0], d
}
function j(a, c, d) {
return b.extend(!0, {}, a, i(c, d))
}
function k(a, b, c, d) {
var e = {className: "bootbox-" + a, buttons: l.apply(null, b)};
return m(j(e, d, c), b)
}
function l() {
for (var a = {}, b = 0, c = arguments.length; c > b; b++) {
var e = arguments[b], f = e.toLowerCase(), g = e.toUpperCase();
a[f] = {label: d(g)}
}
return a
}
function m(a, b) {
var d = {};
return g(b, function (a, b) {
d[b] = !0
}), g(a.buttons, function (a) {
if (d[a] === c)throw new Error("button key " + a + " is not allowed (options are " + b.join("\n") + ")")
}), a
}
var n = {
dialog: "<div class='bootbox modal' tabindex='-1' role='dialog'><div class='modal-dialog'><div class='modal-content'><div class='modal-body'><div class='bootbox-body'></div></div></div></div></div>",
header: "<div class='modal-header'><h4 class='modal-title'></h4></div>",
footer: "<div class='modal-footer'></div>",
closeButton: "<button type='button' class='bootbox-close-button close' data-dismiss='modal' aria-hidden='true'>&times;</button>",
form: "<form class='bootbox-form'></form>",
inputs: {
text: "<input class='bootbox-input bootbox-input-text form-control' autocomplete=off type=text />",
textarea: "<textarea class='bootbox-input bootbox-input-textarea form-control'></textarea>",
email: "<input class='bootbox-input bootbox-input-email form-control' autocomplete='off' type='email' />",
select: "<select class='bootbox-input bootbox-input-select form-control'></select>",
checkbox: "<div class='checkbox'><label><input class='bootbox-input bootbox-input-checkbox' type='checkbox' /></label></div>",
date: "<input class='bootbox-input bootbox-input-date form-control' autocomplete=off type='date' />",
time: "<input class='bootbox-input bootbox-input-time form-control' autocomplete=off type='time' />",
number: "<input class='bootbox-input bootbox-input-number form-control' autocomplete=off type='number' />",
password: "<input class='bootbox-input bootbox-input-password form-control' autocomplete='off' type='password' />"
}
}, o = {
locale: "zh_CN",
backdrop: "static",
animate: !0,
className: null,
closeButton: !0,
show: !0,
container: "body"
}, p = {};
p.alert = function () {
var a;
if (a = k("alert", ["ok"], ["message", "callback"], arguments), a.callback && !b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");
return a.buttons.ok.callback = a.onEscape = function () {
return b.isFunction(a.callback) ? a.callback.call(this) : !0
}, p.dialog(a)
}, p.confirm = function () {
var a;
if (a = k("confirm", ["cancel", "confirm"], ["message", "callback"], arguments), a.buttons.cancel.callback = a.onEscape = function () {
return a.callback.call(this, !1)
}, a.buttons.confirm.callback = function () {
return a.callback.call(this, !0)
}, !b.isFunction(a.callback))throw new Error("confirm requires a callback");
return p.dialog(a)
}, p.prompt = function () {
var a, d, e, f, h, i, k;
if (f = b(n.form), d = {
className: "bootbox-prompt",
buttons: l("cancel", "confirm"),
value: "",
inputType: "text"
}, a = m(j(d, arguments, ["title", "callback"]), ["cancel", "confirm"]), i = a.show === c ? !0 : a.show, a.message = f, a.buttons.cancel.callback = a.onEscape = function () {
return a.callback.call(this, null)
}, a.buttons.confirm.callback = function () {
var c;
switch (a.inputType) {
case"text":
case"textarea":
case"email":
case"select":
case"date":
case"time":
case"number":
case"password":
c = h.val();
break;
case"checkbox":
var d = h.find("input:checked");
c = [], g(d, function (a, d) {
c.push(b(d).val())
})
}
return a.callback.call(this, c)
}, a.show = !1, !a.title)throw new Error("prompt requires a title");
if (!b.isFunction(a.callback))throw new Error("prompt requires a callback");
if (!n.inputs[a.inputType])throw new Error("invalid prompt type");
switch (h = b(n.inputs[a.inputType]), a.inputType) {
case"text":
case"textarea":
case"email":
case"date":
case"time":
case"number":
case"password":
h.val(a.value);
break;
case"select":
var o = {};
if (k = a.inputOptions || [], !b.isArray(k))throw new Error("Please pass an array of input options");
if (!k.length)throw new Error("prompt with select requires options");
g(k, function (a, d) {
var e = h;
if (d.value === c || d.text === c)throw new Error("given options in wrong format");
d.group && (o[d.group] || (o[d.group] = b("<optgroup/>").attr("label", d.group)), e = o[d.group]), e.append("<option value='" + d.value + "'>" + d.text + "</option>")
}), g(o, function (a, b) {
h.append(b)
}), h.val(a.value);
break;
case"checkbox":
var q = b.isArray(a.value) ? a.value : [a.value];
if (k = a.inputOptions || [], !k.length)throw new Error("prompt with checkbox requires options");
if (!k[0].value || !k[0].text)throw new Error("given options in wrong format");
h = b("<div/>"), g(k, function (c, d) {
var e = b(n.inputs[a.inputType]);
e.find("input").attr("value", d.value), e.find("label").append(d.text), g(q, function (a, b) {
b === d.value && e.find("input").prop("checked", !0)
}), h.append(e)
})
}
return a.placeholder && h.attr("placeholder", a.placeholder), a.pattern && h.attr("pattern", a.pattern), a.maxlength && h.attr("maxlength", a.maxlength), f.append(h), f.on("submit", function (a) {
a.preventDefault(), a.stopPropagation(), e.find(".btn-primary").click()
}), e = p.dialog(a), e.off("shown.bs.modal"), e.on("shown.bs.modal", function () {
h.focus()
}), i === !0 && e.modal("show"), e
}, p.dialog = function (a) {
a = h(a);
var d = b(n.dialog), f = d.find(".modal-dialog"), i = d.find(".modal-body"), j = a.buttons, k = "", l = {onEscape: a.onEscape};
if (b.fn.modal === c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");
if (g(j, function (a, b) {
k += "<button data-bb-handler='" + a + "' type='button' class='btn " + b.className + "'>" + b.label + "</button>&emsp;", l[a] = b.callback
}), i.find(".bootbox-body").html(a.message), a.animate === !0 && d.addClass("fade"), a.className && d.addClass(a.className), "large" === a.size ? f.addClass("modal-lg") : "small" === a.size && f.addClass("modal-sm"), a.title && i.before(n.header), a.closeButton) {
var m = b(n.closeButton);
a.title ? d.find(".modal-header").prepend(m) : m.css("margin-top", "-10px").prependTo(i)
}
return a.title && d.find(".modal-title").html(a.title), k.length && (i.after(n.footer), d.find(".modal-footer").html(k)), d.on("hidden.bs.modal", function (a) {
a.target === this && d.remove()
}), d.on("shown.bs.modal", function () {
d.find(".btn-primary:first").focus()
}), "static" !== a.backdrop && d.on("click.dismiss.bs.modal", function (a) {
d.children(".modal-backdrop").length && (a.currentTarget = d.children(".modal-backdrop").get(0)), a.target === a.currentTarget && d.trigger("escape.close.bb")
}), d.on("escape.close.bb", function (a) {
l.onEscape && e(a, d, l.onEscape)
}), d.on("click", ".modal-footer button", function (a) {
var c = b(this).data("bb-handler");
e(a, d, l[c])
}), d.on("click", ".bootbox-close-button", function (a) {
e(a, d, l.onEscape)
}), d.on("keyup", function (a) {
27 === a.which && d.trigger("escape.close.bb")
}), b(a.container).append(d), d.modal({
backdrop: a.backdrop ? "static" : !1,
keyboard: !1,
show: !1
}), a.show && d.modal("show"), d
}, p.setDefaults = function () {
var a = {};
2 === arguments.length ? a[arguments[0]] = arguments[1] : a = arguments[0], b.extend(o, a)
}, p.hideAll = function () {
return b(".bootbox").modal("hide"), p
};
var q = {
bg_BG: {OK: "Ок", CANCEL: "Отказ", CONFIRM: "Потвърждавам"},
br: {OK: "OK", CANCEL: "Cancelar", CONFIRM: "Sim"},
cs: {OK: "OK", CANCEL: "Zrušit", CONFIRM: "Potvrdit"},
da: {OK: "OK", CANCEL: "Annuller", CONFIRM: "Accepter"},
de: {OK: "OK", CANCEL: "Abbrechen", CONFIRM: "Akzeptieren"},
el: {OK: "Εντάξει", CANCEL: "Ακύρωση", CONFIRM: "Επιβεβαίωση"},
en: {OK: "OK", CANCEL: "Cancel", CONFIRM: "OK"},
es: {OK: "OK", CANCEL: "Cancelar", CONFIRM: "Aceptar"},
et: {OK: "OK", CANCEL: "Katkesta", CONFIRM: "OK"},
fa: {OK: "قبول", CANCEL: "لغو", CONFIRM: "تایید"},
fi: {OK: "OK", CANCEL: "Peruuta", CONFIRM: "OK"},
fr: {OK: "OK", CANCEL: "Annuler", CONFIRM: "D'accord"},
he: {OK: "אישור", CANCEL: "ביטול", CONFIRM: "אישור"},
hu: {OK: "OK", CANCEL: "Mégsem", CONFIRM: "Megerősít"},
hr: {OK: "OK", CANCEL: "Odustani", CONFIRM: "Potvrdi"},
id: {OK: "OK", CANCEL: "Batal", CONFIRM: "OK"},
it: {OK: "OK", CANCEL: "Annulla", CONFIRM: "Conferma"},
ja: {OK: "OK", CANCEL: "キャンセル", CONFIRM: "確認"},
lt: {OK: "Gerai", CANCEL: "Atšaukti", CONFIRM: "Patvirtinti"},
lv: {OK: "Labi", CANCEL: "Atcelt", CONFIRM: "Apstiprināt"},
nl: {OK: "OK", CANCEL: "Annuleren", CONFIRM: "Accepteren"},
no: {OK: "OK", CANCEL: "Avbryt", CONFIRM: "OK"},
pl: {OK: "OK", CANCEL: "Anuluj", CONFIRM: "Potwierdź"},
pt: {OK: "OK", CANCEL: "Cancelar", CONFIRM: "Confirmar"},
ru: {OK: "OK", CANCEL: "Отмена", CONFIRM: "Применить"},
sq: {OK: "OK", CANCEL: "Anulo", CONFIRM: "Prano"},
sv: {OK: "OK", CANCEL: "Avbryt", CONFIRM: "OK"},
th: {OK: "ตกลง", CANCEL: "ยกเลิก", CONFIRM: "ยืนยัน"},
tr: {OK: "Tamam", CANCEL: "İptal", CONFIRM: "Onayla"},
zh_CN: {OK: "确定", CANCEL: "取消", CONFIRM: "确认"},
zh_TW: {OK: "OK", CANCEL: "取消", CONFIRM: "確認"}
};
return p.addLocale = function (a, c) {
return b.each(["OK", "CANCEL", "CONFIRM"], function (a, b) {
if (!c[b])throw new Error("Please supply a translation for '" + b + "'")
}), q[a] = {OK: c.OK, CANCEL: c.CANCEL, CONFIRM: c.CONFIRM}, p
}, p.removeLocale = function (a) {
return delete q[a], p
}, p.setLocale = function (a) {
return p.setDefaults("locale", a)
}, p.init = function (c) {
return a(c || b)
}, p
});
#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 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms 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)}} #loading-bar, #loading-bar-spinner {
\ No newline at end of file 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 400ms linear infinite;
-moz-animation: loading-bar-spinner 400ms linear infinite;
-ms-animation: loading-bar-spinner 400ms linear infinite;
-o-animation: loading-bar-spinner 400ms linear infinite;
animation: loading-bar-spinner 400ms 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)
}
}
...@@ -4,4 +4,95 @@ ...@@ -4,4 +4,95 @@
* Copyright (c) 2015 Wes Cruver * Copyright (c) 2015 Wes Cruver
* License: MIT * License: MIT
*/ */
!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(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),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(a,b,c,d){function e(){k||(k=a.get("$animate"));var e=b.find(n).eq(0);c.cancel(m),r||(d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,e,angular.element(e[0].lastChild)),u&&k.enter(q,e,angular.element(e[0].lastChild)),f(w))}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),d.$broadcast("cfpLoadingBar:completed"),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q)},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(); !function () {
\ No newline at end of file "use strict";
angular.module("angular-loading-bar", ["cfp.loadingBarInterceptor"]), angular.module("chieffancypants.loadingBar", ["cfp.loadingBarInterceptor"]), angular.module("cfp.loadingBarInterceptor", ["cfp.loadingBar"]).config(["$httpProvider", function (a) {
var b = ["$q", "$cacheFactory", "$timeout", "$rootScope", "$log", "cfpLoadingBar", function (b, c, d, e, f, g) {
function h() {
d.cancel(j), g.complete(), l = 0, k = 0
}
function i(b) {
var d, e = c.get("$http"), f = a.defaults;
!b.cache && !f.cache || b.cache === !1 || "GET" !== b.method && "JSONP" !== b.method || (d = angular.isObject(b.cache) ? b.cache : angular.isObject(f.cache) ? f.cache : e);
var g = void 0 !== d ? void 0 !== d.get(b.url) : !1;
return void 0 !== b.cached && g !== b.cached ? b.cached : (b.cached = g, g)
}
var j, k = 0, l = 0, m = g.latencyThreshold;
return {
request: function (a) {
return a.ignoreLoadingBar || i(a) || (e.$broadcast("cfpLoadingBar:loading", {url: a.url}), 0 === k && (j = d(function () {
g.start()
}, m)), k++, g.set(l / k)), a
}, response: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), a) : (f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), a)
}, responseError: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), b.reject(a)) : (f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), b.reject(a))
}
}
}];
a.interceptors.push(b)
}]), 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 (a, b, c, d) {
function e() {
k || (k = a.get("$animate"));
var e = b.find(n).eq(0);
c.cancel(m), r || (d.$broadcast("cfpLoadingBar:started"), r = !0, v && k.enter(o, e, angular.element(e[0].lastChild)), u && k.enter(q, e, angular.element(e[0].lastChild)), f(w))
}
function f(a) {
if (r) {
var b = 100 * a + "%";
p.css("width", b), s = a, t && (c.cancel(l), l = c(function () {
g()
}, 250))
}
}
function g() {
if (!(h() >= 1)) {
var a = 0, b = h();
a = b >= 0 && .25 > b ? (3 * Math.random() + 3) / 100 : b >= .25 && .65 > b ? 3 * Math.random() / 100 : b >= .65 && .9 > b ? 2 * Math.random() / 100 : b >= .9 && .99 > b ? .005 : 0;
var c = h() + a;
f(c)
}
}
function h() {
return s
}
function i() {
s = 0, r = !1
}
function j() {
k || (k = a.get("$animate")), d.$broadcast("cfpLoadingBar:completed"), f(1), c.cancel(m), m = c(function () {
var a = k.leave(o, i);
a && a.then && a.then(i), k.leave(q)
}, 500)
}
var k, l, m, n = this.parentSelector, o = angular.element(this.loadingBarTemplate), p = o.find("div").eq(0), q = angular.element(this.spinnerTemplate), r = !1, s = 0, t = this.autoIncrement, u = this.includeSpinner, v = this.includeBar, w = this.startSize;
return {
start: e,
set: f,
status: h,
inc: g,
complete: j,
autoIncrement: this.autoIncrement,
includeSpinner: this.includeSpinner,
latencyThreshold: this.latencyThreshold,
parentSelector: this.parentSelector,
startSize: this.startSize
}
}]
})
}();
/** /**
* @version 2.0.2 * @version 2.0.2
* @license MIT * @license MIT
*/ */
!function(t,e){"use strict";t.module("smart-table",[]).run(["$templateCache",function(t){t.put("template/smart-table/pagination.html",'<nav ng-if="pages.length >= 2"><ul class="pagination"><li ng-repeat="page in pages" ng-class="{active: page==currentPage}"><a ng-click="selectPage(page)">{{page}}</a></li></ul></nav>')}]),t.module("smart-table").constant("stConfig",{pagination:{template:"template/smart-table/pagination.html",itemsByPage:10,displayedPages:5},search:{delay:400},select:{mode:"single",selectedClass:"st-selected"},sort:{ascentClass:"st-sort-ascent",descentClass:"st-sort-descent"},pipe:{delay:100}}),t.module("smart-table").controller("stTableController",["$scope","$parse","$filter","$attrs",function(a,s,n,r){function i(t){return t?[].concat(t):[]}function c(){h=i(l(a)),P===!0&&S.pipe()}var l,o,u,p=r.stTable,g=s(p),f=g.assign,d=n("orderBy"),m=n("filter"),h=i(g(a)),b={sort:{},search:{},pagination:{start:0}},P=!0,S=this;r.stSafeSrc&&(l=s(r.stSafeSrc),a.$watch(function(){var t=l(a);return t?t.length:0},function(t){t!==h.length&&c()}),a.$watch(function(){return l(a)},function(t,e){t!==e&&c()})),this.sortBy=function(e,a){return b.sort.predicate=e,b.sort.reverse=a===!0,t.isFunction(e)?b.sort.functionName=e.name:delete b.sort.functionName,b.pagination.start=0,this.pipe()},this.search=function(e,a){var s=b.search.predicateObject||{},n=a?a:"$";return e=t.isString(e)?e.trim():e,s[n]=e,e||delete s[n],b.search.predicateObject=s,b.pagination.start=0,this.pipe()},this.pipe=function(){var t,s=b.pagination;o=b.search.predicateObject?m(h,b.search.predicateObject):h,b.sort.predicate&&(o=d(o,b.sort.predicate,b.sort.reverse)),s.number!==e&&(s.numberOfPages=o.length>0?Math.ceil(o.length/s.number):1,s.start=s.start>=o.length?(s.numberOfPages-1)*s.number:s.start,t=o.slice(s.start,s.start+parseInt(s.number))),f(a,t||o)},this.select=function(t,a){var s=h,n=s.indexOf(t);-1!==n&&("single"===a?(t.isSelected=t.isSelected!==!0,u&&(u.isSelected=!1),u=t.isSelected===!0?t:e):s[n].isSelected=!s[n].isSelected)},this.slice=function(t,e){return b.pagination.start=t,b.pagination.number=e,this.pipe()},this.tableState=function(){return b},this.getFilteredCollection=function(){return o||h},this.setFilterFunction=function(t){m=n(t)},this.setSortFunction=function(t){d=n(t)},this.preventPipeOnWatch=function(){P=!1}}]).directive("stTable",function(){return{restrict:"A",controller:"stTableController",link:function(t,e,a,s){a.stSetFilter&&s.setFilterFunction(a.stSetFilter),a.stSetSort&&s.setSortFunction(a.stSetSort)}}}),t.module("smart-table").directive("stSearch",["stConfig","$timeout",function(t,e){return{require:"^stTable",link:function(a,s,n,r){var i=r,c=null,l=n.stDelay||t.search.delay;n.$observe("stSearch",function(t,e){var a=s[0].value;t!==e&&a&&(r.tableState().search={},i.search(a,t))}),a.$watch(function(){return r.tableState().search},function(t){var e=n.stSearch||"$";t.predicateObject&&t.predicateObject[e]!==s[0].value&&(s[0].value=t.predicateObject[e]||"")},!0),s.bind("input",function(t){t=t.originalEvent||t,null!==c&&e.cancel(c),c=e(function(){i.search(t.target.value,n.stSearch||""),c=null},l)})}}}]),t.module("smart-table").directive("stSelectRow",["stConfig",function(t){return{restrict:"A",require:"^stTable",scope:{row:"=stSelectRow"},link:function(e,a,s,n){var r=s.stSelectMode||t.select.mode;a.bind("click",function(){e.$apply(function(){n.select(e.row,r)})}),e.$watch("row.isSelected",function(e){e===!0?a.addClass(t.select.selectedClass):a.removeClass(t.select.selectedClass)})}}}]),t.module("smart-table").directive("stSort",["stConfig","$parse",function(a,s){return{restrict:"A",require:"^stTable",link:function(n,r,i,c){function l(){g++,u=t.isFunction(p(n))?p(n):i.stSort,g%3===0&&i.stSkipNatural===e?(g=0,c.tableState().sort={},c.tableState().pagination.start=0,c.pipe()):c.sortBy(u,g%2===0)}var o,u=i.stSort,p=s(u),g=0,f=i.stClassAscent||a.sort.ascentClass,d=i.stClassDescent||a.sort.descentClass,m=[f,d];i.stSortDefault&&(o=n.$eval(i.stSortDefault)!==e?n.$eval(i.stSortDefault):i.stSortDefault),r.bind("click",function(){u&&n.$apply(l)}),o&&(g="reverse"===o?1:0,l()),n.$watch(function(){return c.tableState().sort},function(t){t.predicate!==u?(g=0,r.removeClass(f).removeClass(d)):(g=t.reverse===!0?2:1,r.removeClass(m[g%2]).addClass(m[g-1]))},!0)}}}]),t.module("smart-table").directive("stPagination",["stConfig",function(t){return{restrict:"EA",require:"^stTable",scope:{stItemsByPage:"=?",stDisplayedPages:"=?",stPageChange:"&"},templateUrl:function(e,a){return a.stTemplate?a.stTemplate:t.pagination.template},link:function(e,a,s,n){function r(){var t,a,s=n.tableState().pagination,r=1,i=e.currentPage;for(e.currentPage=Math.floor(s.start/s.number)+1,r=Math.max(r,e.currentPage-Math.abs(Math.floor(e.stDisplayedPages/2))),t=r+e.stDisplayedPages,t>s.numberOfPages&&(t=s.numberOfPages+1,r=Math.max(1,t-e.stDisplayedPages)),e.pages=[],e.numPages=s.numberOfPages,a=r;t>a;a++)e.pages.push(a);i!==e.currentPage&&e.stPageChange({newPage:e.currentPage})}e.stItemsByPage=e.stItemsByPage?+e.stItemsByPage:t.pagination.itemsByPage,e.stDisplayedPages=e.stDisplayedPages?+e.stDisplayedPages:t.pagination.displayedPages,e.currentPage=1,e.pages=[],e.$watch(function(){return n.tableState().pagination},r,!0),e.$watch("stItemsByPage",function(t,a){t!==a&&e.selectPage(1)}),e.$watch("stDisplayedPages",r),e.selectPage=function(t){t>0&&t<=e.numPages&&n.slice((t-1)*e.stItemsByPage,e.stItemsByPage)},n.tableState().pagination.number||n.slice(0,e.stItemsByPage)}}}]),t.module("smart-table").directive("stPipe",["stConfig","$timeout",function(e,a){return{require:"stTable",scope:{stPipe:"="},link:{pre:function(s,n,r,i){var c=null;t.isFunction(s.stPipe)&&(i.preventPipeOnWatch(),i.pipe=function(){return null!==c&&a.cancel(c),c=a(function(){s.stPipe(i.tableState(),i)},e.pipe.delay)})},post:function(t,e,a,s){s.pipe()}}}}])}(angular); !function (t, e) {
\ No newline at end of file "use strict";
t.module("smart-table", []).run(["$templateCache", function (t) {
t.put("template/smart-table/pagination.html", '<nav ng-if="pages.length >= 2"><ul class="pagination"><li ng-repeat="page in pages" ng-class="{active: page==currentPage}"><a ng-click="selectPage(page)">{{page}}</a></li></ul></nav>')
}]), t.module("smart-table").constant("stConfig", {
pagination: {
template: "template/smart-table/pagination.html",
itemsByPage: 10,
displayedPages: 5
},
search: {delay: 400},
select: {mode: "single", selectedClass: "st-selected"},
sort: {ascentClass: "st-sort-ascent", descentClass: "st-sort-descent"},
pipe: {delay: 100}
}), t.module("smart-table").controller("stTableController", ["$scope", "$parse", "$filter", "$attrs", function (a, s, n, r) {
function i(t) {
return t ? [].concat(t) : []
}
function c() {
h = i(l(a)), P === !0 && S.pipe()
}
var l, o, u, p = r.stTable, g = s(p), f = g.assign, d = n("orderBy"), m = n("filter"), h = i(g(a)), b = {
sort: {},
search: {},
pagination: {start: 0}
}, P = !0, S = this;
r.stSafeSrc && (l = s(r.stSafeSrc), a.$watch(function () {
var t = l(a);
return t ? t.length : 0
}, function (t) {
t !== h.length && c()
}), a.$watch(function () {
return l(a)
}, function (t, e) {
t !== e && c()
})), this.sortBy = function (e, a) {
return b.sort.predicate = e, b.sort.reverse = a === !0, t.isFunction(e) ? b.sort.functionName = e.name : delete b.sort.functionName, b.pagination.start = 0, this.pipe()
}, this.search = function (e, a) {
var s = b.search.predicateObject || {}, n = a ? a : "$";
return e = t.isString(e) ? e.trim() : e, s[n] = e, e || delete s[n], b.search.predicateObject = s, b.pagination.start = 0, this.pipe()
}, this.pipe = function () {
var t, s = b.pagination;
o = b.search.predicateObject ? m(h, b.search.predicateObject) : h, b.sort.predicate && (o = d(o, b.sort.predicate, b.sort.reverse)), s.number !== e && (s.numberOfPages = o.length > 0 ? Math.ceil(o.length / s.number) : 1, s.start = s.start >= o.length ? (s.numberOfPages - 1) * s.number : s.start, t = o.slice(s.start, s.start + parseInt(s.number))), f(a, t || o)
}, this.select = function (t, a) {
var s = h, n = s.indexOf(t);
-1 !== n && ("single" === a ? (t.isSelected = t.isSelected !== !0, u && (u.isSelected = !1), u = t.isSelected === !0 ? t : e) : s[n].isSelected = !s[n].isSelected)
}, this.slice = function (t, e) {
return b.pagination.start = t, b.pagination.number = e, this.pipe()
}, this.tableState = function () {
return b
}, this.getFilteredCollection = function () {
return o || h
}, this.setFilterFunction = function (t) {
m = n(t)
}, this.setSortFunction = function (t) {
d = n(t)
}, this.preventPipeOnWatch = function () {
P = !1
}
}]).directive("stTable", function () {
return {
restrict: "A", controller: "stTableController", link: function (t, e, a, s) {
a.stSetFilter && s.setFilterFunction(a.stSetFilter), a.stSetSort && s.setSortFunction(a.stSetSort)
}
}
}), t.module("smart-table").directive("stSearch", ["stConfig", "$timeout", function (t, e) {
return {
require: "^stTable", link: function (a, s, n, r) {
var i = r, c = null, l = n.stDelay || t.search.delay;
n.$observe("stSearch", function (t, e) {
var a = s[0].value;
t !== e && a && (r.tableState().search = {}, i.search(a, t))
}), a.$watch(function () {
return r.tableState().search
}, function (t) {
var e = n.stSearch || "$";
t.predicateObject && t.predicateObject[e] !== s[0].value && (s[0].value = t.predicateObject[e] || "")
}, !0), s.bind("input", function (t) {
t = t.originalEvent || t, null !== c && e.cancel(c), c = e(function () {
i.search(t.target.value, n.stSearch || ""), c = null
}, l)
})
}
}
}]), t.module("smart-table").directive("stSelectRow", ["stConfig", function (t) {
return {
restrict: "A", require: "^stTable", scope: {row: "=stSelectRow"}, link: function (e, a, s, n) {
var r = s.stSelectMode || t.select.mode;
a.bind("click", function () {
e.$apply(function () {
n.select(e.row, r)
})
}), e.$watch("row.isSelected", function (e) {
e === !0 ? a.addClass(t.select.selectedClass) : a.removeClass(t.select.selectedClass)
})
}
}
}]), t.module("smart-table").directive("stSort", ["stConfig", "$parse", function (a, s) {
return {
restrict: "A", require: "^stTable", link: function (n, r, i, c) {
function l() {
g++, u = t.isFunction(p(n)) ? p(n) : i.stSort, g % 3 === 0 && i.stSkipNatural === e ? (g = 0, c.tableState().sort = {}, c.tableState().pagination.start = 0, c.pipe()) : c.sortBy(u, g % 2 === 0)
}
var o, u = i.stSort, p = s(u), g = 0, f = i.stClassAscent || a.sort.ascentClass, d = i.stClassDescent || a.sort.descentClass, m = [f, d];
i.stSortDefault && (o = n.$eval(i.stSortDefault) !== e ? n.$eval(i.stSortDefault) : i.stSortDefault), r.bind("click", function () {
u && n.$apply(l)
}), o && (g = "reverse" === o ? 1 : 0, l()), n.$watch(function () {
return c.tableState().sort
}, function (t) {
t.predicate !== u ? (g = 0, r.removeClass(f).removeClass(d)) : (g = t.reverse === !0 ? 2 : 1, r.removeClass(m[g % 2]).addClass(m[g - 1]))
}, !0)
}
}
}]), t.module("smart-table").directive("stPagination", ["stConfig", function (t) {
return {
restrict: "EA",
require: "^stTable",
scope: {stItemsByPage: "=?", stDisplayedPages: "=?", stPageChange: "&"},
templateUrl: function (e, a) {
return a.stTemplate ? a.stTemplate : t.pagination.template
},
link: function (e, a, s, n) {
function r() {
var t, a, s = n.tableState().pagination, r = 1, i = e.currentPage;
for (e.currentPage = Math.floor(s.start / s.number) + 1, r = Math.max(r, e.currentPage - Math.abs(Math.floor(e.stDisplayedPages / 2))), t = r + e.stDisplayedPages, t > s.numberOfPages && (t = s.numberOfPages + 1, r = Math.max(1, t - e.stDisplayedPages)), e.pages = [], e.numPages = s.numberOfPages, a = r; t > a; a++)e.pages.push(a);
i !== e.currentPage && e.stPageChange({newPage: e.currentPage})
}
e.stItemsByPage = e.stItemsByPage ? +e.stItemsByPage : t.pagination.itemsByPage, e.stDisplayedPages = e.stDisplayedPages ? +e.stDisplayedPages : t.pagination.displayedPages, e.currentPage = 1, e.pages = [], e.$watch(function () {
return n.tableState().pagination
}, r, !0), e.$watch("stItemsByPage", function (t, a) {
t !== a && e.selectPage(1)
}), e.$watch("stDisplayedPages", r), e.selectPage = function (t) {
t > 0 && t <= e.numPages && n.slice((t - 1) * e.stItemsByPage, e.stItemsByPage)
}, n.tableState().pagination.number || n.slice(0, e.stItemsByPage)
}
}
}]), t.module("smart-table").directive("stPipe", ["stConfig", "$timeout", function (e, a) {
return {
require: "stTable", scope: {stPipe: "="}, link: {
pre: function (s, n, r, i) {
var c = null;
t.isFunction(s.stPipe) && (i.preventPipeOnWatch(), i.pipe = function () {
return null !== c && a.cancel(c), c = a(function () {
s.stPipe(i.tableState(), i)
}, e.pipe.delay)
})
}, post: function (t, e, a, s) {
s.pipe()
}
}
}
}])
}(angular);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*! /*!
angular-xeditable - 0.1.8 angular-xeditable - 0.1.8
Edit-in-place for angular.js Edit-in-place for angular.js
Build date: 2014-01-10 Build date: 2014-01-10
*/ */
angular.module("xeditable",[]).value("editableOptions",{theme:"default",buttons:"right",blurElem:"cancel",blurForm:"ignore",activate:"focus"}),angular.module("xeditable").directive("editableBsdate",["editableDirectiveFactory",function(a){return a({directiveName:"editableBsdate",inputTpl:'<input type="text">'})}]),angular.module("xeditable").directive("editableBstime",["editableDirectiveFactory",function(a){return a({directiveName:"editableBstime",inputTpl:"<timepicker></timepicker>",render:function(){this.parent.render.call(this);var a=angular.element('<div class="well well-small" style="display:inline-block;"></div>');a.attr("ng-model",this.inputEl.attr("ng-model")),this.inputEl.removeAttr("ng-model"),this.attrs.eNgChange&&(a.attr("ng-change",this.inputEl.attr("ng-change")),this.inputEl.removeAttr("ng-change")),this.inputEl.wrap(a)}})}]),angular.module("xeditable").directive("editableCheckbox",["editableDirectiveFactory",function(a){return a({directiveName:"editableCheckbox",inputTpl:'<input type="checkbox">',render:function(){this.parent.render.call(this),this.attrs.eTitle&&(this.inputEl.wrap("<label></label>"),this.inputEl.after(angular.element("<span></span>").text(this.attrs.eTitle)))},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableChecklist",["editableDirectiveFactory","editableNgOptionsParser",function(a,b){return a({directiveName:"editableChecklist",inputTpl:"<span></span>",useCopy:!0,render:function(){this.parent.render.call(this);var a=b(this.attrs.eNgOptions),c='<label ng-repeat="'+a.ngRepeat+'">'+'<input type="checkbox" checklist-model="$parent.$data" checklist-value="'+a.locals.valueFn+'">'+'<span ng-bind="'+a.locals.displayFn+'"></span></label>';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(c)}})}]),function(){var a="text|email|tel|number|url|search|color|date|datetime|time|month|week".split("|");angular.forEach(a,function(a){var b="editable"+a.charAt(0).toUpperCase()+a.slice(1);angular.module("xeditable").directive(b,["editableDirectiveFactory",function(c){return c({directiveName:b,inputTpl:'<input type="'+a+'">'})}])}),angular.module("xeditable").directive("editableRange",["editableDirectiveFactory",function(a){return a({directiveName:"editableRange",inputTpl:'<input type="range" id="range" name="range">',render:function(){this.parent.render.call(this),this.inputEl.after("<output>{{$data}}</output>")}})}])}(),angular.module("xeditable").directive("editableRadiolist",["editableDirectiveFactory","editableNgOptionsParser",function(a,b){return a({directiveName:"editableRadiolist",inputTpl:"<span></span>",render:function(){this.parent.render.call(this);var a=b(this.attrs.eNgOptions),c='<label ng-repeat="'+a.ngRepeat+'">'+'<input type="radio" ng-model="$parent.$data" value="{{'+a.locals.valueFn+'}}">'+'<span ng-bind="'+a.locals.displayFn+'"></span></label>';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(c)},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableSelect",["editableDirectiveFactory",function(a){return a({directiveName:"editableSelect",inputTpl:"<select></select>",autosubmit:function(){var a=this;a.inputEl.bind("change",function(){a.scope.$apply(function(){a.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableTextarea",["editableDirectiveFactory",function(a){return a({directiveName:"editableTextarea",inputTpl:"<textarea></textarea>",addListeners:function(){var a=this;a.parent.addListeners.call(a),a.single&&"no"!==a.buttons&&a.autosubmit()},autosubmit:function(){var a=this;a.inputEl.bind("keydown",function(b){(b.ctrlKey||b.metaKey)&&13===b.keyCode&&a.scope.$apply(function(){a.scope.$form.$submit()})})}})}]),angular.module("xeditable").factory("editableController",["$q","editableUtils",function(a,b){function c(a,c,d,e,f,g,h,i,j){var k,l,m=this;m.scope=a,m.elem=d,m.attrs=c,m.inputEl=null,m.editorEl=null,m.single=!0,m.error="",m.theme=f[g.theme]||f["default"],m.parent={},m.inputTpl="",m.directiveName="",m.useCopy=!1,m.single=null,m.buttons="right",m.init=function(b){if(m.single=b,m.name=c.eName||c[m.directiveName],!c[m.directiveName])throw"You should provide value for `"+m.directiveName+"` in editable element!";k=e(c[m.directiveName]),m.buttons=m.single?m.attrs.buttons||g.buttons:"no",c.eName&&m.scope.$watch("$data",function(a){m.scope.$form.$data[c.eName]=a}),c.onshow&&(m.onshow=function(){return m.catchError(e(c.onshow)(a))}),c.onhide&&(m.onhide=function(){return e(c.onhide)(a)}),c.oncancel&&(m.oncancel=function(){return e(c.oncancel)(a)}),c.onbeforesave&&(m.onbeforesave=function(){return m.catchError(e(c.onbeforesave)(a))}),c.onaftersave&&(m.onaftersave=function(){return m.catchError(e(c.onaftersave)(a))}),a.$parent.$watch(c[m.directiveName],function(){m.handleEmpty()})},m.render=function(){var a=m.theme;m.inputEl=angular.element(m.inputTpl),m.controlsEl=angular.element(a.controlsTpl),m.controlsEl.append(m.inputEl),"no"!==m.buttons&&(m.buttonsEl=angular.element(a.buttonsTpl),m.submitEl=angular.element(a.submitTpl),m.cancelEl=angular.element(a.cancelTpl),m.buttonsEl.append(m.submitEl).append(m.cancelEl),m.controlsEl.append(m.buttonsEl),m.inputEl.addClass("editable-has-buttons")),m.errorEl=angular.element(a.errorTpl),m.controlsEl.append(m.errorEl),m.editorEl=angular.element(m.single?a.formTpl:a.noformTpl),m.editorEl.append(m.controlsEl);for(var d in c.$attr)if(!(d.length<=1)){var e=!1,f=d.substring(1,2);if("e"===d.substring(0,1)&&f===f.toUpperCase()&&(e=d.substring(1),"Form"!==e&&"NgSubmit"!==e)){e=e.substring(0,1).toLowerCase()+b.camelToDash(e.substring(1));var h=""===c[d]?e:c[d];m.inputEl.attr(e,h)}}m.inputEl.addClass("editable-input"),m.inputEl.attr("ng-model","$data"),m.editorEl.addClass(b.camelToDash(m.directiveName)),m.single&&(m.editorEl.attr("editable-form","$form"),m.editorEl.attr("blur",m.attrs.blur||("no"===m.buttons?"cancel":g.blurElem))),angular.isFunction(a.postrender)&&a.postrender.call(m)},m.setLocalValue=function(){m.scope.$data=m.useCopy?angular.copy(k(a.$parent)):k(a.$parent)},m.show=function(){return m.setLocalValue(),m.render(),d.after(m.editorEl),i(m.editorEl)(a),m.addListeners(),d.addClass("editable-hide"),m.onshow()},m.hide=function(){return m.editorEl.remove(),d.removeClass("editable-hide"),m.onhide()},m.cancel=function(){m.oncancel()},m.addListeners=function(){m.inputEl.bind("keyup",function(a){if(m.single)switch(a.keyCode){case 27:m.scope.$apply(function(){m.scope.$form.$cancel()})}}),m.single&&"no"===m.buttons&&m.autosubmit(),m.editorEl.bind("click",function(a){1===a.which&&m.scope.$form.$visible&&(m.scope.$form._clicked=!0)})},m.setWaiting=function(a){a?(l=!m.inputEl.attr("disabled")&&!m.inputEl.attr("ng-disabled")&&!m.inputEl.attr("ng-enabled"),l&&(m.inputEl.attr("disabled","disabled"),m.buttonsEl&&m.buttonsEl.find("button").attr("disabled","disabled"))):l&&(m.inputEl.removeAttr("disabled"),m.buttonsEl&&m.buttonsEl.find("button").removeAttr("disabled"))},m.activate=function(){setTimeout(function(){var a=m.inputEl[0];"focus"===g.activate&&a.focus&&a.focus(),"select"===g.activate&&a.select&&a.select()},0)},m.setError=function(b){angular.isObject(b)||(a.$error=b,m.error=b)},m.catchError=function(a,b){return angular.isObject(a)&&b!==!0?j.when(a).then(angular.bind(this,function(a){this.catchError(a,!0)}),angular.bind(this,function(a){this.catchError(a,!0)})):b&&angular.isObject(a)&&a.status&&200!==a.status&&a.data&&angular.isString(a.data)?(this.setError(a.data),a=a.data):angular.isString(a)&&this.setError(a),a},m.save=function(){k.assign(a.$parent,angular.copy(m.scope.$data))},m.handleEmpty=function(){var b=k(a.$parent),c=null===b||void 0===b||""===b||angular.isArray(b)&&0===b.length;d.toggleClass("editable-empty",c)},m.autosubmit=angular.noop,m.onshow=angular.noop,m.onhide=angular.noop,m.oncancel=angular.noop,m.onbeforesave=angular.noop,m.onaftersave=angular.noop}return c.$inject=["$scope","$attrs","$element","$parse","editableThemes","editableOptions","$rootScope","$compile","$q"],c}]),angular.module("xeditable").factory("editableDirectiveFactory",["$parse","$compile","editableThemes","$rootScope","$document","editableController","editableFormController",function(a,b,c,d,e,f,g){return function(b){return{restrict:"A",scope:!0,require:[b.directiveName,"?^form"],controller:f,link:function(c,f,h,i){var j,k=i[0],l=!1;if(i[1])j=i[1],l=!0;else if(h.eForm){var m=a(h.eForm)(c);if(m)j=m,l=!0;else for(var n=0;n<e[0].forms.length;n++)if(e[0].forms[n].name===h.eForm){j=null,l=!0;break}}if(angular.forEach(b,function(a,b){void 0!==k[b]&&(k.parent[b]=k[b])}),angular.extend(k,b),k.init(!l),c.$editable=k,f.addClass("editable"),l)if(j){if(c.$form=j,!c.$form.$addEditable)throw"Form with editable elements should have `editable-form` attribute.";c.$form.$addEditable(k)}else d.$$editableBuffer=d.$$editableBuffer||{},d.$$editableBuffer[h.eForm]=d.$$editableBuffer[h.eForm]||[],d.$$editableBuffer[h.eForm].push(k),c.$form=null;else c.$form=g(),c.$form.$addEditable(k),h.eForm&&(c.$parent[h.eForm]=c.$form),h.eForm||(f.addClass("editable-click"),f.bind("click",function(a){a.preventDefault(),a.editable=k,c.$apply(function(){c.$form.$show()})}))}}}}]),angular.module("xeditable").factory("editableFormController",["$parse","$document","$rootScope","editablePromiseCollection","editableUtils",function(a,b,c,d,e){var f=[];b.bind("click",function(a){if(1===a.which){for(var b=[],d=[],e=0;e<f.length;e++)f[e]._clicked?f[e]._clicked=!1:f[e].$waiting||("cancel"===f[e]._blur&&b.push(f[e]),"submit"===f[e]._blur&&d.push(f[e]));(b.length||d.length)&&c.$apply(function(){angular.forEach(b,function(a){a.$cancel()}),angular.forEach(d,function(a){a.$submit()})})}});var g={$addEditable:function(a){this.$editables.push(a),a.elem.bind("$destroy",angular.bind(this,this.$removeEditable,a)),a.scope.$form||(a.scope.$form=this),this.$visible&&a.catchError(a.show())},$removeEditable:function(a){for(var b=0;b<this.$editables.length;b++)if(this.$editables[b]===a)return this.$editables.splice(b,1),void 0},$show:function(){if(!this.$visible){this.$visible=!0;var a=d();a.when(this.$onshow()),this.$setError(null,""),angular.forEach(this.$editables,function(b){a.when(b.show())}),a.then({onWait:angular.bind(this,this.$setWaiting),onTrue:angular.bind(this,this.$activate),onFalse:angular.bind(this,this.$activate),onString:angular.bind(this,this.$activate)}),setTimeout(angular.bind(this,function(){this._clicked=!1,-1===e.indexOf(f,this)&&f.push(this)}),0)}},$activate:function(a){var b;if(this.$editables.length){if(angular.isString(a))for(b=0;b<this.$editables.length;b++)if(this.$editables[b].name===a)return this.$editables[b].activate(),void 0;for(b=0;b<this.$editables.length;b++)if(this.$editables[b].error)return this.$editables[b].activate(),void 0;this.$editables[0].activate()}},$hide:function(){this.$visible&&(this.$visible=!1,this.$onhide(),angular.forEach(this.$editables,function(a){a.hide()}),e.arrayRemove(f,this))},$cancel:function(){this.$visible&&(this.$oncancel(),angular.forEach(this.$editables,function(a){a.cancel()}),this.$hide())},$setWaiting:function(a){this.$waiting=!!a,angular.forEach(this.$editables,function(b){b.setWaiting(!!a)})},$setError:function(a,b){angular.forEach(this.$editables,function(c){a&&c.name!==a||c.setError(b)})},$submit:function(){function a(a){var b=d();b.when(this.$onbeforesave()),b.then({onWait:angular.bind(this,this.$setWaiting),onTrue:a?angular.bind(this,this.$save):angular.bind(this,this.$hide),onFalse:angular.bind(this,this.$hide),onString:angular.bind(this,this.$activate)})}if(!this.$waiting){this.$setError(null,"");var b=d();angular.forEach(this.$editables,function(a){b.when(a.onbeforesave())}),b.then({onWait:angular.bind(this,this.$setWaiting),onTrue:angular.bind(this,a,!0),onFalse:angular.bind(this,a,!1),onString:angular.bind(this,this.$activate)})}},$save:function(){angular.forEach(this.$editables,function(a){a.save()});var a=d();a.when(this.$onaftersave()),angular.forEach(this.$editables,function(b){a.when(b.onaftersave())}),a.then({onWait:angular.bind(this,this.$setWaiting),onTrue:angular.bind(this,this.$hide),onFalse:angular.bind(this,this.$hide),onString:angular.bind(this,this.$activate)})},$onshow:angular.noop,$oncancel:angular.noop,$onhide:angular.noop,$onbeforesave:angular.noop,$onaftersave:angular.noop};return function(){return angular.extend({$editables:[],$visible:!1,$waiting:!1,$data:{},_clicked:!1,_blur:null},g)}}]),angular.module("xeditable").directive("editableForm",["$rootScope","$parse","editableFormController","editableOptions",function(a,b,c,d){return{restrict:"A",require:["form"],compile:function(){return{pre:function(b,d,e,f){var g,h=f[0];e.editableForm?b[e.editableForm]&&b[e.editableForm].$show?(g=b[e.editableForm],angular.extend(h,g)):(g=c(),b[e.editableForm]=g,angular.extend(g,h)):(g=c(),angular.extend(h,g));var i=a.$$editableBuffer,j=h.$name;j&&i&&i[j]&&(angular.forEach(i[j],function(a){g.$addEditable(a)}),delete i[j])},post:function(a,c,e,f){var g;g=e.editableForm&&a[e.editableForm]&&a[e.editableForm].$show?a[e.editableForm]:f[0],e.onshow&&(g.$onshow=angular.bind(g,b(e.onshow),a)),e.onhide&&(g.$onhide=angular.bind(g,b(e.onhide),a)),e.oncancel&&(g.$oncancel=angular.bind(g,b(e.oncancel),a)),e.shown&&b(e.shown)(a)&&g.$show(),g._blur=e.blur||d.blurForm,e.ngSubmit||e.submit||(e.onbeforesave&&(g.$onbeforesave=function(){return b(e.onbeforesave)(a,{$data:g.$data})}),e.onaftersave&&(g.$onaftersave=function(){return b(e.onaftersave)(a,{$data:g.$data})}),c.bind("submit",function(b){b.preventDefault(),a.$apply(function(){g.$submit()})})),c.bind("click",function(a){1===a.which&&g.$visible&&(g._clicked=!0)})}}}}}]),angular.module("xeditable").factory("editablePromiseCollection",["$q",function(a){function b(){return{promises:[],hasFalse:!1,hasString:!1,when:function(b,c){if(b===!1)this.hasFalse=!0;else if(!c&&angular.isObject(b))this.promises.push(a.when(b));else{if(!angular.isString(b))return;this.hasString=!0}},then:function(b){function c(){h.hasString||h.hasFalse?!h.hasString&&h.hasFalse?e():f():d()}b=b||{};var d=b.onTrue||angular.noop,e=b.onFalse||angular.noop,f=b.onString||angular.noop,g=b.onWait||angular.noop,h=this;this.promises.length?(g(!0),a.all(this.promises).then(function(a){g(!1),angular.forEach(a,function(a){h.when(a,!0)}),c()},function(){g(!1),f()})):c()}}}return b}]),angular.module("xeditable").factory("editableUtils",[function(){return{indexOf:function(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0;c<a.length;c++)if(b===a[c])return c;return-1},arrayRemove:function(a,b){var c=this.indexOf(a,b);return c>=0&&a.splice(c,1),b},camelToDash:function(a){var b=/[A-Z]/g;return a.replace(b,function(a,b){return(b?"-":"")+a.toLowerCase()})},dashToCamel:function(a){var b=/([\:\-\_]+(.))/g,c=/^moz([A-Z])/;return a.replace(b,function(a,b,c,d){return d?c.toUpperCase():c}).replace(c,"Moz$1")}}}]),angular.module("xeditable").factory("editableNgOptionsParser",[function(){function a(a){var c;if(!(c=a.match(b)))throw"ng-options parse error";var d,e=c[2]||c[1],f=c[4]||c[6],g=c[5],h=(c[3]||"",c[2]?c[1]:f),i=c[7],j=c[8],k=j?c[8]:null;return void 0===g?(d=f+" in "+i,void 0!==j&&(d+=" track by "+k)):d="("+g+", "+f+") in "+i,{ngRepeat:d,locals:{valueName:f,keyName:g,valueFn:h,displayFn:e}}}var b=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/;return a}]),angular.module("xeditable").factory("editableThemes",function(){var a={"default":{formTpl:'<form class="editable-wrap"></form>',noformTpl:'<span class="editable-wrap"></span>',controlsTpl:'<span class="editable-controls"></span>',inputTpl:"",errorTpl:'<div class="editable-error" ng-show="$error" ng-bind="$error"></div>',buttonsTpl:'<span class="editable-buttons"></span>',submitTpl:'<button type="submit">save</button>',cancelTpl:'<button type="button" ng-click="$form.$cancel()">cancel</button>'},bs2:{formTpl:'<form class="form-inline editable-wrap" role="form"></form>',noformTpl:'<span class="editable-wrap"></span>',controlsTpl:'<div class="editable-controls controls control-group" ng-class="{\'error\': $error}"></div>',inputTpl:"",errorTpl:'<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',buttonsTpl:'<span class="editable-buttons"></span>',submitTpl:'<button type="submit" class="btn btn-primary"><span class="icon-ok icon-white"></span></button>',cancelTpl:'<button type="button" class="btn" ng-click="$form.$cancel()"><span class="icon-remove"></span></button>'},bs3:{formTpl:'<form class="form-inline editable-wrap" role="form"></form>',noformTpl:'<span class="editable-wrap"></span>',controlsTpl:'<div class="editable-controls form-group" ng-class="{\'has-error\': $error}"></div>',inputTpl:"",errorTpl:'<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',buttonsTpl:'<span class="editable-buttons"></span>',submitTpl:'<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-ok"></span></button>',cancelTpl:'<button type="button" class="btn btn-default" ng-click="$form.$cancel()"><span class="glyphicon glyphicon-remove"></span></button>',buttonsClass:"",inputClass:"",postrender:function(){switch(this.directiveName){case"editableText":case"editableSelect":case"editableTextarea":case"editableEmail":case"editableTel":case"editableNumber":case"editableUrl":case"editableSearch":case"editableDate":case"editableDatetime":case"editableTime":case"editableMonth":case"editableWeek":if(this.inputEl.addClass("form-control"),this.theme.inputClass){if(this.inputEl.attr("multiple")&&("input-sm"===this.theme.inputClass||"input-lg"===this.theme.inputClass))break;this.inputEl.addClass(this.theme.inputClass)}}this.buttonsEl&&this.theme.buttonsClass&&this.buttonsEl.find("button").addClass(this.theme.buttonsClass)}}};return a}); angular.module("xeditable", []).value("editableOptions", {
\ No newline at end of file theme: "default",
buttons: "right",
blurElem: "cancel",
blurForm: "ignore",
activate: "focus"
}), angular.module("xeditable").directive("editableBsdate", ["editableDirectiveFactory", function (a) {
return a({directiveName: "editableBsdate", inputTpl: '<input type="text">'})
}]), angular.module("xeditable").directive("editableBstime", ["editableDirectiveFactory", function (a) {
return a({
directiveName: "editableBstime", inputTpl: "<timepicker></timepicker>", render: function () {
this.parent.render.call(this);
var a = angular.element('<div class="well well-small" style="display:inline-block;"></div>');
a.attr("ng-model", this.inputEl.attr("ng-model")), this.inputEl.removeAttr("ng-model"), this.attrs.eNgChange && (a.attr("ng-change", this.inputEl.attr("ng-change")), this.inputEl.removeAttr("ng-change")), this.inputEl.wrap(a)
}
})
}]), angular.module("xeditable").directive("editableCheckbox", ["editableDirectiveFactory", function (a) {
return a({
directiveName: "editableCheckbox", inputTpl: '<input type="checkbox">', render: function () {
this.parent.render.call(this), this.attrs.eTitle && (this.inputEl.wrap("<label></label>"), this.inputEl.after(angular.element("<span></span>").text(this.attrs.eTitle)))
}, autosubmit: function () {
var a = this;
a.inputEl.bind("change", function () {
setTimeout(function () {
a.scope.$apply(function () {
a.scope.$form.$submit()
})
}, 500)
})
}
})
}]), angular.module("xeditable").directive("editableChecklist", ["editableDirectiveFactory", "editableNgOptionsParser", function (a, b) {
return a({
directiveName: "editableChecklist", inputTpl: "<span></span>", useCopy: !0, render: function () {
this.parent.render.call(this);
var a = b(this.attrs.eNgOptions), c = '<label ng-repeat="' + a.ngRepeat + '">' + '<input type="checkbox" checklist-model="$parent.$data" checklist-value="' + a.locals.valueFn + '">' + '<span ng-bind="' + a.locals.displayFn + '"></span></label>';
this.inputEl.removeAttr("ng-model"), this.inputEl.removeAttr("ng-options"), this.inputEl.html(c)
}
})
}]), function () {
var a = "text|email|tel|number|url|search|color|date|datetime|time|month|week".split("|");
angular.forEach(a, function (a) {
var b = "editable" + a.charAt(0).toUpperCase() + a.slice(1);
angular.module("xeditable").directive(b, ["editableDirectiveFactory", function (c) {
return c({directiveName: b, inputTpl: '<input type="' + a + '">'})
}])
}), angular.module("xeditable").directive("editableRange", ["editableDirectiveFactory", function (a) {
return a({
directiveName: "editableRange",
inputTpl: '<input type="range" id="range" name="range">',
render: function () {
this.parent.render.call(this), this.inputEl.after("<output>{{$data}}</output>")
}
})
}])
}(), angular.module("xeditable").directive("editableRadiolist", ["editableDirectiveFactory", "editableNgOptionsParser", function (a, b) {
return a({
directiveName: "editableRadiolist", inputTpl: "<span></span>", render: function () {
this.parent.render.call(this);
var a = b(this.attrs.eNgOptions), c = '<label ng-repeat="' + a.ngRepeat + '">' + '<input type="radio" ng-model="$parent.$data" value="{{' + a.locals.valueFn + '}}">' + '<span ng-bind="' + a.locals.displayFn + '"></span></label>';
this.inputEl.removeAttr("ng-model"), this.inputEl.removeAttr("ng-options"), this.inputEl.html(c)
}, autosubmit: function () {
var a = this;
a.inputEl.bind("change", function () {
setTimeout(function () {
a.scope.$apply(function () {
a.scope.$form.$submit()
})
}, 500)
})
}
})
}]), angular.module("xeditable").directive("editableSelect", ["editableDirectiveFactory", function (a) {
return a({
directiveName: "editableSelect", inputTpl: "<select></select>", autosubmit: function () {
var a = this;
a.inputEl.bind("change", function () {
a.scope.$apply(function () {
a.scope.$form.$submit()
})
})
}
})
}]), angular.module("xeditable").directive("editableTextarea", ["editableDirectiveFactory", function (a) {
return a({
directiveName: "editableTextarea", inputTpl: "<textarea></textarea>", addListeners: function () {
var a = this;
a.parent.addListeners.call(a), a.single && "no" !== a.buttons && a.autosubmit()
}, autosubmit: function () {
var a = this;
a.inputEl.bind("keydown", function (b) {
(b.ctrlKey || b.metaKey) && 13 === b.keyCode && a.scope.$apply(function () {
a.scope.$form.$submit()
})
})
}
})
}]), angular.module("xeditable").factory("editableController", ["$q", "editableUtils", function (a, b) {
function c(a, c, d, e, f, g, h, i, j) {
var k, l, m = this;
m.scope = a, m.elem = d, m.attrs = c, m.inputEl = null, m.editorEl = null, m.single = !0, m.error = "", m.theme = f[g.theme] || f["default"], m.parent = {}, m.inputTpl = "", m.directiveName = "", m.useCopy = !1, m.single = null, m.buttons = "right", m.init = function (b) {
if (m.single = b, m.name = c.eName || c[m.directiveName], !c[m.directiveName])throw"You should provide value for `" + m.directiveName + "` in editable element!";
k = e(c[m.directiveName]), m.buttons = m.single ? m.attrs.buttons || g.buttons : "no", c.eName && m.scope.$watch("$data", function (a) {
m.scope.$form.$data[c.eName] = a
}), c.onshow && (m.onshow = function () {
return m.catchError(e(c.onshow)(a))
}), c.onhide && (m.onhide = function () {
return e(c.onhide)(a)
}), c.oncancel && (m.oncancel = function () {
return e(c.oncancel)(a)
}), c.onbeforesave && (m.onbeforesave = function () {
return m.catchError(e(c.onbeforesave)(a))
}), c.onaftersave && (m.onaftersave = function () {
return m.catchError(e(c.onaftersave)(a))
}), a.$parent.$watch(c[m.directiveName], function () {
m.handleEmpty()
})
}, m.render = function () {
var a = m.theme;
m.inputEl = angular.element(m.inputTpl), m.controlsEl = angular.element(a.controlsTpl), m.controlsEl.append(m.inputEl), "no" !== m.buttons && (m.buttonsEl = angular.element(a.buttonsTpl), m.submitEl = angular.element(a.submitTpl), m.cancelEl = angular.element(a.cancelTpl), m.buttonsEl.append(m.submitEl).append(m.cancelEl), m.controlsEl.append(m.buttonsEl), m.inputEl.addClass("editable-has-buttons")), m.errorEl = angular.element(a.errorTpl), m.controlsEl.append(m.errorEl), m.editorEl = angular.element(m.single ? a.formTpl : a.noformTpl), m.editorEl.append(m.controlsEl);
for (var d in c.$attr)if (!(d.length <= 1)) {
var e = !1, f = d.substring(1, 2);
if ("e" === d.substring(0, 1) && f === f.toUpperCase() && (e = d.substring(1), "Form" !== e && "NgSubmit" !== e)) {
e = e.substring(0, 1).toLowerCase() + b.camelToDash(e.substring(1));
var h = "" === c[d] ? e : c[d];
m.inputEl.attr(e, h)
}
}
m.inputEl.addClass("editable-input"), m.inputEl.attr("ng-model", "$data"), m.editorEl.addClass(b.camelToDash(m.directiveName)), m.single && (m.editorEl.attr("editable-form", "$form"), m.editorEl.attr("blur", m.attrs.blur || ("no" === m.buttons ? "cancel" : g.blurElem))), angular.isFunction(a.postrender) && a.postrender.call(m)
}, m.setLocalValue = function () {
m.scope.$data = m.useCopy ? angular.copy(k(a.$parent)) : k(a.$parent)
}, m.show = function () {
return m.setLocalValue(), m.render(), d.after(m.editorEl), i(m.editorEl)(a), m.addListeners(), d.addClass("editable-hide"), m.onshow()
}, m.hide = function () {
return m.editorEl.remove(), d.removeClass("editable-hide"), m.onhide()
}, m.cancel = function () {
m.oncancel()
}, m.addListeners = function () {
m.inputEl.bind("keyup", function (a) {
if (m.single)switch (a.keyCode) {
case 27:
m.scope.$apply(function () {
m.scope.$form.$cancel()
})
}
}), m.single && "no" === m.buttons && m.autosubmit(), m.editorEl.bind("click", function (a) {
1 === a.which && m.scope.$form.$visible && (m.scope.$form._clicked = !0)
})
}, m.setWaiting = function (a) {
a ? (l = !m.inputEl.attr("disabled") && !m.inputEl.attr("ng-disabled") && !m.inputEl.attr("ng-enabled"), l && (m.inputEl.attr("disabled", "disabled"), m.buttonsEl && m.buttonsEl.find("button").attr("disabled", "disabled"))) : l && (m.inputEl.removeAttr("disabled"), m.buttonsEl && m.buttonsEl.find("button").removeAttr("disabled"))
}, m.activate = function () {
setTimeout(function () {
var a = m.inputEl[0];
"focus" === g.activate && a.focus && a.focus(), "select" === g.activate && a.select && a.select()
}, 0)
}, m.setError = function (b) {
angular.isObject(b) || (a.$error = b, m.error = b)
}, m.catchError = function (a, b) {
return angular.isObject(a) && b !== !0 ? j.when(a).then(angular.bind(this, function (a) {
this.catchError(a, !0)
}), angular.bind(this, function (a) {
this.catchError(a, !0)
})) : b && angular.isObject(a) && a.status && 200 !== a.status && a.data && angular.isString(a.data) ? (this.setError(a.data), a = a.data) : angular.isString(a) && this.setError(a), a
}, m.save = function () {
k.assign(a.$parent, angular.copy(m.scope.$data))
}, m.handleEmpty = function () {
var b = k(a.$parent), c = null === b || void 0 === b || "" === b || angular.isArray(b) && 0 === b.length;
d.toggleClass("editable-empty", c)
}, m.autosubmit = angular.noop, m.onshow = angular.noop, m.onhide = angular.noop, m.oncancel = angular.noop, m.onbeforesave = angular.noop, m.onaftersave = angular.noop
}
return c.$inject = ["$scope", "$attrs", "$element", "$parse", "editableThemes", "editableOptions", "$rootScope", "$compile", "$q"], c
}]), angular.module("xeditable").factory("editableDirectiveFactory", ["$parse", "$compile", "editableThemes", "$rootScope", "$document", "editableController", "editableFormController", function (a, b, c, d, e, f, g) {
return function (b) {
return {
restrict: "A",
scope: !0,
require: [b.directiveName, "?^form"],
controller: f,
link: function (c, f, h, i) {
var j, k = i[0], l = !1;
if (i[1])j = i[1], l = !0; else if (h.eForm) {
var m = a(h.eForm)(c);
if (m)j = m, l = !0; else for (var n = 0; n < e[0].forms.length; n++)if (e[0].forms[n].name === h.eForm) {
j = null, l = !0;
break
}
}
if (angular.forEach(b, function (a, b) {
void 0 !== k[b] && (k.parent[b] = k[b])
}), angular.extend(k, b), k.init(!l), c.$editable = k, f.addClass("editable"), l)if (j) {
if (c.$form = j, !c.$form.$addEditable)throw"Form with editable elements should have `editable-form` attribute.";
c.$form.$addEditable(k)
} else d.$$editableBuffer = d.$$editableBuffer || {}, d.$$editableBuffer[h.eForm] = d.$$editableBuffer[h.eForm] || [], d.$$editableBuffer[h.eForm].push(k), c.$form = null; else c.$form = g(), c.$form.$addEditable(k), h.eForm && (c.$parent[h.eForm] = c.$form), h.eForm || (f.addClass("editable-click"), f.bind("click", function (a) {
a.preventDefault(), a.editable = k, c.$apply(function () {
c.$form.$show()
})
}))
}
}
}
}]), angular.module("xeditable").factory("editableFormController", ["$parse", "$document", "$rootScope", "editablePromiseCollection", "editableUtils", function (a, b, c, d, e) {
var f = [];
b.bind("click", function (a) {
if (1 === a.which) {
for (var b = [], d = [], e = 0; e < f.length; e++)f[e]._clicked ? f[e]._clicked = !1 : f[e].$waiting || ("cancel" === f[e]._blur && b.push(f[e]), "submit" === f[e]._blur && d.push(f[e]));
(b.length || d.length) && c.$apply(function () {
angular.forEach(b, function (a) {
a.$cancel()
}), angular.forEach(d, function (a) {
a.$submit()
})
})
}
});
var g = {
$addEditable: function (a) {
this.$editables.push(a), a.elem.bind("$destroy", angular.bind(this, this.$removeEditable, a)), a.scope.$form || (a.scope.$form = this), this.$visible && a.catchError(a.show())
},
$removeEditable: function (a) {
for (var b = 0; b < this.$editables.length; b++)if (this.$editables[b] === a)return this.$editables.splice(b, 1), void 0
},
$show: function () {
if (!this.$visible) {
this.$visible = !0;
var a = d();
a.when(this.$onshow()), this.$setError(null, ""), angular.forEach(this.$editables, function (b) {
a.when(b.show())
}), a.then({
onWait: angular.bind(this, this.$setWaiting),
onTrue: angular.bind(this, this.$activate),
onFalse: angular.bind(this, this.$activate),
onString: angular.bind(this, this.$activate)
}), setTimeout(angular.bind(this, function () {
this._clicked = !1, -1 === e.indexOf(f, this) && f.push(this)
}), 0)
}
},
$activate: function (a) {
var b;
if (this.$editables.length) {
if (angular.isString(a))for (b = 0; b < this.$editables.length; b++)if (this.$editables[b].name === a)return this.$editables[b].activate(), void 0;
for (b = 0; b < this.$editables.length; b++)if (this.$editables[b].error)return this.$editables[b].activate(), void 0;
this.$editables[0].activate()
}
},
$hide: function () {
this.$visible && (this.$visible = !1, this.$onhide(), angular.forEach(this.$editables, function (a) {
a.hide()
}), e.arrayRemove(f, this))
},
$cancel: function () {
this.$visible && (this.$oncancel(), angular.forEach(this.$editables, function (a) {
a.cancel()
}), this.$hide())
},
$setWaiting: function (a) {
this.$waiting = !!a, angular.forEach(this.$editables, function (b) {
b.setWaiting(!!a)
})
},
$setError: function (a, b) {
angular.forEach(this.$editables, function (c) {
a && c.name !== a || c.setError(b)
})
},
$submit: function () {
function a(a) {
var b = d();
b.when(this.$onbeforesave()), b.then({
onWait: angular.bind(this, this.$setWaiting),
onTrue: a ? angular.bind(this, this.$save) : angular.bind(this, this.$hide),
onFalse: angular.bind(this, this.$hide),
onString: angular.bind(this, this.$activate)
})
}
if (!this.$waiting) {
this.$setError(null, "");
var b = d();
angular.forEach(this.$editables, function (a) {
b.when(a.onbeforesave())
}), b.then({
onWait: angular.bind(this, this.$setWaiting),
onTrue: angular.bind(this, a, !0),
onFalse: angular.bind(this, a, !1),
onString: angular.bind(this, this.$activate)
})
}
},
$save: function () {
angular.forEach(this.$editables, function (a) {
a.save()
});
var a = d();
a.when(this.$onaftersave()), angular.forEach(this.$editables, function (b) {
a.when(b.onaftersave())
}), a.then({
onWait: angular.bind(this, this.$setWaiting),
onTrue: angular.bind(this, this.$hide),
onFalse: angular.bind(this, this.$hide),
onString: angular.bind(this, this.$activate)
})
},
$onshow: angular.noop,
$oncancel: angular.noop,
$onhide: angular.noop,
$onbeforesave: angular.noop,
$onaftersave: angular.noop
};
return function () {
return angular.extend({$editables: [], $visible: !1, $waiting: !1, $data: {}, _clicked: !1, _blur: null}, g)
}
}]), angular.module("xeditable").directive("editableForm", ["$rootScope", "$parse", "editableFormController", "editableOptions", function (a, b, c, d) {
return {
restrict: "A", require: ["form"], compile: function () {
return {
pre: function (b, d, e, f) {
var g, h = f[0];
e.editableForm ? b[e.editableForm] && b[e.editableForm].$show ? (g = b[e.editableForm], angular.extend(h, g)) : (g = c(), b[e.editableForm] = g, angular.extend(g, h)) : (g = c(), angular.extend(h, g));
var i = a.$$editableBuffer, j = h.$name;
j && i && i[j] && (angular.forEach(i[j], function (a) {
g.$addEditable(a)
}), delete i[j])
}, post: function (a, c, e, f) {
var g;
g = e.editableForm && a[e.editableForm] && a[e.editableForm].$show ? a[e.editableForm] : f[0], e.onshow && (g.$onshow = angular.bind(g, b(e.onshow), a)), e.onhide && (g.$onhide = angular.bind(g, b(e.onhide), a)), e.oncancel && (g.$oncancel = angular.bind(g, b(e.oncancel), a)), e.shown && b(e.shown)(a) && g.$show(), g._blur = e.blur || d.blurForm, e.ngSubmit || e.submit || (e.onbeforesave && (g.$onbeforesave = function () {
return b(e.onbeforesave)(a, {$data: g.$data})
}), e.onaftersave && (g.$onaftersave = function () {
return b(e.onaftersave)(a, {$data: g.$data})
}), c.bind("submit", function (b) {
b.preventDefault(), a.$apply(function () {
g.$submit()
})
})), c.bind("click", function (a) {
1 === a.which && g.$visible && (g._clicked = !0)
})
}
}
}
}
}]), angular.module("xeditable").factory("editablePromiseCollection", ["$q", function (a) {
function b() {
return {
promises: [], hasFalse: !1, hasString: !1, when: function (b, c) {
if (b === !1)this.hasFalse = !0; else if (!c && angular.isObject(b))this.promises.push(a.when(b)); else {
if (!angular.isString(b))return;
this.hasString = !0
}
}, then: function (b) {
function c() {
h.hasString || h.hasFalse ? !h.hasString && h.hasFalse ? e() : f() : d()
}
b = b || {};
var d = b.onTrue || angular.noop, e = b.onFalse || angular.noop, f = b.onString || angular.noop, g = b.onWait || angular.noop, h = this;
this.promises.length ? (g(!0), a.all(this.promises).then(function (a) {
g(!1), angular.forEach(a, function (a) {
h.when(a, !0)
}), c()
}, function () {
g(!1), f()
})) : c()
}
}
}
return b
}]), angular.module("xeditable").factory("editableUtils", [function () {
return {
indexOf: function (a, b) {
if (a.indexOf)return a.indexOf(b);
for (var c = 0; c < a.length; c++)if (b === a[c])return c;
return -1
}, arrayRemove: function (a, b) {
var c = this.indexOf(a, b);
return c >= 0 && a.splice(c, 1), b
}, camelToDash: function (a) {
var b = /[A-Z]/g;
return a.replace(b, function (a, b) {
return (b ? "-" : "") + a.toLowerCase()
})
}, dashToCamel: function (a) {
var b = /([\:\-\_]+(.))/g, c = /^moz([A-Z])/;
return a.replace(b, function (a, b, c, d) {
return d ? c.toUpperCase() : c
}).replace(c, "Moz$1")
}
}
}]), angular.module("xeditable").factory("editableNgOptionsParser", [function () {
function a(a) {
var c;
if (!(c = a.match(b)))throw"ng-options parse error";
var d, e = c[2] || c[1], f = c[4] || c[6], g = c[5], h = (c[3] || "", c[2] ? c[1] : f), i = c[7], j = c[8], k = j ? c[8] : null;
return void 0 === g ? (d = f + " in " + i, void 0 !== j && (d += " track by " + k)) : d = "(" + g + ", " + f + ") in " + i, {
ngRepeat: d,
locals: {valueName: f, keyName: g, valueFn: h, displayFn: e}
}
}
var b = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/;
return a
}]), angular.module("xeditable").factory("editableThemes", function () {
var a = {
"default": {
formTpl: '<form class="editable-wrap"></form>',
noformTpl: '<span class="editable-wrap"></span>',
controlsTpl: '<span class="editable-controls"></span>',
inputTpl: "",
errorTpl: '<div class="editable-error" ng-show="$error" ng-bind="$error"></div>',
buttonsTpl: '<span class="editable-buttons"></span>',
submitTpl: '<button type="submit">save</button>',
cancelTpl: '<button type="button" ng-click="$form.$cancel()">cancel</button>'
},
bs2: {
formTpl: '<form class="form-inline editable-wrap" role="form"></form>',
noformTpl: '<span class="editable-wrap"></span>',
controlsTpl: '<div class="editable-controls controls control-group" ng-class="{\'error\': $error}"></div>',
inputTpl: "",
errorTpl: '<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',
buttonsTpl: '<span class="editable-buttons"></span>',
submitTpl: '<button type="submit" class="btn btn-primary"><span class="icon-ok icon-white"></span></button>',
cancelTpl: '<button type="button" class="btn" ng-click="$form.$cancel()"><span class="icon-remove"></span></button>'
},
bs3: {
formTpl: '<form class="form-inline editable-wrap" role="form"></form>',
noformTpl: '<span class="editable-wrap"></span>',
controlsTpl: '<div class="editable-controls form-group" ng-class="{\'has-error\': $error}"></div>',
inputTpl: "",
errorTpl: '<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',
buttonsTpl: '<span class="editable-buttons"></span>',
submitTpl: '<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-ok"></span></button>',
cancelTpl: '<button type="button" class="btn btn-default" ng-click="$form.$cancel()"><span class="glyphicon glyphicon-remove"></span></button>',
buttonsClass: "",
inputClass: "",
postrender: function () {
switch (this.directiveName) {
case"editableText":
case"editableSelect":
case"editableTextarea":
case"editableEmail":
case"editableTel":
case"editableNumber":
case"editableUrl":
case"editableSearch":
case"editableDate":
case"editableDatetime":
case"editableTime":
case"editableMonth":
case"editableWeek":
if (this.inputEl.addClass("form-control"), this.theme.inputClass) {
if (this.inputEl.attr("multiple") && ("input-sm" === this.theme.inputClass || "input-lg" === this.theme.inputClass))break;
this.inputEl.addClass(this.theme.inputClass)
}
}
this.buttonsEl && this.theme.buttonsClass && this.buttonsEl.find("button").addClass(this.theme.buttonsClass)
}
}
};
return a
});
...@@ -11,4 +11,478 @@ ...@@ -11,4 +11,478 @@
* Bootstrap v3.3.5 (http://getbootstrap.com) * Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-default.disabled,.btn-primary.disabled,.btn-success.disabled,.btn-info.disabled,.btn-warning.disabled,.btn-danger.disabled,.btn-default[disabled],.btn-primary[disabled],.btn-success[disabled],.btn-info[disabled],.btn-warning[disabled],.btn-danger[disabled],fieldset[disabled] .btn-default,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-info,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-danger{-webkit-box-shadow:none;box-shadow:none}.btn-default .badge,.btn-primary .badge,.btn-success .badge,.btn-info .badge,.btn-warning .badge,.btn-danger .badge{text-shadow:none}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-o-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#e0e0e0));background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top, #337ab7 0, #265a88 100%);background-image:-o-linear-gradient(top, #337ab7 0, #265a88 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#265a88));background-image:linear-gradient(to bottom, #337ab7 0, #265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#245580}.btn-primary:hover,.btn-primary:focus{background-color:#265a88;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#419641));background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#2aabd2));background-image:linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#eb9316));background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c12e2a));background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-color:#2e6da4}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-o-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#f8f8f8));background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-o-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dbdbdb), to(#e2e2e2));background-image:linear-gradient(to bottom, #dbdbdb 0, #e2e2e2 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-o-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #3c3c3c), to(#222));background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-o-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #080808), to(#0f0f0f));background-image:linear-gradient(to bottom, #080808 0, #0f0f0f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)}}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#c8e5bc));background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#b9def0));background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#f8efc0));background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#e7c3c3));background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ebebeb), to(#f5f5f5));background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #337ab7 0, #286090 100%);background-image:-o-linear-gradient(top, #337ab7 0, #286090 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#286090));background-image:linear-gradient(to bottom, #337ab7 0, #286090 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#449d44));background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#31b0d5));background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#ec971f));background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c9302c));background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top, #337ab7 0, #2b669a 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2b669a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2b669a));background-image:linear-gradient(to bottom, #337ab7 0, #2b669a 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:hover .badge,.list-group-item.active:focus .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#d0e9c6));background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#c4e3f3));background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#faf2cc));background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-o-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#ebcccc));background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #e8e8e8), to(#f5f5f5));background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} */
\ No newline at end of file .btn-default, .btn-primary, .btn-success, .btn-info, .btn-warning, .btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075)
}
.btn-default:active, .btn-primary:active, .btn-success:active, .btn-info:active, .btn-warning:active, .btn-danger:active, .btn-default.active, .btn-primary.active, .btn-success.active, .btn-info.active, .btn-warning.active, .btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125)
}
.btn-default.disabled, .btn-primary.disabled, .btn-success.disabled, .btn-info.disabled, .btn-warning.disabled, .btn-danger.disabled, .btn-default[disabled], .btn-primary[disabled], .btn-success[disabled], .btn-info[disabled], .btn-warning[disabled], .btn-danger[disabled], fieldset[disabled] .btn-default, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-success, fieldset[disabled] .btn-info, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none
}
.btn-default .badge, .btn-primary .badge, .btn-success .badge, .btn-info .badge, .btn-warning .badge, .btn-danger .badge {
text-shadow: none
}
.btn:active, .btn.active {
background-image: none
}
.btn-default {
background-image: -webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc
}
.btn-default:hover, .btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px
}
.btn-default:active, .btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb
}
.btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #245580
}
.btn-primary:hover, .btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px
}
.btn-primary:active, .btn-primary.active {
background-color: #265a88;
border-color: #245580
}
.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #3e8f3e
}
.btn-success:hover, .btn-success:focus {
background-color: #419641;
background-position: 0 -15px
}
.btn-success:active, .btn-success.active {
background-color: #419641;
border-color: #3e8f3e
}
.btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #28a4c9
}
.btn-info:hover, .btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px
}
.btn-info:active, .btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9
}
.btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #e38d13
}
.btn-warning:hover, .btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px
}
.btn-warning:active, .btn-warning.active {
background-color: #eb9316;
border-color: #e38d13
}
.btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
background-repeat: repeat-x;
border-color: #b92c28
}
.btn-danger:hover, .btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px
}
.btn-danger:active, .btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28
}
.btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none
}
.thumbnail, .img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075)
}
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
background-image: -webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-color: #e8e8e8
}
.dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus {
background-image: -webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-color: #2e6da4
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0, #f8f8f8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075)
}
.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0, #e2e2e2 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075)
}
.navbar-brand, .navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25)
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
border-radius: 4px
}
.navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0, #0f0f0f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25)
}
.navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25)
}
.navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom {
border-radius: 0
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a, .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05)
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
border-color: #b2dba1
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
border-color: #9acfea
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
border-color: #f5e79e
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
border-color: #dca7a7
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0, #286090 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0)
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0, #449d44 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0, #c9302c 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent)
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075)
}
.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0, #2b669a 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
border-color: #2b669a
}
.list-group-item.active .badge, .list-group-item.active:hover .badge, .list-group-item.active:focus .badge {
text-shadow: none
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1)
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -8,5 +8,781 @@ ...@@ -8,5 +8,781 @@
* Generated using the Bootstrap Customizer (http://v3.bootcss.com/customize/?id=822d18dc2e4e823a577b) * Generated using the Bootstrap Customizer (http://v3.bootcss.com/customize/?id=822d18dc2e4e823a577b)
* Config saved to config.json and https://gist.github.com/822d18dc2e4e823a577b * Config saved to config.json and https://gist.github.com/822d18dc2e4e823a577b
*/ */
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var i=t(this),n=i.data("bs.alert");n||i.data("bs.alert",n=new o(this)),"string"==typeof e&&n[e].call(i)})}var i='[data-dismiss="alert"]',o=function(e){t(e).on("click",i,this.close)};o.VERSION="3.3.5",o.TRANSITION_DURATION=150,o.prototype.close=function(e){function i(){a.detach().trigger("closed.bs.alert").remove()}var n=t(this),s=n.attr("data-target");s||(s=n.attr("href"),s=s&&s.replace(/.*(?=#[^\s]*$)/,""));var a=t(s);e&&e.preventDefault(),a.length||(a=n.closest(".alert")),a.trigger(e=t.Event("close.bs.alert")),e.isDefaultPrevented()||(a.removeClass("in"),t.support.transition&&a.hasClass("fade")?a.one("bsTransitionEnd",i).emulateTransitionEnd(o.TRANSITION_DURATION):i())};var n=t.fn.alert;t.fn.alert=e,t.fn.alert.Constructor=o,t.fn.alert.noConflict=function(){return t.fn.alert=n,this},t(document).on("click.bs.alert.data-api",i,o.prototype.close)}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.button"),s="object"==typeof e&&e;n||o.data("bs.button",n=new i(this,s)),"toggle"==e?n.toggle():e&&n.setState(e)})}var i=function(e,o){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,o),this.isLoading=!1};i.VERSION="3.3.5",i.DEFAULTS={loadingText:"loading..."},i.prototype.setState=function(e){var i="disabled",o=this.$element,n=o.is("input")?"val":"html",s=o.data();e+="Text",null==s.resetText&&o.data("resetText",o[n]()),setTimeout(t.proxy(function(){o[n](null==s[e]?this.options[e]:s[e]),"loadingText"==e?(this.isLoading=!0,o.addClass(i).attr(i,i)):this.isLoading&&(this.isLoading=!1,o.removeClass(i).removeAttr(i))},this),0)},i.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")?(i.prop("checked")&&(t=!1),e.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==i.prop("type")&&(i.prop("checked")!==this.$element.hasClass("active")&&(t=!1),this.$element.toggleClass("active")),i.prop("checked",this.$element.hasClass("active")),t&&i.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var o=t.fn.button;t.fn.button=e,t.fn.button.Constructor=i,t.fn.button.noConflict=function(){return t.fn.button=o,this},t(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(i){var o=t(i.target);o.hasClass("btn")||(o=o.closest(".btn")),e.call(o,"toggle"),t(i.target).is('input[type="radio"]')||t(i.target).is('input[type="checkbox"]')||i.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(e){t(e.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(e.type))})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.carousel"),s=t.extend({},i.DEFAULTS,o.data(),"object"==typeof e&&e),a="string"==typeof e?e:s.slide;n||o.data("bs.carousel",n=new i(this,s)),"number"==typeof e?n.to(e):a?n[a]():s.interval&&n.pause().cycle()})}var i=function(e,i){this.$element=t(e),this.$indicators=this.$element.find(".carousel-indicators"),this.options=i,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",t.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",t.proxy(this.pause,this)).on("mouseleave.bs.carousel",t.proxy(this.cycle,this))};i.VERSION="3.3.5",i.TRANSITION_DURATION=600,i.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},i.prototype.keydown=function(t){if(!/input|textarea/i.test(t.target.tagName)){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()}},i.prototype.cycle=function(e){return e||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(t.proxy(this.next,this),this.options.interval)),this},i.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},i.prototype.getItemForDirection=function(t,e){var i=this.getItemIndex(e),o="prev"==t&&0===i||"next"==t&&i==this.$items.length-1;if(o&&!this.options.wrap)return e;var n="prev"==t?-1:1,s=(i+n)%this.$items.length;return this.$items.eq(s)},i.prototype.to=function(t){var e=this,i=this.getItemIndex(this.$active=this.$element.find(".item.active"));return t>this.$items.length-1||0>t?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",this.$items.eq(t))},i.prototype.pause=function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&t.support.transition&&(this.$element.trigger(t.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},i.prototype.next=function(){return this.sliding?void 0:this.slide("next")},i.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},i.prototype.slide=function(e,o){var n=this.$element.find(".item.active"),s=o||this.getItemForDirection(e,n),a=this.interval,r="next"==e?"left":"right",l=this;if(s.hasClass("active"))return this.sliding=!1;var h=s[0],d=t.Event("slide.bs.carousel",{relatedTarget:h,direction:r});if(this.$element.trigger(d),!d.isDefaultPrevented()){if(this.sliding=!0,a&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var p=t(this.$indicators.children()[this.getItemIndex(s)]);p&&p.addClass("active")}var c=t.Event("slid.bs.carousel",{relatedTarget:h,direction:r});return t.support.transition&&this.$element.hasClass("slide")?(s.addClass(e),s[0].offsetWidth,n.addClass(r),s.addClass(r),n.one("bsTransitionEnd",function(){s.removeClass([e,r].join(" ")).addClass("active"),n.removeClass(["active",r].join(" ")),l.sliding=!1,setTimeout(function(){l.$element.trigger(c)},0)}).emulateTransitionEnd(i.TRANSITION_DURATION)):(n.removeClass("active"),s.addClass("active"),this.sliding=!1,this.$element.trigger(c)),a&&this.cycle(),this}};var o=t.fn.carousel;t.fn.carousel=e,t.fn.carousel.Constructor=i,t.fn.carousel.noConflict=function(){return t.fn.carousel=o,this};var n=function(i){var o,n=t(this),s=t(n.attr("data-target")||(o=n.attr("href"))&&o.replace(/.*(?=#[^\s]+$)/,""));if(s.hasClass("carousel")){var a=t.extend({},s.data(),n.data()),r=n.attr("data-slide-to");r&&(a.interval=!1),e.call(s,a),r&&s.data("bs.carousel").to(r),i.preventDefault()}};t(document).on("click.bs.carousel.data-api","[data-slide]",n).on("click.bs.carousel.data-api","[data-slide-to]",n),t(window).on("load",function(){t('[data-ride="carousel"]').each(function(){var i=t(this);e.call(i,i.data())})})}(jQuery),+function(t){"use strict";function e(e){var i=e.attr("data-target");i||(i=e.attr("href"),i=i&&/#[A-Za-z]/.test(i)&&i.replace(/.*(?=#[^\s]*$)/,""));var o=i&&t(i);return o&&o.length?o:e.parent()}function i(i){i&&3===i.which||(t(n).remove(),t(s).each(function(){var o=t(this),n=e(o),s={relatedTarget:this};n.hasClass("open")&&(i&&"click"==i.type&&/input|textarea/i.test(i.target.tagName)&&t.contains(n[0],i.target)||(n.trigger(i=t.Event("hide.bs.dropdown",s)),i.isDefaultPrevented()||(o.attr("aria-expanded","false"),n.removeClass("open").trigger("hidden.bs.dropdown",s))))}))}function o(e){return this.each(function(){var i=t(this),o=i.data("bs.dropdown");o||i.data("bs.dropdown",o=new a(this)),"string"==typeof e&&o[e].call(i)})}var n=".dropdown-backdrop",s='[data-toggle="dropdown"]',a=function(e){t(e).on("click.bs.dropdown",this.toggle)};a.VERSION="3.3.5",a.prototype.toggle=function(o){var n=t(this);if(!n.is(".disabled, :disabled")){var s=e(n),a=s.hasClass("open");if(i(),!a){"ontouchstart"in document.documentElement&&!s.closest(".navbar-nav").length&&t(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(t(this)).on("click",i);var r={relatedTarget:this};if(s.trigger(o=t.Event("show.bs.dropdown",r)),o.isDefaultPrevented())return;n.trigger("focus").attr("aria-expanded","true"),s.toggleClass("open").trigger("shown.bs.dropdown",r)}return!1}},a.prototype.keydown=function(i){if(/(38|40|27|32)/.test(i.which)&&!/input|textarea/i.test(i.target.tagName)){var o=t(this);if(i.preventDefault(),i.stopPropagation(),!o.is(".disabled, :disabled")){var n=e(o),a=n.hasClass("open");if(!a&&27!=i.which||a&&27==i.which)return 27==i.which&&n.find(s).trigger("focus"),o.trigger("click");var r=" li:not(.disabled):visible a",l=n.find(".dropdown-menu"+r);if(l.length){var h=l.index(i.target);38==i.which&&h>0&&h--,40==i.which&&h<l.length-1&&h++,~h||(h=0),l.eq(h).trigger("focus")}}}};var r=t.fn.dropdown;t.fn.dropdown=o,t.fn.dropdown.Constructor=a,t.fn.dropdown.noConflict=function(){return t.fn.dropdown=r,this},t(document).on("click.bs.dropdown.data-api",i).on("click.bs.dropdown.data-api",".dropdown form",function(t){t.stopPropagation()}).on("click.bs.dropdown.data-api",s,a.prototype.toggle).on("keydown.bs.dropdown.data-api",s,a.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",a.prototype.keydown)}(jQuery),+function(t){"use strict";function e(e,o){return this.each(function(){var n=t(this),s=n.data("bs.modal"),a=t.extend({},i.DEFAULTS,n.data(),"object"==typeof e&&e);s||n.data("bs.modal",s=new i(this,a)),"string"==typeof e?s[e](o):a.show&&s.show(o)})}var i=function(e,i){this.options=i,this.$body=t(document.body),this.$element=t(e),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,t.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};i.VERSION="3.3.5",i.TRANSITION_DURATION=300,i.BACKDROP_TRANSITION_DURATION=150,i.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},i.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},i.prototype.show=function(e){var o=this,n=t.Event("show.bs.modal",{relatedTarget:e});this.$element.trigger(n),this.isShown||n.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',t.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){o.$element.one("mouseup.dismiss.bs.modal",function(e){t(e.target).is(o.$element)&&(o.ignoreBackdropClick=!0)})}),this.backdrop(function(){var n=t.support.transition&&o.$element.hasClass("fade");o.$element.parent().length||o.$element.appendTo(o.$body),o.$element.show().scrollTop(0),o.adjustDialog(),n&&o.$element[0].offsetWidth,o.$element.addClass("in"),o.enforceFocus();var s=t.Event("shown.bs.modal",{relatedTarget:e});n?o.$dialog.one("bsTransitionEnd",function(){o.$element.trigger("focus").trigger(s)}).emulateTransitionEnd(i.TRANSITION_DURATION):o.$element.trigger("focus").trigger(s)}))},i.prototype.hide=function(e){e&&e.preventDefault(),e=t.Event("hide.bs.modal"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),t(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),t.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",t.proxy(this.hideModal,this)).emulateTransitionEnd(i.TRANSITION_DURATION):this.hideModal())},i.prototype.enforceFocus=function(){t(document).off("focusin.bs.modal").on("focusin.bs.modal",t.proxy(function(t){this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},i.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",t.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},i.prototype.resize=function(){this.isShown?t(window).on("resize.bs.modal",t.proxy(this.handleUpdate,this)):t(window).off("resize.bs.modal")},i.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},i.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},i.prototype.backdrop=function(e){var o=this,n=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var s=t.support.transition&&n;if(this.$backdrop=t(document.createElement("div")).addClass("modal-backdrop "+n).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",t.proxy(function(t){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),s&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!e)return;s?this.$backdrop.one("bsTransitionEnd",e).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION):e()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var a=function(){o.removeBackdrop(),e&&e()};t.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",a).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION):a()}else e&&e()},i.prototype.handleUpdate=function(){this.adjustDialog()},i.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},i.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},i.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},i.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",t+this.scrollbarWidth)},i.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},i.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var o=t.fn.modal;t.fn.modal=e,t.fn.modal.Constructor=i,t.fn.modal.noConflict=function(){return t.fn.modal=o,this},t(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(i){var o=t(this),n=o.attr("href"),s=t(o.attr("data-target")||n&&n.replace(/.*(?=#[^\s]+$)/,"")),a=s.data("bs.modal")?"toggle":t.extend({remote:!/#/.test(n)&&n},s.data(),o.data());o.is("a")&&i.preventDefault(),s.one("show.bs.modal",function(t){t.isDefaultPrevented()||s.one("hidden.bs.modal",function(){o.is(":visible")&&o.trigger("focus")})}),e.call(s,a,this)})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.tooltip"),s="object"==typeof e&&e;(n||!/destroy|hide/.test(e))&&(n||o.data("bs.tooltip",n=new i(this,s)),"string"==typeof e&&n[e]())})}var i=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};i.VERSION="3.3.5",i.TRANSITION_DURATION=150,i.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},i.prototype.init=function(e,i,o){if(this.enabled=!0,this.type=e,this.$element=t(i),this.options=this.getOptions(o),this.$viewport=this.options.viewport&&t(t.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var n=this.options.trigger.split(" "),s=n.length;s--;){var a=n[s];if("click"==a)this.$element.on("click."+this.type,this.options.selector,t.proxy(this.toggle,this));else if("manual"!=a){var r="hover"==a?"mouseenter":"focusin",l="hover"==a?"mouseleave":"focusout";this.$element.on(r+"."+this.type,this.options.selector,t.proxy(this.enter,this)),this.$element.on(l+"."+this.type,this.options.selector,t.proxy(this.leave,this))}}this.options.selector?this._options=t.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},i.prototype.getDefaults=function(){return i.DEFAULTS},i.prototype.getOptions=function(e){return e=t.extend({},this.getDefaults(),this.$element.data(),e),e.delay&&"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),e},i.prototype.getDelegateOptions=function(){var e={},i=this.getDefaults();return this._options&&t.each(this._options,function(t,o){i[t]!=o&&(e[t]=o)}),e},i.prototype.enter=function(e){var i=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return i||(i=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,i)),e instanceof t.Event&&(i.inState["focusin"==e.type?"focus":"hover"]=!0),i.tip().hasClass("in")||"in"==i.hoverState?void(i.hoverState="in"):(clearTimeout(i.timeout),i.hoverState="in",i.options.delay&&i.options.delay.show?void(i.timeout=setTimeout(function(){"in"==i.hoverState&&i.show()},i.options.delay.show)):i.show())},i.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},i.prototype.leave=function(e){var i=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return i||(i=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,i)),e instanceof t.Event&&(i.inState["focusout"==e.type?"focus":"hover"]=!1),i.isInStateTrue()?void 0:(clearTimeout(i.timeout),i.hoverState="out",i.options.delay&&i.options.delay.hide?void(i.timeout=setTimeout(function(){"out"==i.hoverState&&i.hide()},i.options.delay.hide)):i.hide())},i.prototype.show=function(){var e=t.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(e);var o=t.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(e.isDefaultPrevented()||!o)return;var n=this,s=this.tip(),a=this.getUID(this.type);this.setContent(),s.attr("id",a),this.$element.attr("aria-describedby",a),this.options.animation&&s.addClass("fade");var r="function"==typeof this.options.placement?this.options.placement.call(this,s[0],this.$element[0]):this.options.placement,l=/\s?auto?\s?/i,h=l.test(r);h&&(r=r.replace(l,"")||"top"),s.detach().css({top:0,left:0,display:"block"}).addClass(r).data("bs."+this.type,this),this.options.container?s.appendTo(this.options.container):s.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var d=this.getPosition(),p=s[0].offsetWidth,c=s[0].offsetHeight;if(h){var f=r,u=this.getPosition(this.$viewport);r="bottom"==r&&d.bottom+c>u.bottom?"top":"top"==r&&d.top-c<u.top?"bottom":"right"==r&&d.right+p>u.width?"left":"left"==r&&d.left-p<u.left?"right":r,s.removeClass(f).addClass(r)}var g=this.getCalculatedOffset(r,d,p,c);this.applyPlacement(g,r);var m=function(){var t=n.hoverState;n.$element.trigger("shown.bs."+n.type),n.hoverState=null,"out"==t&&n.leave(n)};t.support.transition&&this.$tip.hasClass("fade")?s.one("bsTransitionEnd",m).emulateTransitionEnd(i.TRANSITION_DURATION):m()}},i.prototype.applyPlacement=function(e,i){var o=this.tip(),n=o[0].offsetWidth,s=o[0].offsetHeight,a=parseInt(o.css("margin-top"),10),r=parseInt(o.css("margin-left"),10);isNaN(a)&&(a=0),isNaN(r)&&(r=0),e.top+=a,e.left+=r,t.offset.setOffset(o[0],t.extend({using:function(t){o.css({top:Math.round(t.top),left:Math.round(t.left)})}},e),0),o.addClass("in");var l=o[0].offsetWidth,h=o[0].offsetHeight;"top"==i&&h!=s&&(e.top=e.top+s-h);var d=this.getViewportAdjustedDelta(i,e,l,h);d.left?e.left+=d.left:e.top+=d.top;var p=/top|bottom/.test(i),c=p?2*d.left-n+l:2*d.top-s+h,f=p?"offsetWidth":"offsetHeight";o.offset(e),this.replaceArrow(c,o[0][f],p)},i.prototype.replaceArrow=function(t,e,i){this.arrow().css(i?"left":"top",50*(1-t/e)+"%").css(i?"top":"left","")},i.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();t.find(".tooltip-inner")[this.options.html?"html":"text"](e),t.removeClass("fade in top bottom left right")},i.prototype.hide=function(e){function o(){"in"!=n.hoverState&&s.detach(),n.$element.removeAttr("aria-describedby").trigger("hidden.bs."+n.type),e&&e()}var n=this,s=t(this.$tip),a=t.Event("hide.bs."+this.type);return this.$element.trigger(a),a.isDefaultPrevented()?void 0:(s.removeClass("in"),t.support.transition&&s.hasClass("fade")?s.one("bsTransitionEnd",o).emulateTransitionEnd(i.TRANSITION_DURATION):o(),this.hoverState=null,this)},i.prototype.fixTitle=function(){var t=this.$element;(t.attr("title")||"string"!=typeof t.attr("data-original-title"))&&t.attr("data-original-title",t.attr("title")||"").attr("title","")},i.prototype.hasContent=function(){return this.getTitle()},i.prototype.getPosition=function(e){e=e||this.$element;var i=e[0],o="BODY"==i.tagName,n=i.getBoundingClientRect();null==n.width&&(n=t.extend({},n,{width:n.right-n.left,height:n.bottom-n.top}));var s=o?{top:0,left:0}:e.offset(),a={scroll:o?document.documentElement.scrollTop||document.body.scrollTop:e.scrollTop()},r=o?{width:t(window).width(),height:t(window).height()}:null;return t.extend({},n,a,r,s)},i.prototype.getCalculatedOffset=function(t,e,i,o){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-i/2}:"top"==t?{top:e.top-o,left:e.left+e.width/2-i/2}:"left"==t?{top:e.top+e.height/2-o/2,left:e.left-i}:{top:e.top+e.height/2-o/2,left:e.left+e.width}},i.prototype.getViewportAdjustedDelta=function(t,e,i,o){var n={top:0,left:0};if(!this.$viewport)return n;var s=this.options.viewport&&this.options.viewport.padding||0,a=this.getPosition(this.$viewport);if(/right|left/.test(t)){var r=e.top-s-a.scroll,l=e.top+s-a.scroll+o;r<a.top?n.top=a.top-r:l>a.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;h<a.left?n.left=a.left-h:d>a.right&&(n.left=a.left+a.width-d)}return n},i.prototype.getTitle=function(){var t,e=this.$element,i=this.options;return t=e.attr("data-original-title")||("function"==typeof i.title?i.title.call(e[0]):i.title)},i.prototype.getUID=function(t){do t+=~~(1e6*Math.random());while(document.getElementById(t));return t},i.prototype.tip=function(){if(!this.$tip&&(this.$tip=t(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},i.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},i.prototype.enable=function(){this.enabled=!0},i.prototype.disable=function(){this.enabled=!1},i.prototype.toggleEnabled=function(){this.enabled=!this.enabled},i.prototype.toggle=function(e){var i=this;e&&(i=t(e.currentTarget).data("bs."+this.type),i||(i=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,i))),e?(i.inState.click=!i.inState.click,i.isInStateTrue()?i.enter(i):i.leave(i)):i.tip().hasClass("in")?i.leave(i):i.enter(i)},i.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null})};var o=t.fn.tooltip;t.fn.tooltip=e,t.fn.tooltip.Constructor=i,t.fn.tooltip.noConflict=function(){return t.fn.tooltip=o,this}}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.popover"),s="object"==typeof e&&e;(n||!/destroy|hide/.test(e))&&(n||o.data("bs.popover",n=new i(this,s)),"string"==typeof e&&n[e]())})}var i=function(t,e){this.init("popover",t,e)};if(!t.fn.tooltip)throw new Error("Popover requires tooltip.js");i.VERSION="3.3.5",i.DEFAULTS=t.extend({},t.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),i.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),i.prototype.constructor=i,i.prototype.getDefaults=function(){return i.DEFAULTS},i.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof i?"html":"append":"text"](i),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},i.prototype.hasContent=function(){return this.getTitle()||this.getContent()},i.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},i.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var o=t.fn.popover;t.fn.popover=e,t.fn.popover.Constructor=i,t.fn.popover.noConflict=function(){return t.fn.popover=o,this}}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.tab");n||o.data("bs.tab",n=new i(this)),"string"==typeof e&&n[e]()})}var i=function(e){this.element=t(e)};i.VERSION="3.3.5",i.TRANSITION_DURATION=150,i.prototype.show=function(){var e=this.element,i=e.closest("ul:not(.dropdown-menu)"),o=e.data("target");if(o||(o=e.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),!e.parent("li").hasClass("active")){var n=i.find(".active:last a"),s=t.Event("hide.bs.tab",{relatedTarget:e[0]}),a=t.Event("show.bs.tab",{relatedTarget:n[0]});if(n.trigger(s),e.trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){var r=t(o);this.activate(e.closest("li"),i),this.activate(r,r.parent(),function(){n.trigger({type:"hidden.bs.tab",relatedTarget:e[0]}),e.trigger({type:"shown.bs.tab",relatedTarget:n[0]})})}}},i.prototype.activate=function(e,o,n){function s(){a.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),e.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),r?(e[0].offsetWidth,e.addClass("in")):e.removeClass("fade"),e.parent(".dropdown-menu").length&&e.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),n&&n()}var a=o.find("> .active"),r=n&&t.support.transition&&(a.length&&a.hasClass("fade")||!!o.find("> .fade").length);a.length&&r?a.one("bsTransitionEnd",s).emulateTransitionEnd(i.TRANSITION_DURATION):s(),a.removeClass("in")};var o=t.fn.tab;t.fn.tab=e,t.fn.tab.Constructor=i,t.fn.tab.noConflict=function(){return t.fn.tab=o,this};var n=function(i){i.preventDefault(),e.call(t(this),"show")};t(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',n).on("click.bs.tab.data-api",'[data-toggle="pill"]',n)}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),n=o.data("bs.affix"),s="object"==typeof e&&e;n||o.data("bs.affix",n=new i(this,s)),"string"==typeof e&&n[e]()})}var i=function(e,o){this.options=t.extend({},i.DEFAULTS,o),this.$target=t(this.options.target).on("scroll.bs.affix.data-api",t.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",t.proxy(this.checkPositionWithEventLoop,this)),this.$element=t(e),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};i.VERSION="3.3.5",i.RESET="affix affix-top affix-bottom",i.DEFAULTS={offset:0,target:window},i.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return i>n?"top":!1;if("bottom"==this.affixed)return null!=i?n+this.unpin<=s.top?!1:"bottom":t-o>=n+a?!1:"bottom";var r=null==this.affixed,l=r?n:s.top,h=r?a:e;return null!=i&&i>=n?"top":null!=o&&l+h>=t-o?"bottom":!1},i.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(i.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},i.prototype.checkPositionWithEventLoop=function(){setTimeout(t.proxy(this.checkPosition,this),1)},i.prototype.checkPosition=function(){if(this.$element.is(":visible")){var e=this.$element.height(),o=this.options.offset,n=o.top,s=o.bottom,a=Math.max(t(document).height(),t(document.body).height());"object"!=typeof o&&(s=n=o),"function"==typeof n&&(n=o.top(this.$element)),"function"==typeof s&&(s=o.bottom(this.$element));var r=this.getState(a,e,n,s);if(this.affixed!=r){null!=this.unpin&&this.$element.css("top","");var l="affix"+(r?"-"+r:""),h=t.Event(l+".bs.affix");if(this.$element.trigger(h),h.isDefaultPrevented())return;this.affixed=r,this.unpin="bottom"==r?this.getPinnedOffset():null,this.$element.removeClass(i.RESET).addClass(l).trigger(l.replace("affix","affixed")+".bs.affix")}"bottom"==r&&this.$element.offset({top:a-e-s})}};var o=t.fn.affix;t.fn.affix=e,t.fn.affix.Constructor=i,t.fn.affix.noConflict=function(){return t.fn.affix=o,this},t(window).on("load",function(){t('[data-spy="affix"]').each(function(){var i=t(this),o=i.data();o.offset=o.offset||{},null!=o.offsetBottom&&(o.offset.bottom=o.offsetBottom),null!=o.offsetTop&&(o.offset.top=o.offsetTop),e.call(i,o)})})}(jQuery),+function(t){"use strict";function e(e){var i,o=e.attr("data-target")||(i=e.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"");return t(o)}function i(e){return this.each(function(){var i=t(this),n=i.data("bs.collapse"),s=t.extend({},o.DEFAULTS,i.data(),"object"==typeof e&&e);!n&&s.toggle&&/show|hide/.test(e)&&(s.toggle=!1),n||i.data("bs.collapse",n=new o(this,s)),"string"==typeof e&&n[e]()})}var o=function(e,i){this.$element=t(e),this.options=t.extend({},o.DEFAULTS,i),this.$trigger=t('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};o.VERSION="3.3.5",o.TRANSITION_DURATION=350,o.DEFAULTS={toggle:!0},o.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},o.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var e,n=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(n&&n.length&&(e=n.data("bs.collapse"),e&&e.transitioning))){var s=t.Event("show.bs.collapse");if(this.$element.trigger(s),!s.isDefaultPrevented()){n&&n.length&&(i.call(n,"hide"),e||n.data("bs.collapse",null));var a=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[a](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var r=function(){this.$element.removeClass("collapsing").addClass("collapse in")[a](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!t.support.transition)return r.call(this);var l=t.camelCase(["scroll",a].join("-"));this.$element.one("bsTransitionEnd",t.proxy(r,this)).emulateTransitionEnd(o.TRANSITION_DURATION)[a](this.$element[0][l]); if ("undefined" == typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");
}}}},o.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var e=t.Event("hide.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){var i=this.dimension();this.$element[i](this.$element[i]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var n=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return t.support.transition?void this.$element[i](0).one("bsTransitionEnd",t.proxy(n,this)).emulateTransitionEnd(o.TRANSITION_DURATION):n.call(this)}}},o.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},o.prototype.getParent=function(){return t(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(t.proxy(function(i,o){var n=t(o);this.addAriaAndCollapsedClass(e(n),n)},this)).end()},o.prototype.addAriaAndCollapsedClass=function(t,e){var i=t.hasClass("in");t.attr("aria-expanded",i),e.toggleClass("collapsed",!i).attr("aria-expanded",i)};var n=t.fn.collapse;t.fn.collapse=i,t.fn.collapse.Constructor=o,t.fn.collapse.noConflict=function(){return t.fn.collapse=n,this},t(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(o){var n=t(this);n.attr("data-target")||o.preventDefault();var s=e(n),a=s.data("bs.collapse"),r=a?"toggle":n.data();i.call(s,r)})}(jQuery),+function(t){"use strict";function e(i,o){this.$body=t(document.body),this.$scrollElement=t(t(i).is(document.body)?window:i),this.options=t.extend({},e.DEFAULTS,o),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",t.proxy(this.process,this)),this.refresh(),this.process()}function i(i){return this.each(function(){var o=t(this),n=o.data("bs.scrollspy"),s="object"==typeof i&&i;n||o.data("bs.scrollspy",n=new e(this,s)),"string"==typeof i&&n[i]()})}e.VERSION="3.3.5",e.DEFAULTS={offset:10},e.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},e.prototype.refresh=function(){var e=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),t.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var e=t(this),n=e.data("target")||e.attr("href"),s=/^#./.test(n)&&t(n);return s&&s.length&&s.is(":visible")&&[[s[i]().top+o,n]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){e.offsets.push(this[0]),e.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),e>=o)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e<n[0])return this.activeTarget=null,this.clear();for(t=n.length;t--;)a!=s[t]&&e>=n[t]&&(void 0===n[t+1]||e<n[t+1])&&this.activate(s[t])},e.prototype.activate=function(e){this.activeTarget=e,this.clear();var i=this.selector+'[data-target="'+e+'"],'+this.selector+'[href="'+e+'"]',o=t(i).parents("li").addClass("active");o.parent(".dropdown-menu").length&&(o=o.closest("li.dropdown").addClass("active")),o.trigger("activate.bs.scrollspy")},e.prototype.clear=function(){t(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var o=t.fn.scrollspy;t.fn.scrollspy=i,t.fn.scrollspy.Constructor=e,t.fn.scrollspy.noConflict=function(){return t.fn.scrollspy=o,this},t(window).on("load.bs.scrollspy.data-api",function(){t('[data-spy="scroll"]').each(function(){var e=t(this);i.call(e,e.data())})})}(jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var i in e)if(void 0!==t.style[i])return{end:e[i]};return!1}t.fn.emulateTransitionEnd=function(e){var i=!1,o=this;t(this).one("bsTransitionEnd",function(){i=!0});var n=function(){i||t(o).trigger(t.support.transition.end)};return setTimeout(n,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){return t(e.target).is(this)?e.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery); +function (t) {
\ No newline at end of file "use strict";
var e = t.fn.jquery.split(" ")[0].split(".");
if (e[0] < 2 && e[1] < 9 || 1 == e[0] && 9 == e[1] && e[2] < 1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var i = t(this), n = i.data("bs.alert");
n || i.data("bs.alert", n = new o(this)), "string" == typeof e && n[e].call(i)
})
}
var i = '[data-dismiss="alert"]', o = function (e) {
t(e).on("click", i, this.close)
};
o.VERSION = "3.3.5", o.TRANSITION_DURATION = 150, o.prototype.close = function (e) {
function i() {
a.detach().trigger("closed.bs.alert").remove()
}
var n = t(this), s = n.attr("data-target");
s || (s = n.attr("href"), s = s && s.replace(/.*(?=#[^\s]*$)/, ""));
var a = t(s);
e && e.preventDefault(), a.length || (a = n.closest(".alert")), a.trigger(e = t.Event("close.bs.alert")), e.isDefaultPrevented() || (a.removeClass("in"), t.support.transition && a.hasClass("fade") ? a.one("bsTransitionEnd", i).emulateTransitionEnd(o.TRANSITION_DURATION) : i())
};
var n = t.fn.alert;
t.fn.alert = e, t.fn.alert.Constructor = o, t.fn.alert.noConflict = function () {
return t.fn.alert = n, this
}, t(document).on("click.bs.alert.data-api", i, o.prototype.close)
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.button"), s = "object" == typeof e && e;
n || o.data("bs.button", n = new i(this, s)), "toggle" == e ? n.toggle() : e && n.setState(e)
})
}
var i = function (e, o) {
this.$element = t(e), this.options = t.extend({}, i.DEFAULTS, o), this.isLoading = !1
};
i.VERSION = "3.3.5", i.DEFAULTS = {loadingText: "loading..."}, i.prototype.setState = function (e) {
var i = "disabled", o = this.$element, n = o.is("input") ? "val" : "html", s = o.data();
e += "Text", null == s.resetText && o.data("resetText", o[n]()), setTimeout(t.proxy(function () {
o[n](null == s[e] ? this.options[e] : s[e]), "loadingText" == e ? (this.isLoading = !0, o.addClass(i).attr(i, i)) : this.isLoading && (this.isLoading = !1, o.removeClass(i).removeAttr(i))
}, this), 0)
}, i.prototype.toggle = function () {
var t = !0, e = this.$element.closest('[data-toggle="buttons"]');
if (e.length) {
var i = this.$element.find("input");
"radio" == i.prop("type") ? (i.prop("checked") && (t = !1), e.find(".active").removeClass("active"), this.$element.addClass("active")) : "checkbox" == i.prop("type") && (i.prop("checked") !== this.$element.hasClass("active") && (t = !1), this.$element.toggleClass("active")), i.prop("checked", this.$element.hasClass("active")), t && i.trigger("change")
} else this.$element.attr("aria-pressed", !this.$element.hasClass("active")), this.$element.toggleClass("active")
};
var o = t.fn.button;
t.fn.button = e, t.fn.button.Constructor = i, t.fn.button.noConflict = function () {
return t.fn.button = o, this
}, t(document).on("click.bs.button.data-api", '[data-toggle^="button"]', function (i) {
var o = t(i.target);
o.hasClass("btn") || (o = o.closest(".btn")), e.call(o, "toggle"), t(i.target).is('input[type="radio"]') || t(i.target).is('input[type="checkbox"]') || i.preventDefault()
}).on("focus.bs.button.data-api blur.bs.button.data-api", '[data-toggle^="button"]', function (e) {
t(e.target).closest(".btn").toggleClass("focus", /^focus(in)?$/.test(e.type))
})
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.carousel"), s = t.extend({}, i.DEFAULTS, o.data(), "object" == typeof e && e), a = "string" == typeof e ? e : s.slide;
n || o.data("bs.carousel", n = new i(this, s)), "number" == typeof e ? n.to(e) : a ? n[a]() : s.interval && n.pause().cycle()
})
}
var i = function (e, i) {
this.$element = t(e), this.$indicators = this.$element.find(".carousel-indicators"), this.options = i, this.paused = null, this.sliding = null, this.interval = null, this.$active = null, this.$items = null, this.options.keyboard && this.$element.on("keydown.bs.carousel", t.proxy(this.keydown, this)), "hover" == this.options.pause && !("ontouchstart" in document.documentElement) && this.$element.on("mouseenter.bs.carousel", t.proxy(this.pause, this)).on("mouseleave.bs.carousel", t.proxy(this.cycle, this))
};
i.VERSION = "3.3.5", i.TRANSITION_DURATION = 600, i.DEFAULTS = {
interval: 5e3,
pause: "hover",
wrap: !0,
keyboard: !0
}, i.prototype.keydown = function (t) {
if (!/input|textarea/i.test(t.target.tagName)) {
switch (t.which) {
case 37:
this.prev();
break;
case 39:
this.next();
break;
default:
return
}
t.preventDefault()
}
}, i.prototype.cycle = function (e) {
return e || (this.paused = !1), this.interval && clearInterval(this.interval), this.options.interval && !this.paused && (this.interval = setInterval(t.proxy(this.next, this), this.options.interval)), this
}, i.prototype.getItemIndex = function (t) {
return this.$items = t.parent().children(".item"), this.$items.index(t || this.$active)
}, i.prototype.getItemForDirection = function (t, e) {
var i = this.getItemIndex(e), o = "prev" == t && 0 === i || "next" == t && i == this.$items.length - 1;
if (o && !this.options.wrap)return e;
var n = "prev" == t ? -1 : 1, s = (i + n) % this.$items.length;
return this.$items.eq(s)
}, i.prototype.to = function (t) {
var e = this, i = this.getItemIndex(this.$active = this.$element.find(".item.active"));
return t > this.$items.length - 1 || 0 > t ? void 0 : this.sliding ? this.$element.one("slid.bs.carousel", function () {
e.to(t)
}) : i == t ? this.pause().cycle() : this.slide(t > i ? "next" : "prev", this.$items.eq(t))
}, i.prototype.pause = function (e) {
return e || (this.paused = !0), this.$element.find(".next, .prev").length && t.support.transition && (this.$element.trigger(t.support.transition.end), this.cycle(!0)), this.interval = clearInterval(this.interval), this
}, i.prototype.next = function () {
return this.sliding ? void 0 : this.slide("next")
}, i.prototype.prev = function () {
return this.sliding ? void 0 : this.slide("prev")
}, i.prototype.slide = function (e, o) {
var n = this.$element.find(".item.active"), s = o || this.getItemForDirection(e, n), a = this.interval, r = "next" == e ? "left" : "right", l = this;
if (s.hasClass("active"))return this.sliding = !1;
var h = s[0], d = t.Event("slide.bs.carousel", {relatedTarget: h, direction: r});
if (this.$element.trigger(d), !d.isDefaultPrevented()) {
if (this.sliding = !0, a && this.pause(), this.$indicators.length) {
this.$indicators.find(".active").removeClass("active");
var p = t(this.$indicators.children()[this.getItemIndex(s)]);
p && p.addClass("active")
}
var c = t.Event("slid.bs.carousel", {relatedTarget: h, direction: r});
return t.support.transition && this.$element.hasClass("slide") ? (s.addClass(e), s[0].offsetWidth, n.addClass(r), s.addClass(r), n.one("bsTransitionEnd", function () {
s.removeClass([e, r].join(" ")).addClass("active"), n.removeClass(["active", r].join(" ")), l.sliding = !1, setTimeout(function () {
l.$element.trigger(c)
}, 0)
}).emulateTransitionEnd(i.TRANSITION_DURATION)) : (n.removeClass("active"), s.addClass("active"), this.sliding = !1, this.$element.trigger(c)), a && this.cycle(), this
}
};
var o = t.fn.carousel;
t.fn.carousel = e, t.fn.carousel.Constructor = i, t.fn.carousel.noConflict = function () {
return t.fn.carousel = o, this
};
var n = function (i) {
var o, n = t(this), s = t(n.attr("data-target") || (o = n.attr("href")) && o.replace(/.*(?=#[^\s]+$)/, ""));
if (s.hasClass("carousel")) {
var a = t.extend({}, s.data(), n.data()), r = n.attr("data-slide-to");
r && (a.interval = !1), e.call(s, a), r && s.data("bs.carousel").to(r), i.preventDefault()
}
};
t(document).on("click.bs.carousel.data-api", "[data-slide]", n).on("click.bs.carousel.data-api", "[data-slide-to]", n), t(window).on("load", function () {
t('[data-ride="carousel"]').each(function () {
var i = t(this);
e.call(i, i.data())
})
})
}(jQuery), +function (t) {
"use strict";
function e(e) {
var i = e.attr("data-target");
i || (i = e.attr("href"), i = i && /#[A-Za-z]/.test(i) && i.replace(/.*(?=#[^\s]*$)/, ""));
var o = i && t(i);
return o && o.length ? o : e.parent()
}
function i(i) {
i && 3 === i.which || (t(n).remove(), t(s).each(function () {
var o = t(this), n = e(o), s = {relatedTarget: this};
n.hasClass("open") && (i && "click" == i.type && /input|textarea/i.test(i.target.tagName) && t.contains(n[0], i.target) || (n.trigger(i = t.Event("hide.bs.dropdown", s)), i.isDefaultPrevented() || (o.attr("aria-expanded", "false"), n.removeClass("open").trigger("hidden.bs.dropdown", s))))
}))
}
function o(e) {
return this.each(function () {
var i = t(this), o = i.data("bs.dropdown");
o || i.data("bs.dropdown", o = new a(this)), "string" == typeof e && o[e].call(i)
})
}
var n = ".dropdown-backdrop", s = '[data-toggle="dropdown"]', a = function (e) {
t(e).on("click.bs.dropdown", this.toggle)
};
a.VERSION = "3.3.5", a.prototype.toggle = function (o) {
var n = t(this);
if (!n.is(".disabled, :disabled")) {
var s = e(n), a = s.hasClass("open");
if (i(), !a) {
"ontouchstart" in document.documentElement && !s.closest(".navbar-nav").length && t(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(t(this)).on("click", i);
var r = {relatedTarget: this};
if (s.trigger(o = t.Event("show.bs.dropdown", r)), o.isDefaultPrevented())return;
n.trigger("focus").attr("aria-expanded", "true"), s.toggleClass("open").trigger("shown.bs.dropdown", r)
}
return !1
}
}, a.prototype.keydown = function (i) {
if (/(38|40|27|32)/.test(i.which) && !/input|textarea/i.test(i.target.tagName)) {
var o = t(this);
if (i.preventDefault(), i.stopPropagation(), !o.is(".disabled, :disabled")) {
var n = e(o), a = n.hasClass("open");
if (!a && 27 != i.which || a && 27 == i.which)return 27 == i.which && n.find(s).trigger("focus"), o.trigger("click");
var r = " li:not(.disabled):visible a", l = n.find(".dropdown-menu" + r);
if (l.length) {
var h = l.index(i.target);
38 == i.which && h > 0 && h--, 40 == i.which && h < l.length - 1 && h++, ~h || (h = 0), l.eq(h).trigger("focus")
}
}
}
};
var r = t.fn.dropdown;
t.fn.dropdown = o, t.fn.dropdown.Constructor = a, t.fn.dropdown.noConflict = function () {
return t.fn.dropdown = r, this
}, t(document).on("click.bs.dropdown.data-api", i).on("click.bs.dropdown.data-api", ".dropdown form", function (t) {
t.stopPropagation()
}).on("click.bs.dropdown.data-api", s, a.prototype.toggle).on("keydown.bs.dropdown.data-api", s, a.prototype.keydown).on("keydown.bs.dropdown.data-api", ".dropdown-menu", a.prototype.keydown)
}(jQuery), +function (t) {
"use strict";
function e(e, o) {
return this.each(function () {
var n = t(this), s = n.data("bs.modal"), a = t.extend({}, i.DEFAULTS, n.data(), "object" == typeof e && e);
s || n.data("bs.modal", s = new i(this, a)), "string" == typeof e ? s[e](o) : a.show && s.show(o)
})
}
var i = function (e, i) {
this.options = i, this.$body = t(document.body), this.$element = t(e), this.$dialog = this.$element.find(".modal-dialog"), this.$backdrop = null, this.isShown = null, this.originalBodyPad = null, this.scrollbarWidth = 0, this.ignoreBackdropClick = !1, this.options.remote && this.$element.find(".modal-content").load(this.options.remote, t.proxy(function () {
this.$element.trigger("loaded.bs.modal")
}, this))
};
i.VERSION = "3.3.5", i.TRANSITION_DURATION = 300, i.BACKDROP_TRANSITION_DURATION = 150, i.DEFAULTS = {
backdrop: !0,
keyboard: !0,
show: !0
}, i.prototype.toggle = function (t) {
return this.isShown ? this.hide() : this.show(t)
}, i.prototype.show = function (e) {
var o = this, n = t.Event("show.bs.modal", {relatedTarget: e});
this.$element.trigger(n), this.isShown || n.isDefaultPrevented() || (this.isShown = !0, this.checkScrollbar(), this.setScrollbar(), this.$body.addClass("modal-open"), this.escape(), this.resize(), this.$element.on("click.dismiss.bs.modal", '[data-dismiss="modal"]', t.proxy(this.hide, this)), this.$dialog.on("mousedown.dismiss.bs.modal", function () {
o.$element.one("mouseup.dismiss.bs.modal", function (e) {
t(e.target).is(o.$element) && (o.ignoreBackdropClick = !0)
})
}), this.backdrop(function () {
var n = t.support.transition && o.$element.hasClass("fade");
o.$element.parent().length || o.$element.appendTo(o.$body), o.$element.show().scrollTop(0), o.adjustDialog(), n && o.$element[0].offsetWidth, o.$element.addClass("in"), o.enforceFocus();
var s = t.Event("shown.bs.modal", {relatedTarget: e});
n ? o.$dialog.one("bsTransitionEnd", function () {
o.$element.trigger("focus").trigger(s)
}).emulateTransitionEnd(i.TRANSITION_DURATION) : o.$element.trigger("focus").trigger(s)
}))
}, i.prototype.hide = function (e) {
e && e.preventDefault(), e = t.Event("hide.bs.modal"), this.$element.trigger(e), this.isShown && !e.isDefaultPrevented() && (this.isShown = !1, this.escape(), this.resize(), t(document).off("focusin.bs.modal"), this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"), this.$dialog.off("mousedown.dismiss.bs.modal"), t.support.transition && this.$element.hasClass("fade") ? this.$element.one("bsTransitionEnd", t.proxy(this.hideModal, this)).emulateTransitionEnd(i.TRANSITION_DURATION) : this.hideModal())
}, i.prototype.enforceFocus = function () {
t(document).off("focusin.bs.modal").on("focusin.bs.modal", t.proxy(function (t) {
this.$element[0] === t.target || this.$element.has(t.target).length || this.$element.trigger("focus")
}, this))
}, i.prototype.escape = function () {
this.isShown && this.options.keyboard ? this.$element.on("keydown.dismiss.bs.modal", t.proxy(function (t) {
27 == t.which && this.hide()
}, this)) : this.isShown || this.$element.off("keydown.dismiss.bs.modal")
}, i.prototype.resize = function () {
this.isShown ? t(window).on("resize.bs.modal", t.proxy(this.handleUpdate, this)) : t(window).off("resize.bs.modal")
}, i.prototype.hideModal = function () {
var t = this;
this.$element.hide(), this.backdrop(function () {
t.$body.removeClass("modal-open"), t.resetAdjustments(), t.resetScrollbar(), t.$element.trigger("hidden.bs.modal")
})
}, i.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove(), this.$backdrop = null
}, i.prototype.backdrop = function (e) {
var o = this, n = this.$element.hasClass("fade") ? "fade" : "";
if (this.isShown && this.options.backdrop) {
var s = t.support.transition && n;
if (this.$backdrop = t(document.createElement("div")).addClass("modal-backdrop " + n).appendTo(this.$body), this.$element.on("click.dismiss.bs.modal", t.proxy(function (t) {
return this.ignoreBackdropClick ? void(this.ignoreBackdropClick = !1) : void(t.target === t.currentTarget && ("static" == this.options.backdrop ? this.$element[0].focus() : this.hide()))
}, this)), s && this.$backdrop[0].offsetWidth, this.$backdrop.addClass("in"), !e)return;
s ? this.$backdrop.one("bsTransitionEnd", e).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION) : e()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass("in");
var a = function () {
o.removeBackdrop(), e && e()
};
t.support.transition && this.$element.hasClass("fade") ? this.$backdrop.one("bsTransitionEnd", a).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION) : a()
} else e && e()
}, i.prototype.handleUpdate = function () {
this.adjustDialog()
}, i.prototype.adjustDialog = function () {
var t = this.$element[0].scrollHeight > document.documentElement.clientHeight;
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && t ? this.scrollbarWidth : "",
paddingRight: this.bodyIsOverflowing && !t ? this.scrollbarWidth : ""
})
}, i.prototype.resetAdjustments = function () {
this.$element.css({paddingLeft: "", paddingRight: ""})
}, i.prototype.checkScrollbar = function () {
var t = window.innerWidth;
if (!t) {
var e = document.documentElement.getBoundingClientRect();
t = e.right - Math.abs(e.left)
}
this.bodyIsOverflowing = document.body.clientWidth < t, this.scrollbarWidth = this.measureScrollbar()
}, i.prototype.setScrollbar = function () {
var t = parseInt(this.$body.css("padding-right") || 0, 10);
this.originalBodyPad = document.body.style.paddingRight || "", this.bodyIsOverflowing && this.$body.css("padding-right", t + this.scrollbarWidth)
}, i.prototype.resetScrollbar = function () {
this.$body.css("padding-right", this.originalBodyPad)
}, i.prototype.measureScrollbar = function () {
var t = document.createElement("div");
t.className = "modal-scrollbar-measure", this.$body.append(t);
var e = t.offsetWidth - t.clientWidth;
return this.$body[0].removeChild(t), e
};
var o = t.fn.modal;
t.fn.modal = e, t.fn.modal.Constructor = i, t.fn.modal.noConflict = function () {
return t.fn.modal = o, this
}, t(document).on("click.bs.modal.data-api", '[data-toggle="modal"]', function (i) {
var o = t(this), n = o.attr("href"), s = t(o.attr("data-target") || n && n.replace(/.*(?=#[^\s]+$)/, "")), a = s.data("bs.modal") ? "toggle" : t.extend({remote: !/#/.test(n) && n}, s.data(), o.data());
o.is("a") && i.preventDefault(), s.one("show.bs.modal", function (t) {
t.isDefaultPrevented() || s.one("hidden.bs.modal", function () {
o.is(":visible") && o.trigger("focus")
})
}), e.call(s, a, this)
})
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.tooltip"), s = "object" == typeof e && e;
(n || !/destroy|hide/.test(e)) && (n || o.data("bs.tooltip", n = new i(this, s)), "string" == typeof e && n[e]())
})
}
var i = function (t, e) {
this.type = null, this.options = null, this.enabled = null, this.timeout = null, this.hoverState = null, this.$element = null, this.inState = null, this.init("tooltip", t, e)
};
i.VERSION = "3.3.5", i.TRANSITION_DURATION = 150, i.DEFAULTS = {
animation: !0,
placement: "top",
selector: !1,
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
trigger: "hover focus",
title: "",
delay: 0,
html: !1,
container: !1,
viewport: {selector: "body", padding: 0}
}, i.prototype.init = function (e, i, o) {
if (this.enabled = !0, this.type = e, this.$element = t(i), this.options = this.getOptions(o), this.$viewport = this.options.viewport && t(t.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : this.options.viewport.selector || this.options.viewport), this.inState = {
click: !1,
hover: !1,
focus: !1
}, this.$element[0] instanceof document.constructor && !this.options.selector)throw new Error("`selector` option must be specified when initializing " + this.type + " on the window.document object!");
for (var n = this.options.trigger.split(" "), s = n.length; s--;) {
var a = n[s];
if ("click" == a)this.$element.on("click." + this.type, this.options.selector, t.proxy(this.toggle, this)); else if ("manual" != a) {
var r = "hover" == a ? "mouseenter" : "focusin", l = "hover" == a ? "mouseleave" : "focusout";
this.$element.on(r + "." + this.type, this.options.selector, t.proxy(this.enter, this)), this.$element.on(l + "." + this.type, this.options.selector, t.proxy(this.leave, this))
}
}
this.options.selector ? this._options = t.extend({}, this.options, {
trigger: "manual",
selector: ""
}) : this.fixTitle()
}, i.prototype.getDefaults = function () {
return i.DEFAULTS
}, i.prototype.getOptions = function (e) {
return e = t.extend({}, this.getDefaults(), this.$element.data(), e), e.delay && "number" == typeof e.delay && (e.delay = {
show: e.delay,
hide: e.delay
}), e
}, i.prototype.getDelegateOptions = function () {
var e = {}, i = this.getDefaults();
return this._options && t.each(this._options, function (t, o) {
i[t] != o && (e[t] = o)
}), e
}, i.prototype.enter = function (e) {
var i = e instanceof this.constructor ? e : t(e.currentTarget).data("bs." + this.type);
return i || (i = new this.constructor(e.currentTarget, this.getDelegateOptions()), t(e.currentTarget).data("bs." + this.type, i)), e instanceof t.Event && (i.inState["focusin" == e.type ? "focus" : "hover"] = !0), i.tip().hasClass("in") || "in" == i.hoverState ? void(i.hoverState = "in") : (clearTimeout(i.timeout), i.hoverState = "in", i.options.delay && i.options.delay.show ? void(i.timeout = setTimeout(function () {
"in" == i.hoverState && i.show()
}, i.options.delay.show)) : i.show())
}, i.prototype.isInStateTrue = function () {
for (var t in this.inState)if (this.inState[t])return !0;
return !1
}, i.prototype.leave = function (e) {
var i = e instanceof this.constructor ? e : t(e.currentTarget).data("bs." + this.type);
return i || (i = new this.constructor(e.currentTarget, this.getDelegateOptions()), t(e.currentTarget).data("bs." + this.type, i)), e instanceof t.Event && (i.inState["focusout" == e.type ? "focus" : "hover"] = !1), i.isInStateTrue() ? void 0 : (clearTimeout(i.timeout), i.hoverState = "out", i.options.delay && i.options.delay.hide ? void(i.timeout = setTimeout(function () {
"out" == i.hoverState && i.hide()
}, i.options.delay.hide)) : i.hide())
}, i.prototype.show = function () {
var e = t.Event("show.bs." + this.type);
if (this.hasContent() && this.enabled) {
this.$element.trigger(e);
var o = t.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]);
if (e.isDefaultPrevented() || !o)return;
var n = this, s = this.tip(), a = this.getUID(this.type);
this.setContent(), s.attr("id", a), this.$element.attr("aria-describedby", a), this.options.animation && s.addClass("fade");
var r = "function" == typeof this.options.placement ? this.options.placement.call(this, s[0], this.$element[0]) : this.options.placement, l = /\s?auto?\s?/i, h = l.test(r);
h && (r = r.replace(l, "") || "top"), s.detach().css({
top: 0,
left: 0,
display: "block"
}).addClass(r).data("bs." + this.type, this), this.options.container ? s.appendTo(this.options.container) : s.insertAfter(this.$element), this.$element.trigger("inserted.bs." + this.type);
var d = this.getPosition(), p = s[0].offsetWidth, c = s[0].offsetHeight;
if (h) {
var f = r, u = this.getPosition(this.$viewport);
r = "bottom" == r && d.bottom + c > u.bottom ? "top" : "top" == r && d.top - c < u.top ? "bottom" : "right" == r && d.right + p > u.width ? "left" : "left" == r && d.left - p < u.left ? "right" : r, s.removeClass(f).addClass(r)
}
var g = this.getCalculatedOffset(r, d, p, c);
this.applyPlacement(g, r);
var m = function () {
var t = n.hoverState;
n.$element.trigger("shown.bs." + n.type), n.hoverState = null, "out" == t && n.leave(n)
};
t.support.transition && this.$tip.hasClass("fade") ? s.one("bsTransitionEnd", m).emulateTransitionEnd(i.TRANSITION_DURATION) : m()
}
}, i.prototype.applyPlacement = function (e, i) {
var o = this.tip(), n = o[0].offsetWidth, s = o[0].offsetHeight, a = parseInt(o.css("margin-top"), 10), r = parseInt(o.css("margin-left"), 10);
isNaN(a) && (a = 0), isNaN(r) && (r = 0), e.top += a, e.left += r, t.offset.setOffset(o[0], t.extend({
using: function (t) {
o.css({top: Math.round(t.top), left: Math.round(t.left)})
}
}, e), 0), o.addClass("in");
var l = o[0].offsetWidth, h = o[0].offsetHeight;
"top" == i && h != s && (e.top = e.top + s - h);
var d = this.getViewportAdjustedDelta(i, e, l, h);
d.left ? e.left += d.left : e.top += d.top;
var p = /top|bottom/.test(i), c = p ? 2 * d.left - n + l : 2 * d.top - s + h, f = p ? "offsetWidth" : "offsetHeight";
o.offset(e), this.replaceArrow(c, o[0][f], p)
}, i.prototype.replaceArrow = function (t, e, i) {
this.arrow().css(i ? "left" : "top", 50 * (1 - t / e) + "%").css(i ? "top" : "left", "")
}, i.prototype.setContent = function () {
var t = this.tip(), e = this.getTitle();
t.find(".tooltip-inner")[this.options.html ? "html" : "text"](e), t.removeClass("fade in top bottom left right")
}, i.prototype.hide = function (e) {
function o() {
"in" != n.hoverState && s.detach(), n.$element.removeAttr("aria-describedby").trigger("hidden.bs." + n.type), e && e()
}
var n = this, s = t(this.$tip), a = t.Event("hide.bs." + this.type);
return this.$element.trigger(a), a.isDefaultPrevented() ? void 0 : (s.removeClass("in"), t.support.transition && s.hasClass("fade") ? s.one("bsTransitionEnd", o).emulateTransitionEnd(i.TRANSITION_DURATION) : o(), this.hoverState = null, this)
}, i.prototype.fixTitle = function () {
var t = this.$element;
(t.attr("title") || "string" != typeof t.attr("data-original-title")) && t.attr("data-original-title", t.attr("title") || "").attr("title", "")
}, i.prototype.hasContent = function () {
return this.getTitle()
}, i.prototype.getPosition = function (e) {
e = e || this.$element;
var i = e[0], o = "BODY" == i.tagName, n = i.getBoundingClientRect();
null == n.width && (n = t.extend({}, n, {width: n.right - n.left, height: n.bottom - n.top}));
var s = o ? {
top: 0,
left: 0
} : e.offset(), a = {scroll: o ? document.documentElement.scrollTop || document.body.scrollTop : e.scrollTop()}, r = o ? {
width: t(window).width(),
height: t(window).height()
} : null;
return t.extend({}, n, a, r, s)
}, i.prototype.getCalculatedOffset = function (t, e, i, o) {
return "bottom" == t ? {
top: e.top + e.height,
left: e.left + e.width / 2 - i / 2
} : "top" == t ? {
top: e.top - o,
left: e.left + e.width / 2 - i / 2
} : "left" == t ? {top: e.top + e.height / 2 - o / 2, left: e.left - i} : {
top: e.top + e.height / 2 - o / 2,
left: e.left + e.width
}
}, i.prototype.getViewportAdjustedDelta = function (t, e, i, o) {
var n = {top: 0, left: 0};
if (!this.$viewport)return n;
var s = this.options.viewport && this.options.viewport.padding || 0, a = this.getPosition(this.$viewport);
if (/right|left/.test(t)) {
var r = e.top - s - a.scroll, l = e.top + s - a.scroll + o;
r < a.top ? n.top = a.top - r : l > a.top + a.height && (n.top = a.top + a.height - l)
} else {
var h = e.left - s, d = e.left + s + i;
h < a.left ? n.left = a.left - h : d > a.right && (n.left = a.left + a.width - d)
}
return n
}, i.prototype.getTitle = function () {
var t, e = this.$element, i = this.options;
return t = e.attr("data-original-title") || ("function" == typeof i.title ? i.title.call(e[0]) : i.title)
}, i.prototype.getUID = function (t) {
do t += ~~(1e6 * Math.random()); while (document.getElementById(t));
return t
}, i.prototype.tip = function () {
if (!this.$tip && (this.$tip = t(this.options.template), 1 != this.$tip.length))throw new Error(this.type + " `template` option must consist of exactly 1 top-level element!");
return this.$tip
}, i.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
}, i.prototype.enable = function () {
this.enabled = !0
}, i.prototype.disable = function () {
this.enabled = !1
}, i.prototype.toggleEnabled = function () {
this.enabled = !this.enabled
}, i.prototype.toggle = function (e) {
var i = this;
e && (i = t(e.currentTarget).data("bs." + this.type), i || (i = new this.constructor(e.currentTarget, this.getDelegateOptions()), t(e.currentTarget).data("bs." + this.type, i))), e ? (i.inState.click = !i.inState.click, i.isInStateTrue() ? i.enter(i) : i.leave(i)) : i.tip().hasClass("in") ? i.leave(i) : i.enter(i)
}, i.prototype.destroy = function () {
var t = this;
clearTimeout(this.timeout), this.hide(function () {
t.$element.off("." + t.type).removeData("bs." + t.type), t.$tip && t.$tip.detach(), t.$tip = null, t.$arrow = null, t.$viewport = null
})
};
var o = t.fn.tooltip;
t.fn.tooltip = e, t.fn.tooltip.Constructor = i, t.fn.tooltip.noConflict = function () {
return t.fn.tooltip = o, this
}
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.popover"), s = "object" == typeof e && e;
(n || !/destroy|hide/.test(e)) && (n || o.data("bs.popover", n = new i(this, s)), "string" == typeof e && n[e]())
})
}
var i = function (t, e) {
this.init("popover", t, e)
};
if (!t.fn.tooltip)throw new Error("Popover requires tooltip.js");
i.VERSION = "3.3.5", i.DEFAULTS = t.extend({}, t.fn.tooltip.Constructor.DEFAULTS, {
placement: "right",
trigger: "click",
content: "",
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
}), i.prototype = t.extend({}, t.fn.tooltip.Constructor.prototype), i.prototype.constructor = i, i.prototype.getDefaults = function () {
return i.DEFAULTS
}, i.prototype.setContent = function () {
var t = this.tip(), e = this.getTitle(), i = this.getContent();
t.find(".popover-title")[this.options.html ? "html" : "text"](e), t.find(".popover-content").children().detach().end()[this.options.html ? "string" == typeof i ? "html" : "append" : "text"](i), t.removeClass("fade top bottom left right in"), t.find(".popover-title").html() || t.find(".popover-title").hide()
}, i.prototype.hasContent = function () {
return this.getTitle() || this.getContent()
}, i.prototype.getContent = function () {
var t = this.$element, e = this.options;
return t.attr("data-content") || ("function" == typeof e.content ? e.content.call(t[0]) : e.content)
}, i.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find(".arrow")
};
var o = t.fn.popover;
t.fn.popover = e, t.fn.popover.Constructor = i, t.fn.popover.noConflict = function () {
return t.fn.popover = o, this
}
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.tab");
n || o.data("bs.tab", n = new i(this)), "string" == typeof e && n[e]()
})
}
var i = function (e) {
this.element = t(e)
};
i.VERSION = "3.3.5", i.TRANSITION_DURATION = 150, i.prototype.show = function () {
var e = this.element, i = e.closest("ul:not(.dropdown-menu)"), o = e.data("target");
if (o || (o = e.attr("href"), o = o && o.replace(/.*(?=#[^\s]*$)/, "")), !e.parent("li").hasClass("active")) {
var n = i.find(".active:last a"), s = t.Event("hide.bs.tab", {relatedTarget: e[0]}), a = t.Event("show.bs.tab", {relatedTarget: n[0]});
if (n.trigger(s), e.trigger(a), !a.isDefaultPrevented() && !s.isDefaultPrevented()) {
var r = t(o);
this.activate(e.closest("li"), i), this.activate(r, r.parent(), function () {
n.trigger({type: "hidden.bs.tab", relatedTarget: e[0]}), e.trigger({
type: "shown.bs.tab",
relatedTarget: n[0]
})
})
}
}
}, i.prototype.activate = function (e, o, n) {
function s() {
a.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !1), e.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded", !0), r ? (e[0].offsetWidth, e.addClass("in")) : e.removeClass("fade"), e.parent(".dropdown-menu").length && e.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !0), n && n()
}
var a = o.find("> .active"), r = n && t.support.transition && (a.length && a.hasClass("fade") || !!o.find("> .fade").length);
a.length && r ? a.one("bsTransitionEnd", s).emulateTransitionEnd(i.TRANSITION_DURATION) : s(), a.removeClass("in")
};
var o = t.fn.tab;
t.fn.tab = e, t.fn.tab.Constructor = i, t.fn.tab.noConflict = function () {
return t.fn.tab = o, this
};
var n = function (i) {
i.preventDefault(), e.call(t(this), "show")
};
t(document).on("click.bs.tab.data-api", '[data-toggle="tab"]', n).on("click.bs.tab.data-api", '[data-toggle="pill"]', n)
}(jQuery), +function (t) {
"use strict";
function e(e) {
return this.each(function () {
var o = t(this), n = o.data("bs.affix"), s = "object" == typeof e && e;
n || o.data("bs.affix", n = new i(this, s)), "string" == typeof e && n[e]()
})
}
var i = function (e, o) {
this.options = t.extend({}, i.DEFAULTS, o), this.$target = t(this.options.target).on("scroll.bs.affix.data-api", t.proxy(this.checkPosition, this)).on("click.bs.affix.data-api", t.proxy(this.checkPositionWithEventLoop, this)), this.$element = t(e), this.affixed = null, this.unpin = null, this.pinnedOffset = null, this.checkPosition()
};
i.VERSION = "3.3.5", i.RESET = "affix affix-top affix-bottom", i.DEFAULTS = {
offset: 0,
target: window
}, i.prototype.getState = function (t, e, i, o) {
var n = this.$target.scrollTop(), s = this.$element.offset(), a = this.$target.height();
if (null != i && "top" == this.affixed)return i > n ? "top" : !1;
if ("bottom" == this.affixed)return null != i ? n + this.unpin <= s.top ? !1 : "bottom" : t - o >= n + a ? !1 : "bottom";
var r = null == this.affixed, l = r ? n : s.top, h = r ? a : e;
return null != i && i >= n ? "top" : null != o && l + h >= t - o ? "bottom" : !1
}, i.prototype.getPinnedOffset = function () {
if (this.pinnedOffset)return this.pinnedOffset;
this.$element.removeClass(i.RESET).addClass("affix");
var t = this.$target.scrollTop(), e = this.$element.offset();
return this.pinnedOffset = e.top - t
}, i.prototype.checkPositionWithEventLoop = function () {
setTimeout(t.proxy(this.checkPosition, this), 1)
}, i.prototype.checkPosition = function () {
if (this.$element.is(":visible")) {
var e = this.$element.height(), o = this.options.offset, n = o.top, s = o.bottom, a = Math.max(t(document).height(), t(document.body).height());
"object" != typeof o && (s = n = o), "function" == typeof n && (n = o.top(this.$element)), "function" == typeof s && (s = o.bottom(this.$element));
var r = this.getState(a, e, n, s);
if (this.affixed != r) {
null != this.unpin && this.$element.css("top", "");
var l = "affix" + (r ? "-" + r : ""), h = t.Event(l + ".bs.affix");
if (this.$element.trigger(h), h.isDefaultPrevented())return;
this.affixed = r, this.unpin = "bottom" == r ? this.getPinnedOffset() : null, this.$element.removeClass(i.RESET).addClass(l).trigger(l.replace("affix", "affixed") + ".bs.affix")
}
"bottom" == r && this.$element.offset({top: a - e - s})
}
};
var o = t.fn.affix;
t.fn.affix = e, t.fn.affix.Constructor = i, t.fn.affix.noConflict = function () {
return t.fn.affix = o, this
}, t(window).on("load", function () {
t('[data-spy="affix"]').each(function () {
var i = t(this), o = i.data();
o.offset = o.offset || {}, null != o.offsetBottom && (o.offset.bottom = o.offsetBottom), null != o.offsetTop && (o.offset.top = o.offsetTop), e.call(i, o)
})
})
}(jQuery), +function (t) {
"use strict";
function e(e) {
var i, o = e.attr("data-target") || (i = e.attr("href")) && i.replace(/.*(?=#[^\s]+$)/, "");
return t(o)
}
function i(e) {
return this.each(function () {
var i = t(this), n = i.data("bs.collapse"), s = t.extend({}, o.DEFAULTS, i.data(), "object" == typeof e && e);
!n && s.toggle && /show|hide/.test(e) && (s.toggle = !1), n || i.data("bs.collapse", n = new o(this, s)), "string" == typeof e && n[e]()
})
}
var o = function (e, i) {
this.$element = t(e), this.options = t.extend({}, o.DEFAULTS, i), this.$trigger = t('[data-toggle="collapse"][href="#' + e.id + '"],[data-toggle="collapse"][data-target="#' + e.id + '"]'), this.transitioning = null, this.options.parent ? this.$parent = this.getParent() : this.addAriaAndCollapsedClass(this.$element, this.$trigger), this.options.toggle && this.toggle()
};
o.VERSION = "3.3.5", o.TRANSITION_DURATION = 350, o.DEFAULTS = {toggle: !0}, o.prototype.dimension = function () {
var t = this.$element.hasClass("width");
return t ? "width" : "height"
}, o.prototype.show = function () {
if (!this.transitioning && !this.$element.hasClass("in")) {
var e, n = this.$parent && this.$parent.children(".panel").children(".in, .collapsing");
if (!(n && n.length && (e = n.data("bs.collapse"), e && e.transitioning))) {
var s = t.Event("show.bs.collapse");
if (this.$element.trigger(s), !s.isDefaultPrevented()) {
n && n.length && (i.call(n, "hide"), e || n.data("bs.collapse", null));
var a = this.dimension();
this.$element.removeClass("collapse").addClass("collapsing")[a](0).attr("aria-expanded", !0), this.$trigger.removeClass("collapsed").attr("aria-expanded", !0), this.transitioning = 1;
var r = function () {
this.$element.removeClass("collapsing").addClass("collapse in")[a](""), this.transitioning = 0, this.$element.trigger("shown.bs.collapse")
};
if (!t.support.transition)return r.call(this);
var l = t.camelCase(["scroll", a].join("-"));
this.$element.one("bsTransitionEnd", t.proxy(r, this)).emulateTransitionEnd(o.TRANSITION_DURATION)[a](this.$element[0][l]);
}
}
}
}, o.prototype.hide = function () {
if (!this.transitioning && this.$element.hasClass("in")) {
var e = t.Event("hide.bs.collapse");
if (this.$element.trigger(e), !e.isDefaultPrevented()) {
var i = this.dimension();
this.$element[i](this.$element[i]())[0].offsetHeight, this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded", !1), this.$trigger.addClass("collapsed").attr("aria-expanded", !1), this.transitioning = 1;
var n = function () {
this.transitioning = 0, this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")
};
return t.support.transition ? void this.$element[i](0).one("bsTransitionEnd", t.proxy(n, this)).emulateTransitionEnd(o.TRANSITION_DURATION) : n.call(this)
}
}
}, o.prototype.toggle = function () {
this[this.$element.hasClass("in") ? "hide" : "show"]()
}, o.prototype.getParent = function () {
return t(this.options.parent).find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]').each(t.proxy(function (i, o) {
var n = t(o);
this.addAriaAndCollapsedClass(e(n), n)
}, this)).end()
}, o.prototype.addAriaAndCollapsedClass = function (t, e) {
var i = t.hasClass("in");
t.attr("aria-expanded", i), e.toggleClass("collapsed", !i).attr("aria-expanded", i)
};
var n = t.fn.collapse;
t.fn.collapse = i, t.fn.collapse.Constructor = o, t.fn.collapse.noConflict = function () {
return t.fn.collapse = n, this
}, t(document).on("click.bs.collapse.data-api", '[data-toggle="collapse"]', function (o) {
var n = t(this);
n.attr("data-target") || o.preventDefault();
var s = e(n), a = s.data("bs.collapse"), r = a ? "toggle" : n.data();
i.call(s, r)
})
}(jQuery), +function (t) {
"use strict";
function e(i, o) {
this.$body = t(document.body), this.$scrollElement = t(t(i).is(document.body) ? window : i), this.options = t.extend({}, e.DEFAULTS, o), this.selector = (this.options.target || "") + " .nav li > a", this.offsets = [], this.targets = [], this.activeTarget = null, this.scrollHeight = 0, this.$scrollElement.on("scroll.bs.scrollspy", t.proxy(this.process, this)), this.refresh(), this.process()
}
function i(i) {
return this.each(function () {
var o = t(this), n = o.data("bs.scrollspy"), s = "object" == typeof i && i;
n || o.data("bs.scrollspy", n = new e(this, s)), "string" == typeof i && n[i]()
})
}
e.VERSION = "3.3.5", e.DEFAULTS = {offset: 10}, e.prototype.getScrollHeight = function () {
return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
}, e.prototype.refresh = function () {
var e = this, i = "offset", o = 0;
this.offsets = [], this.targets = [], this.scrollHeight = this.getScrollHeight(), t.isWindow(this.$scrollElement[0]) || (i = "position", o = this.$scrollElement.scrollTop()), this.$body.find(this.selector).map(function () {
var e = t(this), n = e.data("target") || e.attr("href"), s = /^#./.test(n) && t(n);
return s && s.length && s.is(":visible") && [[s[i]().top + o, n]] || null
}).sort(function (t, e) {
return t[0] - e[0]
}).each(function () {
e.offsets.push(this[0]), e.targets.push(this[1])
})
}, e.prototype.process = function () {
var t, e = this.$scrollElement.scrollTop() + this.options.offset, i = this.getScrollHeight(), o = this.options.offset + i - this.$scrollElement.height(), n = this.offsets, s = this.targets, a = this.activeTarget;
if (this.scrollHeight != i && this.refresh(), e >= o)return a != (t = s[s.length - 1]) && this.activate(t);
if (a && e < n[0])return this.activeTarget = null, this.clear();
for (t = n.length; t--;)a != s[t] && e >= n[t] && (void 0 === n[t + 1] || e < n[t + 1]) && this.activate(s[t])
}, e.prototype.activate = function (e) {
this.activeTarget = e, this.clear();
var i = this.selector + '[data-target="' + e + '"],' + this.selector + '[href="' + e + '"]', o = t(i).parents("li").addClass("active");
o.parent(".dropdown-menu").length && (o = o.closest("li.dropdown").addClass("active")), o.trigger("activate.bs.scrollspy")
}, e.prototype.clear = function () {
t(this.selector).parentsUntil(this.options.target, ".active").removeClass("active")
};
var o = t.fn.scrollspy;
t.fn.scrollspy = i, t.fn.scrollspy.Constructor = e, t.fn.scrollspy.noConflict = function () {
return t.fn.scrollspy = o, this
}, t(window).on("load.bs.scrollspy.data-api", function () {
t('[data-spy="scroll"]').each(function () {
var e = t(this);
i.call(e, e.data())
})
})
}(jQuery), +function (t) {
"use strict";
function e() {
var t = document.createElement("bootstrap"), e = {
WebkitTransition: "webkitTransitionEnd",
MozTransition: "transitionend",
OTransition: "oTransitionEnd otransitionend",
transition: "transitionend"
};
for (var i in e)if (void 0 !== t.style[i])return {end: e[i]};
return !1
}
t.fn.emulateTransitionEnd = function (e) {
var i = !1, o = this;
t(this).one("bsTransitionEnd", function () {
i = !0
});
var n = function () {
i || t(o).trigger(t.support.transition.end)
};
return setTimeout(n, e), this
}, t(function () {
t.support.transition = e(), t.support.transition && (t.event.special.bsTransitionEnd = {
bindType: t.support.transition.end,
delegateType: t.support.transition.end,
handle: function (e) {
return t(e.target).is(this) ? e.handleObj.handler.apply(this, arguments) : void 0
}
})
})
}(jQuery);
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="app" ng-controller="AppPageController"> <div class="app" ng-controller="AppPageController">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li >{{breadcrumb.project}}</li> <li>{{breadcrumb.project}}</li>
<li>{{breadcrumb.nav}}</li> <li>{{breadcrumb.nav}}</li>
<li ng-show="breadcrumb.env">{{breadcrumb.env}}</li> <li ng-show="breadcrumb.env">{{breadcrumb.env}}</li>
<!--<li>1.0</li>--> <!--<li>1.0</li>-->
......
<div class="footer"> <div class="footer">
<p class="text-center"> <p class="text-center">
<span class="glyphicon glyphicon-copyright-mark" aria-hidden="true"></span>携程 框架研发部<br> <span class="glyphicon glyphicon-copyright-mark" aria-hidden="true"></span>携程 框架研发部<br>
......
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label"><font style="color: red">*</font> 应用ID</label> <label class="col-sm-2 control-label"><font style="color: red">*</font> 应用ID</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" name="appAppId" ng-model="app.appId" required> <input type="text" class="form-control" name="appAppId" ng-model="app.appId"
required>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -38,7 +39,7 @@ ...@@ -38,7 +39,7 @@
<input type="text" class="form-control" name="appName" ng-model="app.name" required> <input type="text" class="form-control" name="appName" ng-model="app.name" required>
</div> </div>
</div> </div>
<!--接入sso之后将会去除--> <!--接入sso之后将会去除-->
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label">Owner</label> <label class="col-sm-2 control-label">Owner</label>
<div class="col-sm-3"> <div class="col-sm-3">
...@@ -48,7 +49,8 @@ ...@@ -48,7 +49,8 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label">手机号</label> <label class="col-sm-2 control-label">手机号</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" name="appOwnerPhone" ng-model="app.ownerPhone"> <input type="text" class="form-control" name="appOwnerPhone"
ng-model="app.ownerPhone">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
......
package com.ctrip.apollo.portal; package com.ctrip.apollo.portal;
import com.ctrip.apollo.portal.controller.AppControllerTest;
import com.ctrip.apollo.portal.repository.AppRepositoryTest;
import com.ctrip.apollo.portal.service.ConfigServiceTest; import com.ctrip.apollo.portal.service.ConfigServiceTest;
import com.ctrip.apollo.portal.service.PrivilegeServiceTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import com.ctrip.apollo.portal.controller.AppControllerTest;
import com.ctrip.apollo.portal.repository.AppRepositoryTest;
import com.ctrip.apollo.portal.service.PrivilegeServiceTest;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({AppControllerTest.class, AppRepositoryTest.class, PrivilegeServiceTest.class, ConfigServiceTest.class @SuiteClasses({AppControllerTest.class, AppRepositoryTest.class, PrivilegeServiceTest.class,
ConfigServiceTest.class
}) })
public class AllTests { public class AllTests {
......
package com.ctrip.apollo.portal.controller; package com.ctrip.apollo.portal.controller;
import java.net.URI; import com.ctrip.apollo.portal.AbstractPortalTest;
import java.net.URISyntaxException; import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.repository.AppRepository;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
...@@ -13,9 +14,8 @@ import org.springframework.http.HttpStatus; ...@@ -13,9 +14,8 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.portal.AbstractPortalTest; import java.net.URI;
import com.ctrip.apollo.portal.entity.App; import java.net.URISyntaxException;
import com.ctrip.apollo.portal.repository.AppRepository;
@WebIntegrationTest @WebIntegrationTest
......
package com.ctrip.apollo.portal.repository; package com.ctrip.apollo.portal.repository;
import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
public class AppRepositoryTest extends AbstractPortalTest{ public class AppRepositoryTest extends AbstractPortalTest {
@Autowired @Autowired
AppRepository repository; AppRepository repository;
......
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import com.ctrip.apollo.core.dto.*; import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.RestUtils; import com.ctrip.apollo.portal.RestUtils;
import com.ctrip.apollo.portal.constants.PortalConstants; import com.ctrip.apollo.portal.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO; import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.service.impl.ConfigServiceImpl; import com.ctrip.apollo.portal.service.impl.ConfigServiceImpl;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -18,226 +22,236 @@ import org.springframework.test.util.ReflectionTestUtils; ...@@ -18,226 +22,236 @@ import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.*; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest { public class ConfigServiceTest {
@Mock @Mock
private RestTemplate restTemplate; private RestTemplate restTemplate;
@Mock @Mock
private ResponseEntity releaseSnapShotResponse; private ResponseEntity releaseSnapShotResponse;
@Mock @Mock
private ResponseEntity versionResponse; private ResponseEntity versionResponse;
@Mock @Mock
private ResponseEntity clusterResponse; private ResponseEntity clusterResponse;
@Mock @Mock
private ResponseEntity configItemResponse; private ResponseEntity configItemResponse;
private ConfigServiceImpl configService; private ConfigServiceImpl configService;
@Before @Before
public void setUp() { public void setUp() {
ReflectionTestUtils.setField(RestUtils.class, "restTemplate", restTemplate); ReflectionTestUtils.setField(RestUtils.class, "restTemplate", restTemplate);
configService = new ConfigServiceImpl(); configService = new ConfigServiceImpl();
} }
@Test @Test
public void testLoadReleaseConfig() { public void testLoadReleaseConfig() {
long appId = 6666; long appId = 6666;
long versionId = 100; long versionId = 100;
long releaseId = 11111; long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId); VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] someReleaseSnapShots = assembleReleaseSnapShots(); ReleaseSnapshotDTO[] someReleaseSnapShots = assembleReleaseSnapShots();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/" restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, someReleaseSnapShots, releaseSnapShotResponse); + releaseId, ReleaseSnapshotDTO[].class, someReleaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/" restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse); + versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId); AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId); assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId); assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2); assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 2); assertEquals(appConfigVO.getOverrideAppConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 2); assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 2);
} }
@Test @Test
public void testLoadReleaseConfigOnlyDefaultConfigs() { public void testLoadReleaseConfigOnlyDefaultConfigs() {
long appId = 6666; long appId = 6666;
long versionId = 100; long versionId = 100;
long releaseId = 11111; long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId); VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1]; ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
releaseSnapShots[0] = releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}"); assembleReleaseSnapShot(11111, "default-cluster-name",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse); restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse); restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId); assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2); assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 0); assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0); assertEquals(appConfigVO.getOverrideAppConfigs().size(), 0);
} assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
}
@Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideApp() { @Test
long appId = 6666; public void testLoadReleaseConfigDefaultConfigsAndOverrideApp() {
long versionId = 100; long appId = 6666;
long releaseId = 11111; long versionId = 100;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId); long releaseId = 11111;
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1]; VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
releaseSnapShots[0] = ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\", \"5555.bar\":\"demo2\", \"22.bar\":\"demo2\"}"); releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name",
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/" "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\", \"5555.bar\":\"demo2\", \"22.bar\":\"demo2\"}");
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/" + releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
+ versionId, VersionDTO.class, someVersion, versionResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId); assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId); assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2); assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(2, appConfigVO.getOverrideAppConfigs().size()); assertEquals(2, appConfigVO.getOverrideAppConfigs().size());
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0); assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
} }
@Test @Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideCluster() { public void testLoadReleaseConfigDefaultConfigsAndOverrideCluster() {
long appId = 6666; long appId = 6666;
long versionId = 100; long versionId = 100;
long releaseId = 11111; long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId); VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[2]; ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[2];
releaseSnapShots[0] = releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}"); assembleReleaseSnapShot(11111, "default-cluster-name",
releaseSnapShots[1] = "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
assembleReleaseSnapShot(11112, "cluster1", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}"); releaseSnapShots[1] =
assembleReleaseSnapShot(11112, "cluster1",
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/" "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/" + releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
+ versionId, VersionDTO.class, someVersion, versionResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId); + versionId, VersionDTO.class, someVersion, versionResponse);
assertEquals(appConfigVO.getAppId(), appId); AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2); assertEquals(appConfigVO.getAppId(), appId);
assertEquals(0, appConfigVO.getOverrideAppConfigs().size()); assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(1, appConfigVO.getOverrideClusterConfigs().size()); assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
} assertEquals(0, appConfigVO.getOverrideAppConfigs().size());
assertEquals(1, appConfigVO.getOverrideClusterConfigs().size());
@Test }
public void testLoadLastestConfig() {
long appId = 6666; @Test
ClusterDTO[] someClusters = assembleClusters(); public void testLoadLastestConfig() {
ConfigItemDTO[] someConfigItem = assembleConfigItems(); long appId = 6666;
ClusterDTO[] someClusters = assembleClusters();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/cluster/app/" ConfigItemDTO[] someConfigItem = assembleConfigItems();
+ appId, ClusterDTO[].class, someClusters, clusterResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/cluster/app/"
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + appId, ClusterDTO[].class, someClusters, clusterResponse);
+ "/configs/latest?clusterIds=100,101", ConfigItemDTO[].class, someConfigItem, configItemResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST
AppConfigVO appConfigVO = configService.loadLatestConfig(appId); + "/configs/latest?clusterIds=100,101", ConfigItemDTO[].class, someConfigItem,
configItemResponse);
assertEquals(appConfigVO.getAppId(), 6666);
assertEquals(appConfigVO.getVersionId(), PortalConstants.LASTEST_VERSION_ID); AppConfigVO appConfigVO = configService.loadLatestConfig(appId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 3);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 1); assertEquals(appConfigVO.getAppId(), 6666);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 1); assertEquals(appConfigVO.getVersionId(), PortalConstants.LASTEST_VERSION_ID);
} assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 3);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 1);
private <T> void restInvoke(String url, Class<T> responseType, T result, ResponseEntity someResponse) { assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 1);
when(restTemplate.exchange(eq(url), eq(HttpMethod.GET), any(HttpEntity.class), eq(responseType))).thenReturn(someResponse); }
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(result); private <T> void restInvoke(String url, Class<T> responseType, T result,
} ResponseEntity someResponse) {
when(
private VersionDTO assembleVersion(long appId, String versionName, long releaseId) { restTemplate.exchange(eq(url), eq(HttpMethod.GET), any(HttpEntity.class), eq(responseType)))
VersionDTO version = new VersionDTO(); .thenReturn(someResponse);
version.setAppId(appId); when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
version.setName(versionName); when(someResponse.getBody()).thenReturn(result);
version.setReleaseId(releaseId); }
return version;
} private VersionDTO assembleVersion(long appId, String versionName, long releaseId) {
VersionDTO version = new VersionDTO();
private ReleaseSnapshotDTO[] assembleReleaseSnapShots() { version.setAppId(appId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[3]; version.setName(versionName);
releaseSnapShots[0] = version.setReleaseId(releaseId);
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}"); return version;
releaseSnapShots[1] = }
assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}");
releaseSnapShots[2] = private ReleaseSnapshotDTO[] assembleReleaseSnapShots() {
assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}"); ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[3];
return releaseSnapShots; releaseSnapShots[0] =
} assembleReleaseSnapShot(11111, "default-cluster-name",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}");
private ReleaseSnapshotDTO assembleReleaseSnapShot(long releaseId, String clusterName, String configurations) { releaseSnapShots[1] =
ReleaseSnapshotDTO releaseSnapShot = new ReleaseSnapshotDTO(); assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}");
releaseSnapShot.setReleaseId(releaseId); releaseSnapShots[2] =
releaseSnapShot.setClusterName(clusterName); assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}");
releaseSnapShot.setConfigurations(configurations); return releaseSnapShots;
return releaseSnapShot; }
}
private ReleaseSnapshotDTO assembleReleaseSnapShot(long releaseId, String clusterName,
private ClusterDTO[] assembleClusters() { String configurations) {
ClusterDTO[] clusters = new ClusterDTO[2]; ReleaseSnapshotDTO releaseSnapShot = new ReleaseSnapshotDTO();
clusters[0] = assembleCluster(100, 6666, "default-cluster-name"); releaseSnapShot.setReleaseId(releaseId);
clusters[1] = assembleCluster(101, 6666, "cluster1"); releaseSnapShot.setClusterName(clusterName);
return clusters; releaseSnapShot.setConfigurations(configurations);
} return releaseSnapShot;
}
private ClusterDTO assembleCluster(long id, long appId, String name) {
ClusterDTO cluster = new ClusterDTO(); private ClusterDTO[] assembleClusters() {
cluster.setAppId(appId); ClusterDTO[] clusters = new ClusterDTO[2];
cluster.setId(id); clusters[0] = assembleCluster(100, 6666, "default-cluster-name");
cluster.setName(name); clusters[1] = assembleCluster(101, 6666, "cluster1");
return cluster; return clusters;
} }
private ConfigItemDTO[] assembleConfigItems() { private ClusterDTO assembleCluster(long id, long appId, String name) {
ConfigItemDTO[] configItems = new ConfigItemDTO[5]; ClusterDTO cluster = new ClusterDTO();
configItems[0] = cluster.setAppId(appId);
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k1", "6666.v1"); cluster.setId(id);
configItems[1] = cluster.setName(name);
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k2", "6666.v2"); return cluster;
configItems[2] = }
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k3", "6666.v3");
configItems[3] = private ConfigItemDTO[] assembleConfigItems() {
assembleConfigItem(100, "default-cluster-name", 5555, "5555.k1", "5555.v1"); ConfigItemDTO[] configItems = new ConfigItemDTO[5];
configItems[4] = assembleConfigItem(101, "cluster1", 6666, "6666.k1", "6666.v1"); configItems[0] =
return configItems; assembleConfigItem(100, "default-cluster-name", 6666, "6666.k1", "6666.v1");
} configItems[1] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k2", "6666.v2");
private ConfigItemDTO assembleConfigItem(long clusterId, String clusterName, int appId, String key, String value) { configItems[2] =
ConfigItemDTO configItem = new ConfigItemDTO(); assembleConfigItem(100, "default-cluster-name", 6666, "6666.k3", "6666.v3");
configItem.setClusterName(clusterName); configItems[3] =
configItem.setClusterId(clusterId); assembleConfigItem(100, "default-cluster-name", 5555, "5555.k1", "5555.v1");
configItem.setAppId(appId); configItems[4] = assembleConfigItem(101, "cluster1", 6666, "6666.k1", "6666.v1");
configItem.setKey(key); return configItems;
configItem.setValue(value); }
return configItem;
} private ConfigItemDTO assembleConfigItem(long clusterId, String clusterName, int appId,
String key, String value) {
ConfigItemDTO configItem = new ConfigItemDTO();
configItem.setClusterName(clusterName);
configItem.setClusterId(clusterId);
configItem.setAppId(appId);
configItem.setKey(key);
configItem.setValue(value);
return configItem;
}
} }
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import java.util.List; import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.entity.Privilege;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.ctrip.apollo.portal.AbstractPortalTest; import java.util.List;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.entity.Privilege;
public class PrivilegeServiceTest extends AbstractPortalTest { public class PrivilegeServiceTest extends AbstractPortalTest {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment