Commit bebe9de4 authored by Yiming Liu's avatar Yiming Liu

Merge pull request #40 from nobodyiam/google-style-format

format according google code style
parents e96d68c7 7c7f7b2a
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-adminservice</artifactId>
<name>Apollo AdminService</name>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId>
</dependency>
<!-- end of apollo -->
<!-- redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- end of redis -->
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- end of eureka -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-adminservice</artifactId>
<name>Apollo AdminService</name>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId>
</dependency>
<!-- end of apollo -->
<!-- redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- end of redis -->
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- end of eureka -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......@@ -2,13 +2,12 @@ package com.ctrip.apollo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//@EnableEurekaClient
public class AdminServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(AdminServiceApplication.class).web(true).run(args);
}
public static void main(String[] args) {
new SpringApplicationBuilder(AdminServiceApplication.class).web(true).run(args);
}
}
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.ClusterDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -13,11 +14,11 @@ import java.util.List;
@RequestMapping("/cluster")
public class ClusterController {
@Autowired
private AdminConfigService adminConfigService;
@Autowired
private AdminConfigService adminConfigService;
@RequestMapping("/app/{appId}")
public List<ClusterDTO> findClustersByApp(@PathVariable long appId){
return adminConfigService.findClustersByApp(appId);
}
@RequestMapping("/app/{appId}")
public List<ClusterDTO> findClustersByApp(@PathVariable long appId) {
return adminConfigService.findClustersByApp(appId);
}
}
......@@ -3,31 +3,34 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import javax.annotation.Resource;
@RestController
@RequestMapping("/configs")
public class ConfigController {
@Resource(name = "adminConfigService")
private AdminConfigService adminConfigService;
@Resource(name = "adminConfigService")
private AdminConfigService adminConfigService;
@RequestMapping("/release/{releaseId}")
public List<ReleaseSnapshotDTO> getRelaseSnapshot(@PathVariable long releaseId){
return adminConfigService.findReleaseSnapshotByReleaseId(releaseId);
}
@RequestMapping("/release/{releaseId}")
public List<ReleaseSnapshotDTO> getRelaseSnapshot(@PathVariable long releaseId) {
return adminConfigService.findReleaseSnapshotByReleaseId(releaseId);
}
@RequestMapping("/latest")
public List<ConfigItemDTO> findConfigItemsByClusters(@RequestParam(value = "clusterIds") List<Long> clusterIds){
return adminConfigService.findConfigItemsByClusters(clusterIds);
}
@RequestMapping("/latest")
public List<ConfigItemDTO> findConfigItemsByClusters(
@RequestParam(value = "clusterIds") List<Long> clusterIds) {
return adminConfigService.findConfigItemsByClusters(clusterIds);
}
}
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.adminservice.controller;
import com.ctrip.apollo.biz.service.AdminConfigService;
import com.ctrip.apollo.core.dto.VersionDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -13,21 +14,19 @@ import java.util.List;
@RequestMapping("/version")
public class VersionController {
@Autowired
private AdminConfigService adminConfigService;
@RequestMapping("/app/{appId}")
public List<VersionDTO> versions(@PathVariable long appId){
return adminConfigService.findVersionsByApp(appId);
}
@Autowired
private AdminConfigService adminConfigService;
@RequestMapping("/{versionId}")
public VersionDTO version(@PathVariable long versionId){
return adminConfigService.loadVersionById(versionId);
}
@RequestMapping("/app/{appId}")
public List<VersionDTO> versions(@PathVariable long appId) {
return adminConfigService.findVersionsByApp(appId);
}
@RequestMapping("/{versionId}")
public VersionDTO version(@PathVariable long versionId) {
return adminConfigService.loadVersionById(versionId);
}
}
package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ClusterDTO;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
......@@ -16,58 +17,58 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?")
public class Cluster {
@Id
@GeneratedValue
private long id;
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private long appId;
@Column(nullable = false)
private long appId;
private boolean isDeleted;
private boolean isDeleted;
public Cluster() {
}
public Cluster() {
}
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public boolean isDeleted() {
return isDeleted;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public ClusterDTO toDTO(){
ClusterDTO dto = new ClusterDTO();
dto.setAppId(appId);
dto.setId(id);
dto.setName(name);
return dto;
}
public ClusterDTO toDTO() {
ClusterDTO dto = new ClusterDTO();
dto.setAppId(appId);
dto.setId(id);
dto.setName(name);
return dto;
}
}
package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ConfigItemDTO;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;
@Entity
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update ConfigItem set isDeleted = 1 where id = ?")
public class ConfigItem {
@Id
@GeneratedValue
private long id;
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private long clusterId;
@Column(nullable = false)
private long clusterId;
@Column(nullable = false)
private String clusterName;
@Column(nullable = false)
private String clusterName;
@Column(nullable = false)
private long appId;
@Column(nullable = false)
private long appId;
@Column(nullable = false)
private String key;
@Column(nullable = false)
private String key;
@Column
private String value;
@Column
private String value;
@Column
private String comment;
@Column
private String comment;
@Column
private String dataChangeCreatedBy;
@Column
private String dataChangeCreatedBy;
@Column
private Date dataChangeCreatedTime;
@Column
private Date dataChangeCreatedTime;
@Column
private String dataChangeLastModifiedBy;
@Column
private String dataChangeLastModifiedBy;
@Column
private Date dataChangeLastModifiedTime;
@Column
private Date dataChangeLastModifiedTime;
@Column
private boolean IsDeleted;
@Column
private boolean IsDeleted;
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public long getClusterId() {
return clusterId;
}
public long getClusterId() {
return clusterId;
}
public void setClusterId(long clusterId) {
this.clusterId = clusterId;
}
public void setClusterId(long clusterId) {
this.clusterId = clusterId;
}
public String getClusterName() {
return clusterName;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public String getKey() {
return key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getComment() {
return comment;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy;
}
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
}
public boolean isDeleted() {
return IsDeleted;
}
public void setDeleted(boolean isDeleted) {
IsDeleted = isDeleted;
}
public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime;
}
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
}
public ConfigItemDTO toDTO(){
ConfigItemDTO dto = new ConfigItemDTO();
dto.setAppId(appId);
dto.setId(id);
dto.setClusterId(clusterId);
dto.setClusterName(clusterName);
dto.setDataChangeCreatedBy(dataChangeCreatedBy);
dto.setDataChangeLastModifiedBy(dataChangeLastModifiedBy);
dto.setDataChangeCreatedTime(dataChangeCreatedTime);
dto.setDataChangeLastModifiedTime(dataChangeLastModifiedTime);
dto.setKey(key);
dto.setValue(value);
dto.setComment(comment);
return dto;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy;
}
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
}
public boolean isDeleted() {
return IsDeleted;
}
public void setDeleted(boolean isDeleted) {
IsDeleted = isDeleted;
}
public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime;
}
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
}
public ConfigItemDTO toDTO() {
ConfigItemDTO dto = new ConfigItemDTO();
dto.setAppId(appId);
dto.setId(id);
dto.setClusterId(clusterId);
dto.setClusterName(clusterName);
dto.setDataChangeCreatedBy(dataChangeCreatedBy);
dto.setDataChangeLastModifiedBy(dataChangeLastModifiedBy);
dto.setDataChangeCreatedTime(dataChangeCreatedTime);
dto.setDataChangeLastModifiedTime(dataChangeLastModifiedTime);
dto.setKey(key);
dto.setValue(value);
dto.setComment(comment);
return dto;
}
}
......@@ -14,55 +14,55 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Release set isDeleted = 1 where id = ?")
public class Release {
@Id
@GeneratedValue
private long id;
@Id
@GeneratedValue
private long id;
private String name;
private long appId;
private String comment;
private boolean isDeleted;
private String name;
private long appId;
private String comment;
private boolean isDeleted;
public Release() {
}
public Release() {
}
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public String getComment() {
return comment;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public boolean isDeleted() {
return isDeleted;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
}
package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.ReleaseSnapshotDTO;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
......@@ -16,70 +17,70 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update ReleaseSnapShot set isDeleted = 1 where id = ?")
public class ReleaseSnapShot {
@Id
@GeneratedValue
private long id;
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private long releaseId;
@Column(nullable = false)
private long releaseId;
@Column(nullable = false)
private String clusterName;
@Column(nullable = false)
private String clusterName;
@Column(nullable = false)
private String configurations;
@Column(nullable = false)
private String configurations;
private boolean isDeleted;
private boolean isDeleted;
public ReleaseSnapShot() {
}
public ReleaseSnapShot() {
}
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public long getReleaseId() {
return releaseId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public String getClusterName() {
return clusterName;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getConfigurations() {
return configurations;
}
public String getConfigurations() {
return configurations;
}
public void setConfigurations(String configurations) {
this.configurations = configurations;
}
public void setConfigurations(String configurations) {
this.configurations = configurations;
}
public boolean isDeleted() {
return isDeleted;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public ReleaseSnapshotDTO toDTO(){
ReleaseSnapshotDTO dto = new ReleaseSnapshotDTO();
dto.setId(id);
dto.setClusterName(clusterName);
dto.setConfigurations(configurations);
dto.setReleaseId(releaseId);
return dto;
}
public ReleaseSnapshotDTO toDTO() {
ReleaseSnapshotDTO dto = new ReleaseSnapshotDTO();
dto.setId(id);
dto.setClusterName(clusterName);
dto.setConfigurations(configurations);
dto.setReleaseId(releaseId);
return dto;
}
}
package com.ctrip.apollo.biz.entity;
import com.ctrip.apollo.core.dto.VersionDTO;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
......@@ -16,80 +17,80 @@ import javax.persistence.Id;
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Version set isDeleted = 1 where id = ?")
public class Version {
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private long appId;
@Column(nullable = false)
private long releaseId;
//parent version could be null
private Long parentVersion;
private boolean isDeleted;
public Version() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public Long getParentVersion() {
return parentVersion;
}
public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion;
}
public VersionDTO toDTO() {
VersionDTO dto = new VersionDTO();
dto.setAppId(this.appId);
dto.setId(this.id);
dto.setName(this.name);
dto.setParentVersion(this.parentVersion);
dto.setReleaseId(this.releaseId);
return dto;
}
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private long appId;
@Column(nullable = false)
private long releaseId;
//parent version could be null
private Long parentVersion;
private boolean isDeleted;
public Version() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public Long getParentVersion() {
return parentVersion;
}
public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion;
}
public VersionDTO toDTO() {
VersionDTO dto = new VersionDTO();
dto.setAppId(this.appId);
dto.setId(this.id);
dto.setName(this.name);
dto.setParentVersion(this.parentVersion);
dto.setReleaseId(this.releaseId);
return dto;
}
}
......@@ -2,12 +2,13 @@ package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.Cluster;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> {
List<Cluster> findByAppId(long appId);
List<Cluster> findByAppId(long appId);
}
package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.ConfigItem;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
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;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
......@@ -8,8 +9,9 @@ import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ReleaseSnapShotRepository extends PagingAndSortingRepository<ReleaseSnapShot, Long> {
ReleaseSnapShot findByReleaseIdAndClusterName(long releaseId, String clusterName);
public interface ReleaseSnapShotRepository
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;
import com.ctrip.apollo.biz.entity.Version;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
......@@ -9,9 +10,9 @@ import java.util.List;
* @author Jason Song(song_s@ctrip.com)
*/
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;
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;
......@@ -9,14 +12,14 @@ import java.util.List;
*/
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;
/**
* Config Service
*
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigService {
/**
* Load configuration from database
* @param appId
* @param clusterName
* @param versionName
* @return
*/
ApolloConfig loadConfig(long appId, String clusterName, String versionName);
/**
* Load configuration from database
*/
ApolloConfig loadConfig(long appId, String clusterName, String versionName);
/**
* Load Version by appId and versionName from database
* @param appId
* @param versionName
* @return
*/
Version loadVersionByAppIdAndVersionName(long appId, String versionName);
/**
* Load Version by appId and versionName from database
*/
Version loadVersionByAppIdAndVersionName(long appId, String versionName);
/**
* Load Config by version and clusterName from database
* @param version
* @param clusterName
* @return
*/
ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName);
/**
* Load Config by version and clusterName from database
*/
ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName);
}
......@@ -9,103 +9,109 @@ import com.ctrip.apollo.biz.repository.ConfigItemRepository;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
import com.ctrip.apollo.biz.repository.VersionRepository;
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.stereotype.Service;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service("adminConfigService")
public class AdminConfigServiceImpl implements AdminConfigService {
@Autowired
private VersionRepository versionRepository;
@Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired
private ClusterRepository clusterRepository;
@Autowired
private ConfigItemRepository configItemRepository;
@Override
public List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId) {
if (releaseId <= 0){
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;
@Autowired
private VersionRepository versionRepository;
@Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired
private ClusterRepository clusterRepository;
@Autowired
private ConfigItemRepository configItemRepository;
@Override
public List<ReleaseSnapshotDTO> findReleaseSnapshotByReleaseId(long releaseId) {
if (releaseId <= 0) {
return Collections.EMPTY_LIST;
}
List<ReleaseSnapShot> releaseSnapShots = releaseSnapShotRepository.findByReleaseId(releaseId);
if (releaseSnapShots == null || releaseSnapShots.size() == 0) {
return Collections.EMPTY_LIST;
}
@Override
public List<VersionDTO> findVersionsByApp(long appId) {
if (appId <= 0) {
return Collections.EMPTY_LIST;
}
List<ReleaseSnapshotDTO> result = new ArrayList<>(releaseSnapShots.size());
for (ReleaseSnapShot releaseSnapShot : releaseSnapShots) {
result.add(releaseSnapShot.toDTO());
}
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());
for (Version version : versions) {
result.add(version.toDTO());
}
return result;
@Override
public List<VersionDTO> findVersionsByApp(long appId) {
if (appId <= 0) {
return Collections.EMPTY_LIST;
}
@Override
public VersionDTO loadVersionById(long versionId) {
if (versionId <= 0){
return null;
}
Version version = versionRepository.findById(versionId);
return version.toDTO();
List<Version> versions = versionRepository.findByAppId(appId);
if (versions == null || versions.size() == 0) {
return Collections.EMPTY_LIST;
}
@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;
List<VersionDTO> result = new ArrayList<>(versions.size());
for (Version version : versions) {
result.add(version.toDTO());
}
return result;
}
@Override
public VersionDTO loadVersionById(long versionId) {
if (versionId <= 0) {
return null;
}
Version version = versionRepository.findById(versionId);
return version.toDTO();
}
@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
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;
}
List<ConfigItemDTO> result = new ArrayList<>(configItems.size());
for (ConfigItem configItem: configItems){
result.add(configItem.toDTO());
}
return result;
List<ConfigItemDTO> result = new ArrayList<>(configItems.size());
for (ConfigItem configItem : configItems) {
result.add(configItem.toDTO());
}
return result;
}
}
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.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
......@@ -8,7 +10,7 @@ import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -20,56 +22,58 @@ import java.util.Map;
*/
@Service("configService")
public class ConfigServiceImpl implements ConfigService {
@Autowired
private VersionRepository versionRepository;
@Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired
private ObjectMapper objectMapper;
private TypeReference<Map<String, Object>> configurationTypeReference =
new TypeReference<Map<String, Object>>() {
};
@Autowired
private VersionRepository versionRepository;
@Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired
private ObjectMapper objectMapper;
private TypeReference<Map<String, Object>> configurationTypeReference =
new TypeReference<Map<String, Object>>() {
};
@Override
public ApolloConfig loadConfig(long appId, String clusterName, String versionName) {
Version version = loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) {
return null;
}
return loadConfigByVersionAndClusterName(version, clusterName);
@Override
public ApolloConfig loadConfig(long appId, String clusterName, String versionName) {
Version version = loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) {
return null;
}
@Override
public Version loadVersionByAppIdAndVersionName(long appId, String versionName) {
return versionRepository.findByAppIdAndName(appId, versionName);
}
return loadConfigByVersionAndClusterName(version, clusterName);
}
@Override
public ApolloConfig loadConfigByVersionAndClusterName(Version version, String clusterName) {
ReleaseSnapShot releaseSnapShot =
releaseSnapShotRepository.findByReleaseIdAndClusterName(version.getReleaseId(), clusterName);
if (releaseSnapShot == null) {
return null;
}
@Override
public Version loadVersionByAppIdAndVersionName(long appId, String versionName) {
return versionRepository.findByAppIdAndName(appId, versionName);
}
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) {
ApolloConfig config =
new ApolloConfig(version.getAppId(), releaseSnapShot.getClusterName(), version.getName(), version.getReleaseId());
config.setConfigurations(transformConfigurationToMap(releaseSnapShot.getConfigurations()));
return assembleConfig(version, releaseSnapShot);
}
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) {
try {
return objectMapper.readValue(configurations, configurationTypeReference);
} catch (IOException e) {
e.printStackTrace();
return Maps.newHashMap();
}
Map<String, Object> transformConfigurationToMap(String configurations) {
try {
return objectMapper.readValue(configurations, configurationTypeReference);
} catch (IOException e) {
e.printStackTrace();
return Maps.newHashMap();
}
}
}
package com.ctrip.apollo.biz;
import com.ctrip.apollo.biz.service.impl.ConfigServiceImplTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ConfigServiceImplTest.class})
ConfigServiceImplTest.class})
public class AllTests {
}
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.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
......@@ -7,7 +9,7 @@ import com.ctrip.apollo.biz.repository.VersionRepository;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -21,123 +23,140 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceImplTest {
@Mock
private VersionRepository versionRepository;
@Mock
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Mock
private ObjectMapper objectMapper;
private ConfigServiceImpl configService;
@Before
public void setUp() throws Exception {
configService = new ConfigServiceImpl();
ReflectionTestUtils.setField(configService, "versionRepository", versionRepository);
ReflectionTestUtils.setField(configService, "releaseSnapShotRepository", releaseSnapShotRepository);
ReflectionTestUtils.setField(configService, "objectMapper", objectMapper);
}
@Test
public void testLoadConfig() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
long someReleaseId = 1;
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
ReleaseSnapShot someReleaseSnapShot = assembleReleaseSnapShot(someReleaseId, someClusterName, someValidConfiguration);
Map<String, Object> someMap = Maps.newHashMap();
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName)).thenReturn(someReleaseSnapShot);
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(someVersionName, result.getVersion());
assertEquals(someReleaseId, result.getReleaseId());
assertEquals(someMap, result.getConfigurations());
}
@Test
public void testLoadConfigWithVersionNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(null);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertNull(result);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
}
@Test
public void testLoadConfigWithConfigNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
long someReleaseId = 1;
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName)).thenReturn(null);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertNull(result);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
verify(releaseSnapShotRepository, times(1)).findByReleaseIdAndClusterName(someReleaseId, someClusterName);
}
private Version assembleVersion(long appId, String versionName, long releaseId) {
Version version = new Version();
version.setAppId(appId);
version.setName(versionName);
version.setReleaseId(releaseId);
return version;
}
private ReleaseSnapShot assembleReleaseSnapShot(long releaseId, String clusterName, String configurations) {
ReleaseSnapShot releaseSnapShot = new ReleaseSnapShot();
releaseSnapShot.setReleaseId(releaseId);
releaseSnapShot.setClusterName(clusterName);
releaseSnapShot.setConfigurations(configurations);
return releaseSnapShot;
}
@Test
public void testTransformConfigurationToMapSuccessful() throws Exception {
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Map<String, String> someMap = Maps.newHashMap();
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap);
Map<String, Object> result = configService.transformConfigurationToMap(someValidConfiguration);
assertEquals(someMap, result);
verify(objectMapper, times(1)).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());
}
@Mock
private VersionRepository versionRepository;
@Mock
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Mock
private ObjectMapper objectMapper;
private ConfigServiceImpl configService;
@Before
public void setUp() throws Exception {
configService = new ConfigServiceImpl();
ReflectionTestUtils.setField(configService, "versionRepository", versionRepository);
ReflectionTestUtils
.setField(configService, "releaseSnapShotRepository", releaseSnapShotRepository);
ReflectionTestUtils.setField(configService, "objectMapper", objectMapper);
}
@Test
public void testLoadConfig() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
long someReleaseId = 1;
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
ReleaseSnapShot
someReleaseSnapShot =
assembleReleaseSnapShot(someReleaseId, someClusterName, someValidConfiguration);
Map<String, Object> someMap = Maps.newHashMap();
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName))
.thenReturn(someReleaseSnapShot);
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
.thenReturn(someMap);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(someVersionName, result.getVersion());
assertEquals(someReleaseId, result.getReleaseId());
assertEquals(someMap, result.getConfigurations());
}
@Test
public void testLoadConfigWithVersionNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(null);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertNull(result);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
}
@Test
public void testLoadConfigWithConfigNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
long someReleaseId = 1;
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName))
.thenReturn(null);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertNull(result);
verify(versionRepository, times(1)).findByAppIdAndName(someAppId, someVersionName);
verify(releaseSnapShotRepository, times(1))
.findByReleaseIdAndClusterName(someReleaseId, someClusterName);
}
private Version assembleVersion(long appId, String versionName, long releaseId) {
Version version = new Version();
version.setAppId(appId);
version.setName(versionName);
version.setReleaseId(releaseId);
return version;
}
private ReleaseSnapShot assembleReleaseSnapShot(long releaseId, String clusterName,
String configurations) {
ReleaseSnapShot releaseSnapShot = new ReleaseSnapShot();
releaseSnapShot.setReleaseId(releaseId);
releaseSnapShot.setClusterName(clusterName);
releaseSnapShot.setConfigurations(configurations);
return releaseSnapShot;
}
@Test
public void testTransformConfigurationToMapSuccessful() throws Exception {
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Map<String, String> someMap = Maps.newHashMap();
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
.thenReturn(someMap);
Map<String, Object> result = configService.transformConfigurationToMap(someValidConfiguration);
assertEquals(someMap, result);
verify(objectMapper, times(1))
.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"?>
<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>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
......
......@@ -5,6 +5,7 @@ import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
......@@ -35,140 +36,155 @@ import java.util.concurrent.atomic.AtomicReference;
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class);
private static AtomicReference<ApolloConfigManager> singletonProtector = new AtomicReference<ApolloConfigManager>();
private ConfigLoaderManager configLoaderManager;
private ConfigurableApplicationContext applicationContext;
private ConfigUtil configUtil;
private ScheduledExecutorService executorService;
private AtomicLong counter;
private RefreshScope scope;
public ApolloConfigManager() {
if(!singletonProtector.compareAndSet(null, this)) {
throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
}
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
this.configUtil = ConfigUtil.getInstance();
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();
public class ApolloConfigManager
implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class);
private static AtomicReference<ApolloConfigManager>
singletonProtector =
new AtomicReference<ApolloConfigManager>();
private ConfigLoaderManager configLoaderManager;
private ConfigurableApplicationContext applicationContext;
private ConfigUtil configUtil;
private ScheduledExecutorService executorService;
private AtomicLong counter;
private RefreshScope scope;
public ApolloConfigManager() {
if (!singletonProtector.compareAndSet(null, this)) {
throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
}
/**
* 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>
* @param registry
*/
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.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
this.configUtil = ConfigUtil.getInstance();
this.counter = new AtomicLong();
executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "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 is executed after postProcessBeanDefinitionRegistry
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
/**
* Make sure this bean is called before other beans
* @return
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
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();
}
/**
* 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;
}
/**
* 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);
}
void schedulePeriodicRefresh() {
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
updatePropertySource();
} catch (Throwable e) {
logger.error("Refreshing config failed", e);
}
currentPropertySources.addFirst(currentPropertySource);
}
void schedulePeriodicRefresh() {
executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
try {
updatePropertySource();
} catch (Throwable ex) {
logger.error("Refreshing config failed", ex);
}
}, 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() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource();
if (result.hasChanges()) {
logger.info("Found changes, refresh environment and refreshscope beans.");
updateEnvironmentPropertySource(result.getPropertySource());
refreshBeans();
}
return result.getChanges();
private void refreshBeans() {
if (this.scope == null) {
this.scope = applicationContext.getBean("refreshScope", RefreshScope.class);
}
private void refreshBeans() {
if (this.scope == null) {
this.scope = applicationContext.getBean("refreshScope", RefreshScope.class);
}
if (this.scope == null) {
logger.error("Could not get refresh scope object, skip refresh beans");
return;
}
this.scope.refreshAll();
if (this.scope == null) {
logger.error("Could not get refresh scope object, skip refresh beans");
return;
}
this.scope.refreshAll();
}
}
......@@ -4,8 +4,8 @@ package com.ctrip.apollo.client.constants;
* @author Jason Song(song_s@ctrip.com)
*/
public class Constants {
public static final String APP_ID = "app.id";
public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env";
public static final String APP_ID = "app.id";
public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env";
}
......@@ -5,17 +5,17 @@ package com.ctrip.apollo.client.enums;
* @author Jason Song(song_s@ctrip.com)
*/
public enum PropertyChangeType {
NEW("New"),
MODIFIED("Modified"),
DELETED("Deleted");
NEW("New"),
MODIFIED("Modified"),
DELETED("Deleted");
private String type;
private String type;
PropertyChangeType(String type) {
this.type = type;
}
PropertyChangeType(String type) {
this.type = type;
}
public String getType() {
return type;
}
public String getType() {
return type;
}
}
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.FileInputStream;
import java.io.IOException;
......@@ -10,20 +19,11 @@ import java.util.Enumeration;
import java.util.Properties;
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 {
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>();
......@@ -84,20 +84,20 @@ public class ClientEnvironment {
props.load(in);
in.close();
}
} catch (Exception e) {
logger.warn("Reading config failed: {}", e.getMessage());
} catch (Exception ex) {
logger.warn("Reading config failed: {}", ex.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
logger.warn("Close config failed: {}", e.getMessage());
} catch (IOException ex) {
logger.warn("Close config failed: {}", ex.getMessage());
}
}
}
StringBuilder sb = new StringBuilder();
for (Enumeration<String> e = (Enumeration<String>) props.propertyNames(); e
.hasMoreElements();) {
.hasMoreElements(); ) {
String key = e.nextElement();
String val = (String) props.getProperty(key);
sb.append(key).append('=').append(val).append('\n');
......
......@@ -7,7 +7,7 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
* @author Jason Song(songs_ctrip.com)
*/
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;
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.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.util.ConfigUtil;
import org.springframework.web.client.RestTemplate;
/**
......@@ -14,35 +15,39 @@ import org.springframework.web.client.RestTemplate;
public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {}
private ConfigLoaderFactory() {
}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
return configLoader;
}
public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
return configLoader;
}
public ConfigLoader getInMemoryConfigLoader() {
ConfigLoader inMemoryConfigLoader = new InMemoryConfigLoader();
inMemoryConfigLoader.setFallBackLoader(getLocalFileConfigLoader());
return inMemoryConfigLoader;
}
public ConfigLoader getInMemoryConfigLoader() {
ConfigLoader inMemoryConfigLoader = new InMemoryConfigLoader();
inMemoryConfigLoader.setFallBackLoader(getLocalFileConfigLoader());
return inMemoryConfigLoader;
}
public ConfigLoader getRemoteConfigLoader() {
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(), new ConfigServiceLocator());
public ConfigLoader getRemoteConfigLoader() {
ConfigLoader
remoteConfigLoader =
new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(),
new ConfigServiceLocator());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader;
}
return remoteConfigLoader;
}
public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
}
}
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.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
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.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
......@@ -19,172 +21,186 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
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;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigLoaderManager {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderManager.class);
private ConfigLoader configLoader;
private ConfigUtil configUtil;
private final ExecutorService executorService;
private final AtomicLong counter;
private Map<ApolloRegistry, ApolloConfig> currentApolloRegistryConfigCache;
private Map<ApolloRegistry, ApolloConfig> previousApolloRegistryConfigCache;
private List<ApolloRegistry> apolloRegistries;
public ConfigLoaderManager(ConfigLoader configLoader, ConfigUtil configUtil) {
this.configLoader = configLoader;
this.configUtil = configUtil;
this.counter = new AtomicLong();
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "ConfigLoaderManager-" + counter.incrementAndGet());
return thread;
}
});
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderManager.class);
private ConfigLoader configLoader;
private ConfigUtil configUtil;
private final ExecutorService executorService;
private final AtomicLong counter;
private Map<ApolloRegistry, ApolloConfig> currentApolloRegistryConfigCache;
private Map<ApolloRegistry, ApolloConfig> previousApolloRegistryConfigCache;
private List<ApolloRegistry> apolloRegistries;
public ConfigLoaderManager(ConfigLoader configLoader, ConfigUtil configUtil) {
this.configLoader = configLoader;
this.configUtil = configUtil;
this.counter = new AtomicLong();
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "ConfigLoaderManager-" + counter.incrementAndGet());
return thread;
}
});
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() {
try {
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException e) {
throw new RuntimeException("Load apollo config registry failed", e);
}
return loadPropertySourceWithApolloRegistries(apolloRegistries);
}
public PropertySourceReloadResult reloadPropertySource() {
CompositePropertySource composite = loadPropertySourceWithApolloRegistries(apolloRegistries);
List<ApolloConfig> previous = Lists.newArrayList(this.previousApolloRegistryConfigCache.values());
List<ApolloConfig> current = Lists.newArrayList(this.currentApolloRegistryConfigCache.values());
return new PropertySourceReloadResult(composite, calcPropertyChanges(previous, current));
return loadPropertySourceWithApolloRegistries(apolloRegistries);
}
public PropertySourceReloadResult reloadPropertySource() {
CompositePropertySource composite = loadPropertySourceWithApolloRegistries(apolloRegistries);
List<ApolloConfig>
previous =
Lists.newArrayList(this.previousApolloRegistryConfigCache.values());
List<ApolloConfig> current = Lists.newArrayList(this.currentApolloRegistryConfigCache.values());
return new PropertySourceReloadResult(composite, calcPropertyChanges(previous, current));
}
/**
* Load property source with apollo registries provided
* 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;
}
/**
* Load property source with apollo registries provided
* Should not be invoked in parallel since there are some operations like create/destroy cache,
* writing to files etc.
* @param apolloRegistries
* @return
*/
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);
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);
}
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) {
Map<String, Object> previousMap = collectConfigurations(previous);
Map<String, Object> currentMap = collectConfigurations(current);
List<PropertyChange> calcPropertyChanges(List<ApolloConfig> previous,
List<ApolloConfig> current) {
Map<String, Object> previousMap = collectConfigurations(previous);
Map<String, Object> currentMap = collectConfigurations(current);
Set<String> previousKeys = previousMap.keySet();
Set<String> currentKeys = currentMap.keySet();
Set<String> previousKeys = previousMap.keySet();
Set<String> currentKeys = currentMap.keySet();
Set<String> commonKeys = Sets.intersection(previousKeys, currentKeys);
Set<String> newKeys = Sets.difference(currentKeys, commonKeys);
Set<String> removedKeys = Sets.difference(previousKeys, commonKeys);
Set<String> commonKeys = Sets.intersection(previousKeys, currentKeys);
Set<String> newKeys = Sets.difference(currentKeys, commonKeys);
Set<String> removedKeys = Sets.difference(previousKeys, commonKeys);
List<PropertyChange> changes = Lists.newArrayList();
for (String newKey : newKeys) {
changes.add(new PropertyChange(newKey, null, currentMap.get(newKey), PropertyChangeType.NEW));
}
List<PropertyChange> changes = Lists.newArrayList();
for (String removedKey : removedKeys) {
changes.add(new PropertyChange(removedKey, previousMap.get(removedKey), null, PropertyChangeType.DELETED));
}
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));
}
for (String newKey : newKeys) {
changes.add(new PropertyChange(newKey, null, currentMap.get(newKey), PropertyChangeType.NEW));
}
return changes;
for (String removedKey : removedKeys) {
changes.add(new PropertyChange(removedKey, previousMap.get(removedKey), null,
PropertyChangeType.DELETED));
}
Map<String, Object> collectConfigurations(List<ApolloConfig> apolloConfigs) {
Collections.sort(apolloConfigs);
Map<String, Object> configMap = Maps.newHashMap();
for (int i = apolloConfigs.size() - 1; i > -1; i--) {
configMap.putAll(apolloConfigs.get(i).getConfigurations());
}
return configMap;
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));
}
List<ApolloConfig> loadApolloConfigs(List<ApolloRegistry> apolloRegistries) throws Throwable {
List<Future<ApolloConfig>> futures = Lists.newArrayList();
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<ApolloConfig>() {
@Override
public ApolloConfig call() throws Exception {
return loadSingleApolloConfig(apolloRegistry);
}
}));
}
List<ApolloConfig> apolloConfigList = Lists.newArrayList();
for (Future<ApolloConfig> future : futures) {
try {
ApolloConfig result = future.get();
if (result == null) {
continue;
}
apolloConfigList.add(result);
} catch (ExecutionException e) {
throw e.getCause();
}
return changes;
}
Map<String, Object> collectConfigurations(List<ApolloConfig> apolloConfigs) {
Collections.sort(apolloConfigs);
Map<String, Object> configMap = Maps.newHashMap();
for (int i = apolloConfigs.size() - 1; i > -1; i--) {
configMap.putAll(apolloConfigs.get(i).getConfigurations());
}
return configMap;
}
List<ApolloConfig> loadApolloConfigs(List<ApolloRegistry> apolloRegistries) throws Throwable {
List<Future<ApolloConfig>> futures = Lists.newArrayList();
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<ApolloConfig>() {
@Override
public ApolloConfig call() throws Exception {
return loadSingleApolloConfig(apolloRegistry);
}
return apolloConfigList;
}));
}
ApolloConfig loadSingleApolloConfig(ApolloRegistry apolloRegistry) {
ApolloConfig result = configLoader.loadApolloConfig(apolloRegistry, getPreviousApolloConfig(apolloRegistry));
List<ApolloConfig> apolloConfigList = Lists.newArrayList();
for (Future<ApolloConfig> future : futures) {
try {
ApolloConfig result = future.get();
if (result == null) {
logger.error("Loaded config null...");
return null;
continue;
}
logger.info("Loaded config: {}", result);
updateCurrentApolloConfigCache(apolloRegistry, result);
return result;
apolloConfigList.add(result);
} catch (ExecutionException ex) {
throw ex.getCause();
}
}
void resetApolloRegistryConfigCache() {
this.previousApolloRegistryConfigCache = currentApolloRegistryConfigCache;
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
return apolloConfigList;
}
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 previousApolloRegistryConfigCache.get(apolloRegistry);
}
return result;
}
void updateCurrentApolloConfigCache(ApolloRegistry apolloRegistry, ApolloConfig apolloConfig) {
currentApolloRegistryConfigCache.put(apolloRegistry, apolloConfig);
}
void resetApolloRegistryConfigCache() {
this.previousApolloRegistryConfigCache = currentApolloRegistryConfigCache;
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
private String assemblePropertySourceName(ApolloConfig apolloConfig) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(), apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
ApolloConfig getPreviousApolloConfig(ApolloRegistry apolloRegistry) {
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;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.core.serivce.ApolloService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.core.serivce.ApolloService;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
public class ConfigServiceLocator {
......@@ -31,8 +31,8 @@ public class ConfigServiceLocator {
serviceCaches.add(service);
}
}
} catch (Exception e) {
logger.warn(e.getMessage());
} catch (Exception ex) {
logger.warn(ex.getMessage());
}
return serviceCaches;
}
......
......@@ -3,6 +3,7 @@ package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -10,26 +11,29 @@ import org.slf4j.LoggerFactory;
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class AbstractConfigLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class);
private ConfigLoader fallback;
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class);
private ConfigLoader fallback;
@Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try {
return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable e) {
if (this.fallback == null) {
throw new RuntimeException(String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), e);
}
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);
}
@Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try {
return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable ex) {
if (this.fallback == null) {
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(), ex);
return this.fallback.loadApolloConfig(apolloRegistry, previous);
}
}
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry,
ApolloConfig previous);
@Override
public void setFallBackLoader(ConfigLoader configLoader) {
this.fallback = configLoader;
}
@Override
public void setFallBackLoader(ConfigLoader configLoader) {
this.fallback = configLoader;
}
}
......@@ -6,10 +6,10 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class InMemoryConfigLoader extends AbstractConfigLoader{
public class InMemoryConfigLoader extends AbstractConfigLoader {
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
}
......@@ -5,11 +5,12 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* Load config from local backup file
*
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalFileConfigLoader extends AbstractConfigLoader{
@Override
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
public class LocalFileConfigLoader extends AbstractConfigLoader {
@Override
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
}
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.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
......@@ -24,76 +26,80 @@ import java.util.Map;
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil, ConfigServiceLocator locator) {
this.restTemplate = restTemplate;
this.configUtil = configUtil;
this.serviceLocator = locator;
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil,
ConfigServiceLocator locator) {
this.restTemplate = restTemplate;
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) {
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());
}
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;
ResponseEntity<ApolloConfig> response;
try {
// TODO retry
response = restTemplate.exchange(uri
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, paramMap);
} catch (Throwable ex) {
throw ex;
}
if (response == null) {
throw new RuntimeException("Load apollo config failed, response is null");
}
if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
return null;
}
@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;
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
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");
}
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
return services.get(0).getHomepageUrl();
}
}
......@@ -6,31 +6,31 @@ import com.google.common.base.MoreObjects;
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloRegistry {
private long appId;
private String version;
private long appId;
private String version;
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public String getVersion() {
return version;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("version", version)
.toString();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("version", version)
.toString();
}
}
......@@ -6,47 +6,48 @@ import com.ctrip.apollo.client.enums.PropertyChangeType;
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertyChange {
private String propertyName;
private Object oldValue;
private Object newValue;
private PropertyChangeType changeType;
public PropertyChange(String propertyName, Object oldValue, Object newValue, PropertyChangeType changeType) {
this.propertyName = propertyName;
this.oldValue = oldValue;
this.newValue = newValue;
this.changeType = changeType;
}
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public Object getOldValue() {
return oldValue;
}
public void setOldValue(Object oldValue) {
this.oldValue = oldValue;
}
public Object getNewValue() {
return newValue;
}
public void setNewValue(Object newValue) {
this.newValue = newValue;
}
public PropertyChangeType getChangeType() {
return changeType;
}
public void setChangeType(PropertyChangeType changeType) {
this.changeType = changeType;
}
private String propertyName;
private Object oldValue;
private Object newValue;
private PropertyChangeType changeType;
public PropertyChange(String propertyName, Object oldValue, Object newValue,
PropertyChangeType changeType) {
this.propertyName = propertyName;
this.oldValue = oldValue;
this.newValue = newValue;
this.changeType = changeType;
}
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public Object getOldValue() {
return oldValue;
}
public void setOldValue(Object oldValue) {
this.oldValue = oldValue;
}
public Object getNewValue() {
return newValue;
}
public void setNewValue(Object newValue) {
this.newValue = newValue;
}
public PropertyChangeType getChangeType() {
return changeType;
}
public void setChangeType(PropertyChangeType changeType) {
this.changeType = changeType;
}
}
package com.ctrip.apollo.client.model;
import com.google.common.collect.Lists;
import org.springframework.core.env.CompositePropertySource;
import java.util.List;
......@@ -9,36 +10,37 @@ import java.util.List;
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertySourceReloadResult {
private CompositePropertySource propertySource;
private List<PropertyChange> changes;
public PropertySourceReloadResult(CompositePropertySource propertySource) {
this.propertySource = propertySource;
changes = Lists.newArrayList();
}
public PropertySourceReloadResult(CompositePropertySource propertySource, List<PropertyChange> changes) {
this.propertySource = propertySource;
this.changes = changes;
}
public CompositePropertySource getPropertySource() {
return propertySource;
}
public void setPropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource;
}
public List<PropertyChange> getChanges() {
return changes;
}
public void setChanges(List<PropertyChange> changes) {
this.changes = changes;
}
public boolean hasChanges() {
return !changes.isEmpty();
}
private CompositePropertySource propertySource;
private List<PropertyChange> changes;
public PropertySourceReloadResult(CompositePropertySource propertySource) {
this.propertySource = propertySource;
changes = Lists.newArrayList();
}
public PropertySourceReloadResult(CompositePropertySource propertySource,
List<PropertyChange> changes) {
this.propertySource = propertySource;
this.changes = changes;
}
public CompositePropertySource getPropertySource() {
return propertySource;
}
public void setPropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource;
}
public List<PropertyChange> getChanges() {
return changes;
}
public void setChanges(List<PropertyChange> changes) {
this.changes = changes;
}
public boolean hasChanges() {
return !changes.isEmpty();
}
}
......@@ -7,18 +7,18 @@ import org.slf4j.LoggerFactory;
* @author Jason Song(song_s@ctrip.com)
*/
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 {
if (loader == null) {
logger.info("Using system class loader");
loader = ClassLoader.getSystemClassLoader();
}
static {
if (loader == null) {
logger.info("Using system class loader");
loader = ClassLoader.getSystemClassLoader();
}
}
public static ClassLoader getLoader() {
return loader;
}
public static ClassLoader getLoader() {
return loader;
}
}
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.Predicates;
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.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
......@@ -21,68 +23,69 @@ import java.util.concurrent.TimeUnit;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigUtil {
public static final String APOLLO_PROPERTY = "apollo.properties";
//TODO read from config?
private static final int refreshInterval = 5;
private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
public static final String APOLLO_PROPERTY = "apollo.properties";
//TODO read from config?
private static final int refreshInterval = 5;
private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
private static ConfigUtil configUtil = new ConfigUtil();
private ApplicationContext applicationContext;
private ConfigUtil() {
}
private static ConfigUtil configUtil = new ConfigUtil();
private ApplicationContext applicationContext;
public static ConfigUtil getInstance() {
return configUtil;
}
private ConfigUtil() {
}
public String getCluster() {
// TODO return the actual cluster
return "default";
}
public static ConfigUtil getInstance() {
return configUtil;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public String getCluster() {
// TODO return the actual cluster
return "default";
}
public int getRefreshInterval() {
return refreshInterval;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public TimeUnit getRefreshTimeUnit() {
return refreshIntervalTimeUnit;
}
public int getRefreshInterval() {
return refreshInterval;
}
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
List<URL> resourceUrls =
Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY));
List<ApolloRegistry> registries =
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() {
@Override
public ApolloRegistry apply(URL input) {
Properties properties = loadPropertiesFromResourceURL(input);
if (properties == null || !properties.containsKey(Constants.APP_ID)) {
return null;
}
ApolloRegistry registry = new ApolloRegistry();
registry.setAppId(Long.parseLong(properties.getProperty(Constants.APP_ID)));
registry.setVersion(properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME));
return registry;
}
}).filter(Predicates.notNull()).toList();
return registries;
}
public TimeUnit getRefreshTimeUnit() {
return refreshIntervalTimeUnit;
}
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
List<URL> resourceUrls =
Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY));
List<ApolloRegistry> registries =
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() {
@Override
public ApolloRegistry apply(URL input) {
Properties properties = loadPropertiesFromResourceURL(input);
if (properties == null || !properties.containsKey(Constants.APP_ID)) {
return null;
}
ApolloRegistry registry = new ApolloRegistry();
registry.setAppId(Long.parseLong(properties.getProperty(Constants.APP_ID)));
registry.setVersion(
properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME));
return registry;
}
}).filter(Predicates.notNull()).toList();
return registries;
}
Properties loadPropertiesFromResourceURL(URL resourceUrl) {
Resource resource = applicationContext.getResource(resourceUrl.toExternalForm());
if (resource == null || !resource.exists()) {
return null;
}
try {
return PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
return null;
Properties loadPropertiesFromResourceURL(URL resourceUrl) {
Resource resource = applicationContext.getResource(resourceUrl.toExternalForm());
if (resource == null || !resource.exists()) {
return null;
}
try {
return PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8"));
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
}
......@@ -3,14 +3,15 @@ package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderManagerTest;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest;
import com.ctrip.apollo.client.util.ConfigUtilTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class,
ConfigUtilTest.class
ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class,
ConfigUtilTest.class
})
public class AllTests {
......
......@@ -3,6 +3,7 @@ package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -25,110 +26,126 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloConfigManagerTest {
private ApolloConfigManager apolloConfigManager;
@Mock
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigurableApplicationContext applicationContext;
@Mock
private ConfigurableEnvironment env;
@Mock
private MutablePropertySources mutablePropertySources;
@Mock
private BeanDefinitionRegistry beanDefinitionRegistry;
@Mock
private RefreshScope scope;
@Before
public void setUp() {
apolloConfigManager = spy(new ApolloConfigManager());
when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloConfigManager, "scope", scope);
}
@After
public void tearDown() throws Exception {
AtomicReference<ApolloConfigManager> singletonProtector =
(AtomicReference<ApolloConfigManager>)ReflectionTestUtils.getField(ApolloConfigManager.class, "singletonProtector");
singletonProtector.set(null);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() {
ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
apolloConfigManager.setApplicationContext(someInvalidApplication);
}
@Test
public void testInitializePropertySourceSuccessfully() {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource> captor = ArgumentCaptor.forClass(CompositePropertySource.class);
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
apolloConfigManager.initializePropertySource();
verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
final CompositePropertySource insertedPropertySource = captor.getValue();
assertEquals(insertedPropertySource, somePropertySource);
}
@Test
public void testPostProcessBeanDefinitionRegistry() {
doNothing().when(apolloConfigManager).initializePropertySource();
apolloConfigManager.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);
verify(beanDefinitionRegistry, times(2)).registerBeanDefinition(anyString(), any(BeanDefinition.class));
}
@Test
public void testUpdatePropertySourceWithChanges() throws Exception {
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = mock(List.class);
when(somePropertySourceReloadResult.hasChanges()).thenReturn(true);
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
when(somePropertySourceReloadResult.getChanges()).thenReturn(someChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
assertEquals(someChanges, result);
verify(scope, times(1)).refreshAll();
}
@Test
public void testUpdatePropertySourceWithNoChange() throws Exception {
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> emptyChanges = Collections.emptyList();
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();
}
private ApolloConfigManager apolloConfigManager;
@Mock
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigurableApplicationContext applicationContext;
@Mock
private ConfigurableEnvironment env;
@Mock
private MutablePropertySources mutablePropertySources;
@Mock
private BeanDefinitionRegistry beanDefinitionRegistry;
@Mock
private RefreshScope scope;
@Before
public void setUp() {
apolloConfigManager = spy(new ApolloConfigManager());
when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloConfigManager, "scope", scope);
}
@After
public void tearDown() throws Exception {
AtomicReference<ApolloConfigManager> singletonProtector =
(AtomicReference<ApolloConfigManager>) ReflectionTestUtils
.getField(ApolloConfigManager.class, "singletonProtector");
singletonProtector.set(null);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() {
ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
apolloConfigManager.setApplicationContext(someInvalidApplication);
}
@Test
public void testInitializePropertySourceSuccessfully() {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource>
captor =
ArgumentCaptor.forClass(CompositePropertySource.class);
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
apolloConfigManager.initializePropertySource();
verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
final CompositePropertySource insertedPropertySource = captor.getValue();
assertEquals(insertedPropertySource, somePropertySource);
}
@Test
public void testPostProcessBeanDefinitionRegistry() {
doNothing().when(apolloConfigManager).initializePropertySource();
apolloConfigManager.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);
verify(beanDefinitionRegistry, times(2))
.registerBeanDefinition(anyString(), any(BeanDefinition.class));
}
@Test
public void testUpdatePropertySourceWithChanges() throws Exception {
PropertySourceReloadResult
somePropertySourceReloadResult =
mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = mock(List.class);
when(somePropertySourceReloadResult.hasChanges()).thenReturn(true);
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
when(somePropertySourceReloadResult.getChanges()).thenReturn(someChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
assertEquals(someChanges, result);
verify(scope, times(1)).refreshAll();
}
@Test
public void testUpdatePropertySourceWithNoChange() throws Exception {
PropertySourceReloadResult
somePropertySourceReloadResult =
mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> emptyChanges = Collections.emptyList();
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;
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.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
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.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.test.util.ReflectionTestUtils;
......@@ -26,173 +26,192 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigLoaderManagerTest {
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigLoader configLoader;
@Mock
private ConfigUtil configUtil;
@Before
public void setUp() {
configLoaderManager = spy(new ConfigLoaderManager(configLoader, configUtil));
}
@Test
public void testLoadPropertySource() throws Exception {
long someAppId = 100;
long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloConfig anotherApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
Map<String, Object> anotherMap = mock(Map.class);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getAppId()).thenReturn(anotherAppId);
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
doReturn(anotherApolloConfig).when(configLoaderManager).loadSingleApolloConfig(anotherApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(anotherApolloConfig.getConfigurations()).thenReturn(anotherMap);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertEquals(2, result.getPropertySources().size());
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>)input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap, anotherMap)));
}
@Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
Exception someException = mock(Exception.class);
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry));
doThrow(someException).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
configLoaderManager.loadPropertySource();
}
@Test
public void testLoadApolloConfigsWithNoApolloRegistry() throws Exception {
when(configUtil.loadApolloRegistries()).thenReturn(null);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertTrue(result.getPropertySources().isEmpty());
}
@Test
public void testLoadSingleApolloConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = Maps.newHashMap();
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
doReturn(null).when(configLoaderManager).getPreviousApolloConfig(someApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(configLoader.loadApolloConfig(someApolloRegistry, previousConfig)).thenReturn(someApolloConfig);
ApolloConfig result = configLoaderManager.loadSingleApolloConfig(someApolloRegistry);
assertEquals(someMap, result.getConfigurations());
}
@Test
public void testReloadPropertySource() throws Exception {
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
List<PropertyChange> someChanges = mock(List.class);
ReflectionTestUtils.setField(configLoaderManager, "apolloRegistries", Lists.newArrayList(someApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
doReturn(someChanges).when(configLoaderManager).calcPropertyChanges(anyList(), anyList());
PropertySourceReloadResult result = configLoaderManager.reloadPropertySource();
assertEquals(1, result.getPropertySource().getPropertySources().size());
assertEquals(someChanges, result.getChanges());
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySource().getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>)input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap)));
}
@Test
public void testCalcPropertyChanges() throws Exception {
long someAppId = 1;
Map<String, Object> someConfig = Maps.newHashMap();
someConfig.put("key1", "val1");
someConfig.put("key2", "val2");
Map<String, Object> anotherConfig = Maps.newHashMap();
anotherConfig.put("key1", "val11");
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());
}
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigLoader configLoader;
@Mock
private ConfigUtil configUtil;
@Before
public void setUp() {
configLoaderManager = spy(new ConfigLoaderManager(configLoader, configUtil));
}
@Test
public void testLoadPropertySource() throws Exception {
long someAppId = 100;
long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry
anotherApolloRegistry =
assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloConfig anotherApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
Map<String, Object> anotherMap = mock(Map.class);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getAppId()).thenReturn(anotherAppId);
when(configUtil.loadApolloRegistries())
.thenReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
doReturn(anotherApolloConfig).when(configLoaderManager)
.loadSingleApolloConfig(anotherApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(anotherApolloConfig.getConfigurations()).thenReturn(anotherMap);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertEquals(2, result.getPropertySources().size());
List<Map<String, Object>>
resultMaps =
FluentIterable.from(result.getPropertySources())
.transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>) input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap, anotherMap)));
}
@Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
Exception someException = mock(Exception.class);
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry));
doThrow(someException).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
configLoaderManager.loadPropertySource();
}
@Test
public void testLoadApolloConfigsWithNoApolloRegistry() throws Exception {
when(configUtil.loadApolloRegistries()).thenReturn(null);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertTrue(result.getPropertySources().isEmpty());
}
@Test
public void testLoadSingleApolloConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = Maps.newHashMap();
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
doReturn(null).when(configLoaderManager).getPreviousApolloConfig(someApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(configLoader.loadApolloConfig(someApolloRegistry, previousConfig))
.thenReturn(someApolloConfig);
ApolloConfig result = configLoaderManager.loadSingleApolloConfig(someApolloRegistry);
assertEquals(someMap, result.getConfigurations());
}
@Test
public void testReloadPropertySource() throws Exception {
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
List<PropertyChange> someChanges = mock(List.class);
ReflectionTestUtils
.setField(configLoaderManager, "apolloRegistries", Lists.newArrayList(someApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
doReturn(someChanges).when(configLoaderManager).calcPropertyChanges(anyList(), anyList());
PropertySourceReloadResult result = configLoaderManager.reloadPropertySource();
assertEquals(1, result.getPropertySource().getPropertySources().size());
assertEquals(someChanges, result.getChanges());
List<Map<String, Object>>
resultMaps =
FluentIterable.from(result.getPropertySource().getPropertySources())
.transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>) input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap)));
}
@Test
public void testCalcPropertyChanges() throws Exception {
long someAppId = 1;
Map<String, Object> someConfig = Maps.newHashMap();
someConfig.put("key1", "val1");
someConfig.put("key2", "val2");
Map<String, Object> anotherConfig = Maps.newHashMap();
anotherConfig.put("key1", "val11");
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();
assertTrue(changeResult.containsAll(
Lists.newArrayList(
"key1-" + PropertyChangeType.MODIFIED,
"key2-" + PropertyChangeType.DELETED,
"key3-" + PropertyChangeType.NEW
)));
}
assertTrue(changeResult.containsAll(
Lists.newArrayList(
"key1-" + PropertyChangeType.MODIFIED,
"key2-" + PropertyChangeType.DELETED,
"key3-" + PropertyChangeType.NEW
)));
}
ApolloConfig assembleApolloConfig(long appId, Map<String, Object> configurations) {
String someCluster = "someCluster";
String someVersion = "someVersion";
long someReleaseId = 1;
ApolloConfig assembleApolloConfig(long appId, Map<String, Object> configurations) {
String someCluster = "someCluster";
String someVersion = "someVersion";
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) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
return someApolloRegistry;
}
return someApolloRegistry;
}
}
package com.ctrip.apollo.client.loader.impl;
import static org.junit.Assert.assertEquals;
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;
import java.util.ArrayList;
import java.util.List;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
import org.junit.Before;
import org.junit.Test;
......@@ -25,116 +17,132 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class RemoteConfigLoaderTest {
private RemoteConfigLoader remoteConfigLoader;
@Mock
private RestTemplate restTemplate;
private ConfigUtil configUtil;
@Mock
private ConfigServiceLocator serviceLocater;
@Mock
private ResponseEntity<ApolloConfig> someResponse;
@Before
public void setUp() {
configUtil = spy(ConfigUtil.getInstance());
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater));
}
@Test
public void testLoadApolloConfig() throws Exception {
String someServerUrl = "http://someUrl";
String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
ApolloService someService = new ApolloService();
someService.setHomepageUrl(someServerUrl);
List<ApolloService> someServices = new ArrayList<>();
someServices.add(someService);
when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
assertEquals(apolloConfig, result);
}
@Test
public void testGetRemoteConfig() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(someApolloConfig);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
assertEquals(someApolloConfig, result);
}
@Test(expected = RuntimeException.class)
public void testGetRemoteConfigWithServerError() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
when(someResponse.getStatusCode()).thenReturn(someErrorCode);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
}
@Test
public void testGetRemoteConfigWith304Response() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
assertNull(result);
}
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
return someApolloRegistry;
}
private RemoteConfigLoader remoteConfigLoader;
@Mock
private RestTemplate restTemplate;
private ConfigUtil configUtil;
@Mock
private ConfigServiceLocator serviceLocater;
@Mock
private ResponseEntity<ApolloConfig> someResponse;
@Before
public void setUp() {
configUtil = spy(ConfigUtil.getInstance());
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater));
}
@Test
public void testLoadApolloConfig() throws Exception {
String someServerUrl = "http://someUrl";
String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
ApolloService someService = new ApolloService();
someService.setHomepageUrl(someServerUrl);
List<ApolloService> someServices = new ArrayList<>();
someServices.add(someService);
when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
assertEquals(apolloConfig, result);
}
@Test
public void testGetRemoteConfig() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(someApolloConfig);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
ApolloConfig
result =
remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
previousConfig);
assertEquals(someApolloConfig, result);
}
@Test(expected = RuntimeException.class)
public void testGetRemoteConfigWithServerError() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
when(someResponse.getStatusCode()).thenReturn(someErrorCode);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
previousConfig);
}
@Test
public void testGetRemoteConfigWith304Response() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
ApolloConfig
result =
remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry,
previousConfig);
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;
import com.google.common.collect.Lists;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -19,60 +21,65 @@ import java.util.Properties;
import static org.junit.Assert.assertEquals;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigUtilTest {
private ConfigUtil configUtil;
@Mock
private ConfigurableApplicationContext applicationContext;
private ConfigUtil configUtil;
@Mock
private ConfigurableApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
configUtil = spy(ConfigUtil.getInstance());
@Before
public void setUp() throws Exception {
configUtil = spy(ConfigUtil.getInstance());
configUtil.setApplicationContext(applicationContext);
}
configUtil.setApplicationContext(applicationContext);
}
@Test
public void testLoadApolloRegistriesSuccessfully() throws Exception {
Properties someProperties = mock(Properties.class);
preparePropertiesFromLocalResource(someProperties);
@Test
public void testLoadApolloRegistriesSuccessfully() throws Exception {
Properties someProperties = mock(Properties.class);
preparePropertiesFromLocalResource(someProperties);
String someAppId = "1";
String someVersionId = "someVersion";
String someAppId = "1";
String someVersionId = "someVersion";
when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true);
when(someProperties.getProperty(Constants.APP_ID)).thenReturn(someAppId);
when(someProperties.getProperty(eq(Constants.VERSION), anyString())).thenReturn(someVersionId);
when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true);
when(someProperties.getProperty(Constants.APP_ID)).thenReturn(someAppId);
when(someProperties.getProperty(eq(Constants.VERSION), anyString())).thenReturn(someVersionId);
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
ApolloRegistry apolloRegistry = apolloRegistries.get(0);
assertEquals(1, apolloRegistries.size());
assertEquals(Long.parseLong(someAppId), apolloRegistry.getAppId());
assertEquals(someVersionId, apolloRegistry.getVersion());
}
ApolloRegistry apolloRegistry = apolloRegistries.get(0);
assertEquals(1, apolloRegistries.size());
assertEquals(Long.parseLong(someAppId), apolloRegistry.getAppId());
assertEquals(someVersionId, apolloRegistry.getVersion());
}
@Test
public void testLoadApolloRegistriesError() throws Exception {
preparePropertiesFromLocalResource(null);
@Test
public void testLoadApolloRegistriesError() throws Exception {
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 {
ClassLoader someClassLoader = mock(ClassLoader.class);
Thread.currentThread().setContextClassLoader(someClassLoader);
URL someUrl = new URL("http", "somepath/", "someFile");
Enumeration<URL> someResourceUrls = Collections.enumeration(Lists.newArrayList(someUrl));
private void preparePropertiesFromLocalResource(Properties someProperties) throws IOException {
ClassLoader someClassLoader = mock(ClassLoader.class);
Thread.currentThread().setContextClassLoader(someClassLoader);
URL someUrl = new URL("http", "somepath/", "someFile");
Enumeration<URL> someResourceUrls = Collections.enumeration(Lists.newArrayList(someUrl));
when(someClassLoader.getResources(anyString())).thenReturn(someResourceUrls);
doReturn(someProperties).when(configUtil).loadPropertiesFromResourceURL(someUrl);
}
when(someClassLoader.getResources(anyString())).thenReturn(someResourceUrls);
doReturn(someProperties).when(configUtil).loadPropertiesFromResourceURL(someUrl);
}
}
......@@ -5,7 +5,7 @@
<PatternLayout pattern="[apollo-client][%t]%d %-5p [%c] %m%n"/>
</Console>
<Async name="Async" includeLocation="true">
<AppenderRef ref="Console" />
<AppenderRef ref="Console"/>
</Async>
</appenders>
<loggers>
......
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-configservice</artifactId>
<name>Apollo ConfigService</name>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId>
</dependency>
<!-- end of apollo -->
<!-- redis -->
<!-- <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId>
</dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId>
</dependency> -->
<!-- end of redis -->
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<exclusions>
<exclusion>
<artifactId>
spring-cloud-starter-archaius
</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>ribbon-eureka</artifactId>
<groupId>com.netflix.ribbon</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-core</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-ec2</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-autoscaling</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-sts</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-route53</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- end of eureka -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-configservice</artifactId>
<name>Apollo ConfigService</name>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId>
</dependency>
<!-- end of apollo -->
<!-- redis -->
<!-- <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId>
</dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId>
</dependency> -->
<!-- end of redis -->
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<exclusions>
<exclusion>
<artifactId>
spring-cloud-starter-archaius
</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
<exclusion>
<artifactId>ribbon-eureka</artifactId>
<groupId>com.netflix.ribbon</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-core</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-ec2</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-autoscaling</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-sts</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
<exclusion>
<artifactId>aws-java-sdk-route53</artifactId>
<groupId>com.amazonaws</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- end of eureka -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
......@@ -6,15 +6,15 @@ import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Spring boot application entry point
*
*
* @author Jason Song(song_s@ctrip.com)
*/
@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ServerApplication.class).web(true).run(args);
}
}
......@@ -5,13 +5,14 @@ import org.springframework.boot.context.web.SpringBootServletInitializer;
/**
* Entry point for traditional web app
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ServerApplication.class);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ServerApplication.class);
}
}
......@@ -3,11 +3,17 @@ package com.ctrip.apollo.configservice.controller;
import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.service.ConfigService;
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.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -15,36 +21,38 @@ import java.io.IOException;
@RestController
@RequestMapping("/config")
public class ConfigController {
@Resource(name = "configService")
private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable long appId,
@PathVariable String clusterName,
@PathVariable String versionName,
@RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId,
HttpServletResponse response) throws IOException {
Version version = configService.loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load version with appId: %d, versionName: %s", appId, 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;
@Resource(name = "configService")
private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable long appId,
@PathVariable String clusterName,
@PathVariable String versionName,
@RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId,
HttpServletResponse response) throws IOException {
Version version = configService.loadVersionByAppIdAndVersionName(appId, versionName);
if (version == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load version with appId: %d, versionName: %s", appId,
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;
}
}
package com.ctrip.apollo.metaservice.controller;
import java.util.ArrayList;
import java.util.List;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.ctrip.apollo.metaservice.service.DiscoveryService;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.ctrip.apollo.metaservice.service.DiscoveryService;
import com.netflix.appinfo.InstanceInfo;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/services")
......
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.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
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
public class DiscoveryService {
......@@ -27,7 +27,7 @@ public class DiscoveryService {
return application != null ? application.getInstances() : new ArrayList<>();
}
public List<InstanceInfo> getAdminServiceInstances(){
public List<InstanceInfo> getAdminServiceInstances() {
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_ADMINSERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
......
package com.ctrip.apollo.configservice;
import com.ctrip.apollo.ServerApplication;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
......
package com.ctrip.apollo.configservice;
import com.ctrip.apollo.configservice.controller.ConfigControllerTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import com.ctrip.apollo.configservice.controller.ConfigControllerTest;
@RunWith(Suite.class)
@SuiteClasses({
ConfigControllerTest.class
ConfigControllerTest.class
})
public class AllTests {
......
......@@ -2,8 +2,8 @@ package com.ctrip.apollo.configservice.controller;
import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.configservice.controller.ConfigController;
import com.ctrip.apollo.core.dto.ApolloConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -15,98 +15,128 @@ import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals;
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)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigControllerTest {
private ConfigController configController;
@Mock
private ConfigService configService;
@Before
public void setUp() throws Exception {
configController = new ConfigController();
ReflectionTestUtils.setField(configController, "configService", configService);
}
@Test
public void testQueryConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName)).thenReturn(someApolloConfig);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse);
assertEquals(someApolloConfig, result);
verify(configService, times(1)).loadVersionByAppIdAndVersionName(someAppId, someVersionName);
verify(configService, times(1)).loadConfigByVersionAndClusterName(someVersion, someClusterName);
}
@Test
public void testQueryConfigWithVersionNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(null);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse);
assertNull(result);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
}
@Test
public void testQueryConfigWithApolloConfigNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName)).thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName)).thenReturn(null);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId, someResponse);
assertNull(result);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), 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());
}
private ConfigController configController;
@Mock
private ConfigService configService;
@Before
public void setUp() throws Exception {
configController = new ConfigController();
ReflectionTestUtils.setField(configController, "configService", configService);
}
@Test
public void testQueryConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
.thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName))
.thenReturn(someApolloConfig);
ApolloConfig
result =
configController
.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
someResponse);
assertEquals(someApolloConfig, result);
verify(configService, times(1)).loadVersionByAppIdAndVersionName(someAppId, someVersionName);
verify(configService, times(1)).loadConfigByVersionAndClusterName(someVersion, someClusterName);
}
@Test
public void testQueryConfigWithVersionNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
.thenReturn(null);
ApolloConfig
result =
configController
.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
someResponse);
assertNull(result);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
}
@Test
public void testQueryConfigWithApolloConfigNotFound() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersion";
long someClientSideReleaseId = 1;
long someServerSideNewReleaseId = 2;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Version someVersion = mock(Version.class);
when(configService.loadVersionByAppIdAndVersionName(someAppId, someVersionName))
.thenReturn(someVersion);
when(someVersion.getReleaseId()).thenReturn(someServerSideNewReleaseId);
when(configService.loadConfigByVersionAndClusterName(someVersion, someClusterName))
.thenReturn(null);
ApolloConfig
result =
configController
.queryConfig(someAppId, someClusterName, someVersionName, someClientSideReleaseId,
someResponse);
assertNull(result);
verify(someResponse, times(1)).sendError(eq(HttpServletResponse.SC_NOT_FOUND), 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"?>
<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">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-core</artifactId>
<name>Apollo Core</name>
<packaging>jar</packaging>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-core</artifactId>
<name>Apollo Core</name>
<packaging>jar</packaging>
<dependencies>
<!-- json -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- end of json -->
<!-- util -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependencies>
<!-- json -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- end of json -->
<!-- util -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- end of log -->
</dependencies>
</dependencies>
</project>
package com.ctrip.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;
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) {
m_env = env;
m_env = env;
}
public static Env getEnv() {
return m_env;
return m_env;
}
}
......@@ -2,6 +2,6 @@ package com.ctrip.apollo.core;
public interface Constants {
String DEFAULT_CLUSTER_NAME = "default-cluster-name";
String DEFAULT_CLUSTER_NAME = "default-cluster-name";
}
package com.ctrip.apollo.core;
import com.ctrip.apollo.Apollo.Env;
import java.util.HashMap;
import java.util.Map;
import com.ctrip.apollo.Apollo.Env;
public class MetaDomainConsts {
public static final String DEFAULT_PORT = "8080";
......
......@@ -7,6 +7,6 @@ public class ServiceNameConsts {
public static final String APOLLO_CONFIGSERVICE = "apollo-configservice";
public static final String APOLLO_ADMINSERVICE = "apollo-adminservice";
public static final String APOLLO_PORTAL = "apollo-portal";
}
package com.ctrip.apollo.core.dto;
import com.google.common.base.MoreObjects;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Comparator;
import java.util.Map;
/**
......@@ -12,82 +12,82 @@ import java.util.Map;
*/
public class ApolloConfig implements Comparable<ApolloConfig> {
private long appId;
private String cluster;
private String version;
private Map<String, Object> configurations;
private long releaseId;
private int order;
@JsonCreator
public ApolloConfig(@JsonProperty("appId") long appId,
@JsonProperty("cluster") String cluster,
@JsonProperty("version") String version,
@JsonProperty("releaseId") long releaseId) {
super();
this.appId = appId;
this.cluster = cluster;
this.version = version;
this.releaseId = releaseId;
}
public Map<String, Object> getConfigurations() {
return configurations;
}
public void setConfigurations(Map<String, Object> configurations) {
this.configurations = configurations;
}
public long getAppId() {
return appId;
}
public String getCluster() {
return cluster;
}
public String getVersion() {
return version;
}
public long getReleaseId() {
return releaseId;
private long appId;
private String cluster;
private String version;
private Map<String, Object> configurations;
private long releaseId;
private int order;
@JsonCreator
public ApolloConfig(@JsonProperty("appId") long appId,
@JsonProperty("cluster") String cluster,
@JsonProperty("version") String version,
@JsonProperty("releaseId") long releaseId) {
super();
this.appId = appId;
this.cluster = cluster;
this.version = version;
this.releaseId = releaseId;
}
public Map<String, Object> getConfigurations() {
return configurations;
}
public void setConfigurations(Map<String, Object> configurations) {
this.configurations = configurations;
}
public long getAppId() {
return appId;
}
public String getCluster() {
return cluster;
}
public String getVersion() {
return version;
}
public long getReleaseId() {
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;
}
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 o) {
if (o == null || this.getOrder() > o.getOrder()) {
return 1;
}
if (o.getOrder() > this.getOrder()) {
return -1;
}
return 0;
if (toCompare.getOrder() > this.getOrder()) {
return -1;
}
return 0;
}
}
......@@ -2,33 +2,33 @@ package com.ctrip.apollo.core.dto;
public class ClusterDTO {
private long id;
private long id;
private String name;
private String name;
private long appId;
private long appId;
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
}
......@@ -4,122 +4,122 @@ import java.util.Date;
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){
this.key = key;
this.value = value;
}
public ConfigItemDTO(String key, String value) {
this.key = key;
this.value = value;
}
public long getId() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public long getClusterId() {
return clusterId;
}
public long getClusterId() {
return clusterId;
}
public void setClusterId(long clusterId) {
this.clusterId = clusterId;
}
public void setClusterId(long clusterId) {
this.clusterId = clusterId;
}
public String getClusterName() {
return clusterName;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public String getKey() {
return key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
public String getDataChangeLastModifiedBy() {
return DataChangeLastModifiedBy;
}
public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy;
}
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
DataChangeLastModifiedBy = dataChangeLastModifiedBy;
}
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
}
public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime;
}
public Date getDataChangeLastModifiedTime() {
return dataChangeLastModifiedTime;
}
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
}
public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
}
public String getComment() {
return comment;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
......@@ -2,47 +2,47 @@ package com.ctrip.apollo.core.dto;
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() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public long getReleaseId() {
return releaseId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public String getClusterName() {
return clusterName;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getConfigurations() {
return configurations;
}
public String getConfigurations() {
return configurations;
}
public void setConfigurations(String configurations) {
this.configurations = configurations;
}
public void setConfigurations(String configurations) {
this.configurations = configurations;
}
}
......@@ -2,57 +2,57 @@ package com.ctrip.apollo.core.dto;
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() {
return id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public long getReleaseId() {
return releaseId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public Long getParentVersion() {
return parentVersion;
}
public Long getParentVersion() {
return parentVersion;
}
public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion;
}
public void setParentVersion(Long parentVersion) {
this.parentVersion = parentVersion;
}
}
package com.ctrip.apollo.core.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
......@@ -7,85 +10,84 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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() {
return m_threadGroup;
}
public static ThreadGroup getThreadGroup() {
return threadGroup;
}
public static ThreadFactory create(String namePrefix, boolean daemon) {
return new ApolloThreadFactory(namePrefix, daemon);
}
public static ThreadFactory create(String namePrefix, boolean daemon) {
return new ApolloThreadFactory(namePrefix, daemon);
}
public static boolean waitAllShutdown(int timeoutInMillis) {
ThreadGroup group = getThreadGroup();
Thread[] activeThreads = new Thread[group.activeCount()];
group.enumerate(activeThreads);
Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
Set<Thread> dies = new HashSet<Thread>();
log.info("Current ACTIVE thread count is: {}", alives.size());
long expire = System.currentTimeMillis() + timeoutInMillis;
while (System.currentTimeMillis() < expire) {
classify(alives, dies, new ClassifyStandard<Thread>() {
@Override
public boolean satisfy(Thread t) {
return !t.isAlive() || t.isInterrupted() || t.isDaemon();
}
});
if (alives.size() > 0) {
log.info("Alive apollo threads: {}", alives);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// ignore
}
} else {
log.info("All apollo threads are shutdown.");
return true;
}
public static boolean waitAllShutdown(int timeoutInMillis) {
ThreadGroup group = getThreadGroup();
Thread[] activeThreads = new Thread[group.activeCount()];
group.enumerate(activeThreads);
Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
Set<Thread> dies = new HashSet<Thread>();
log.info("Current ACTIVE thread count is: {}", alives.size());
long expire = System.currentTimeMillis() + timeoutInMillis;
while (System.currentTimeMillis() < expire) {
classify(alives, dies, new ClassifyStandard<Thread>() {
@Override
public boolean satisfy(Thread thread) {
return !thread.isAlive() || thread.isInterrupted() || thread.isDaemon();
}
});
if (alives.size() > 0) {
log.info("Alive apollo threads: {}", alives);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException ex) {
// ignore
}
log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}", alives);
return false;
} 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);
return false;
}
private static interface ClassifyStandard<T> {
boolean satisfy(T t);
}
private static interface ClassifyStandard<T> {
boolean satisfy(T thread);
}
private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
Set<T> s = new HashSet<>();
for (T t : src) {
if (standard.satisfy(t)) {
s.add(t);
}
}
src.removeAll(s);
des.addAll(s);
private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
Set<T> set = new HashSet<>();
for (T t : src) {
if (standard.satisfy(t)) {
set.add(t);
}
}
src.removeAll(set);
des.addAll(set);
}
private ApolloThreadFactory(String namePrefix, boolean daemon) {
m_namePrefix = namePrefix;
m_daemon = daemon;
}
private ApolloThreadFactory(String namePrefix, boolean daemon) {
this.namePrefix = namePrefix;
this.daemon = daemon;
}
public Thread newThread(Runnable r) {
Thread t = new Thread(m_threadGroup, r,//
m_threadGroup.getName() + "-" + m_namePrefix + "-" + m_threadNumber.getAndIncrement());
t.setDaemon(m_daemon);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(threadGroup, runnable,//
threadGroup.getName() + "-" + namePrefix + "-" + threadNumber.getAndIncrement());
thread.setDaemon(daemon);
if (thread.getPriority() != Thread.NORM_PRIORITY) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}
......@@ -7,17 +7,17 @@ import java.util.List;
public class DNSUtil {
public static List<String> resolve(String domainName) throws UnknownHostException {
List<String> result = new ArrayList<String>();
public static List<String> resolve(String domainName) throws UnknownHostException {
List<String> result = new ArrayList<String>();
InetAddress[] addresses = InetAddress.getAllByName(domainName);
if (addresses != null) {
for (InetAddress addr : addresses) {
result.add(addr.getHostAddress());
}
}
InetAddress[] addresses = InetAddress.getAllByName(domainName);
if (addresses != null) {
for (InetAddress addr : addresses) {
result.add(addr.getHostAddress());
}
}
return result;
}
return result;
}
}
......@@ -5,369 +5,353 @@ import java.util.Iterator;
public class StringUtils {
public static final String EMPTY = "";
public static final String EMPTY = "";
/**
* <p>
* Checks if a String is empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>
* NOTE: This method changed in Lang version 2.0. It no longer trims the String. That functionality is available in isBlank().
* </p>
*
* @param str
* the String to check, may be null
* @return <code>true</code> if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
* <p>
* Checks if a String is empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>
* NOTE: This method changed in Lang version 2.0. It no longer trims the String. That functionality is available in isBlank().
* </p>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
* <p>
* Checks if a String is whitespace, empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
*
* @param str
* the String to check, may be null
* @return <code>true</code> if the String is null, empty or whitespace
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* <p>
* Checks if a String is whitespace, empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is null, empty or whitespace
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* <p>
* 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>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToNull(String)}.
* </p>
*
* <pre>
* StringUtils.trimToNull(null) = null
* StringUtils.trimToNull("") = null
* StringUtils.trimToNull(" ") = null
* StringUtils.trimToNull("abc") = "abc"
* StringUtils.trimToNull(" abc ") = "abc"
* </pre>
*
* @param str
* 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
* @since 2.0
*/
public static String trimToNull(String str) {
String ts = trim(str);
return isEmpty(ts) ? null : ts;
}
/**
* <p>
* 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>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToNull(String)}.
* </p>
*
* <pre>
* StringUtils.trimToNull(null) = null
* StringUtils.trimToNull("") = null
* StringUtils.trimToNull(" ") = null
* StringUtils.trimToNull("abc") = "abc"
* StringUtils.trimToNull(" abc ") = "abc"
* </pre>
*
* @param str 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
* @since 2.0
*/
public static String trimToNull(String str) {
String ts = trim(str);
return isEmpty(ts) ? null : ts;
}
/**
* <p>
* 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>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToEmpty(String)}.
* </p>
*
* <pre>
* StringUtils.trimToEmpty(null) = ""
* StringUtils.trimToEmpty("") = ""
* StringUtils.trimToEmpty(" ") = ""
* StringUtils.trimToEmpty("abc") = "abc"
* StringUtils.trimToEmpty(" abc ") = "abc"
* </pre>
*
* @param str
* the String to be trimmed, may be null
* @return the trimmed String, or an empty String if <code>null</code> input
* @since 2.0
*/
public static String trimToEmpty(String str) {
return str == null ? EMPTY : str.trim();
}
/**
* <p>
* 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>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToEmpty(String)}.
* </p>
*
* <pre>
* StringUtils.trimToEmpty(null) = ""
* StringUtils.trimToEmpty("") = ""
* StringUtils.trimToEmpty(" ") = ""
* StringUtils.trimToEmpty("abc") = "abc"
* StringUtils.trimToEmpty(" abc ") = "abc"
* </pre>
*
* @param str the String to be trimmed, may be null
* @return the trimmed String, or an empty String if <code>null</code> input
* @since 2.0
*/
public static String trimToEmpty(String str) {
return str == null ? EMPTY : str.trim();
}
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String, handling <code>null</code> by returning
* <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
* {@link #strip(String)}.
* </p>
*
* <p>
* To trim your choice of characters, use the {@link #strip(String, String)} methods.
* </p>
*
* <pre>
* StringUtils.trim(null) = null
* StringUtils.trim("") = ""
* StringUtils.trim(" ") = ""
* StringUtils.trim("abc") = "abc"
* StringUtils.trim(" abc ") = "abc"
* </pre>
*
* @param str
* the String to be trimmed, may be null
* @return the trimmed string, <code>null</code> if null String input
*/
public static String trim(String str) {
return str == null ? null : str.trim();
}
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String, handling <code>null</code> by returning
* <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
* {@link #strip(String)}.
* </p>
*
* <p>
* To trim your choice of characters, use the {@link #strip(String, String)} methods.
* </p>
*
* <pre>
* StringUtils.trim(null) = null
* StringUtils.trim("") = ""
* StringUtils.trim(" ") = ""
* StringUtils.trim("abc") = "abc"
* StringUtils.trim(" abc ") = "abc"
* </pre>
*
* @param str the String to be trimmed, may be null
* @return the trimmed string, <code>null</code> if null String input
*/
public static String trim(String str) {
return str == null ? null : str.trim();
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.equals(null, null) = true
* StringUtils.equals(null, "abc") = false
* StringUtils.equals("abc", null) = false
* StringUtils.equals("abc", "abc") = true
* StringUtils.equals("abc", "ABC") = false
* </pre>
*
* @see java.lang.String#equals(Object)
* @param str1
* the first String, may be null
* @param str2
* 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);
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.equals(null, null) = true
* StringUtils.equals(null, "abc") = false
* StringUtils.equals("abc", null) = false
* StringUtils.equals("abc", "abc") = true
* StringUtils.equals("abc", "ABC") = false
* </pre>
*
* @param str1 the first String, may be null
* @param str2 the second String, may be null
* @return <code>true</code> if the Strings are equal, case sensitive, or both <code>null</code>
* @see java.lang.String#equals(Object)
*/
public static boolean equals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal ignoring the case.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered equal. Comparison is case
* insensitive.
* </p>
*
* <pre>
* StringUtils.equalsIgnoreCase(null, null) = true
* StringUtils.equalsIgnoreCase(null, "abc") = false
* StringUtils.equalsIgnoreCase("abc", null) = false
* StringUtils.equalsIgnoreCase("abc", "abc") = true
* StringUtils.equalsIgnoreCase("abc", "ABC") = true
* </pre>
*
* @see java.lang.String#equalsIgnoreCase(String)
* @param str1
* the first String, may be null
* @param str2
* 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);
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal ignoring the case.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered equal. Comparison is case
* insensitive.
* </p>
*
* <pre>
* StringUtils.equalsIgnoreCase(null, null) = true
* StringUtils.equalsIgnoreCase(null, "abc") = false
* StringUtils.equalsIgnoreCase("abc", null) = false
* StringUtils.equalsIgnoreCase("abc", "abc") = true
* StringUtils.equalsIgnoreCase("abc", "ABC") = true
* </pre>
*
* @param str1 the first String, may be null
* @param str2 the second String, may be null
* @return <code>true</code> if the Strings are equal, case insensitive, or both <code>null</code>
* @see java.lang.String#equalsIgnoreCase(String)
*/
public static boolean equalsIgnoreCase(String str1, String str2) {
return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
}
/**
* <p>
* Check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.startsWith(null, null) = true
* StringUtils.startsWith(null, "abc") = false
* StringUtils.startsWith("abcdef", null) = false
* StringUtils.startsWith("abcdef", "abc") = true
* StringUtils.startsWith("ABCDEF", "abc") = false
* </pre>
*
* @see java.lang.String#startsWith(String)
* @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 sensitive, or both <code>null</code>
* @since 2.4
*/
public static boolean startsWith(String str, String prefix) {
return startsWith(str, prefix, false);
}
/**
* <p>
* Check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.startsWith(null, null) = true
* StringUtils.startsWith(null, "abc") = false
* StringUtils.startsWith("abcdef", null) = false
* StringUtils.startsWith("abcdef", "abc") = true
* StringUtils.startsWith("ABCDEF", "abc") = false
* </pre>
*
* @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 sensitive, or both <code>null</code>
* @see java.lang.String#startsWith(String)
* @since 2.4
*/
public static boolean startsWith(String str, String prefix) {
return startsWith(str, prefix, false);
}
/**
* <p>
* Case insensitive check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case insensitive.
* </p>
*
* <pre>
* StringUtils.startsWithIgnoreCase(null, null) = true
* StringUtils.startsWithIgnoreCase(null, "abc") = false
* StringUtils.startsWithIgnoreCase("abcdef", null) = false
* StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
* StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
* </pre>
*
* @see java.lang.String#startsWith(String)
* @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>
* Check if a String starts with a specified prefix (optionally case insensitive).
* </p>
*
* @param str the String to check, may be null
* @param prefix the prefix to find, may be null
* @param ignoreCase inidicates whether the compare should ignore case (case insensitive) or not.
* @return <code>true</code> if the String starts with the prefix or both <code>null</code>
* @see java.lang.String#startsWith(String)
*/
private static boolean startsWith(String str, String prefix, boolean ignoreCase) {
if (str == null || prefix == null) {
return (str == null && prefix == null);
}
if (prefix.length() > str.length()) {
return false;
}
return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
}
/**
* <p>
* Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false.
* </p>
*
* <p>
* <code>null</code> will return <code>false</code>. An empty String (length()=0) will return <code>true</code>.
* </p>
*
* <pre>
* StringUtils.isNumeric(null) = false
* StringUtils.isNumeric("") = true
* StringUtils.isNumeric(" ") = false
* StringUtils.isNumeric("123") = true
* StringUtils.isNumeric("12 3") = false
* StringUtils.isNumeric("ab2c") = false
* StringUtils.isNumeric("12-3") = false
* StringUtils.isNumeric("12.3") = false
* </pre>
*
* @param str
* the String to check, may be null
* @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;
}
/**
* <p>
* Case insensitive check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case insensitive.
* </p>
*
* <pre>
* StringUtils.startsWithIgnoreCase(null, null) = true
* StringUtils.startsWithIgnoreCase(null, "abc") = false
* StringUtils.startsWithIgnoreCase("abcdef", null) = false
* StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
* StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
* </pre>
*
* @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>
* @see java.lang.String#startsWith(String)
* @since 2.4
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
return startsWith(str, prefix, true);
}
/**
* <p>
* Check if a String starts with a specified prefix (optionally case insensitive).
* </p>
*
* @see java.lang.String#startsWith(String)
* @param str
* the String to check, may be null
* @param prefix
* the prefix to find, may be null
* @param ignoreCase
* inidicates whether the compare should ignore case (case insensitive) or not.
* @return <code>true</code> if the String starts with the prefix or both <code>null</code>
*/
private static boolean startsWith(String str, String prefix, boolean ignoreCase) {
if (str == null || prefix == null) {
return (str == null && prefix == null);
}
if (prefix.length() > str.length()) {
return false;
}
return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
}
/**
* <p>
* Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false.
* </p>
*
* <p>
* <code>null</code> will return <code>false</code>. An empty String (length()=0) will return <code>true</code>.
* </p>
*
* <pre>
* StringUtils.isNumeric(null) = false
* StringUtils.isNumeric("") = true
* StringUtils.isNumeric(" ") = false
* StringUtils.isNumeric("123") = true
* StringUtils.isNumeric("12 3") = false
* StringUtils.isNumeric("ab2c") = false
* StringUtils.isNumeric("12-3") = false
* StringUtils.isNumeric("12.3") = false
* </pre>
*
* @param str the String to check, may be null
* @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 String format(T obj);
}
public static interface StringFormatter<T> {
public String format(T obj);
}
public static <T> String join(Collection<T> collection, String separator) {
return join(collection, separator, new StringFormatter<T>() {
@Override
public String format(T obj) {
return obj.toString();
}
});
}
public static <T> String join(Collection<T> collection, String separator) {
return join(collection, separator, new StringFormatter<T>() {
@Override
public String format(T obj) {
return obj.toString();
}
});
}
public static <T> String join(Collection<T> collection, String separator, StringFormatter<T> formatter) {
Iterator<T> iterator = collection.iterator();
// handle null, zero and one elements before building a buffer
if (iterator == null) {
return null;
}
if (!iterator.hasNext()) {
return EMPTY;
}
T first = iterator.next();
if (!iterator.hasNext()) {
return first == null ? "" : formatter.format(first);
}
public static <T> String join(Collection<T> collection, String separator,
StringFormatter<T> formatter) {
Iterator<T> iterator = collection.iterator();
// handle null, zero and one elements before building a buffer
if (iterator == null) {
return null;
}
if (!iterator.hasNext()) {
return EMPTY;
}
T first = iterator.next();
if (!iterator.hasNext()) {
return first == null ? "" : formatter.format(first);
}
// two or more elements
StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
if (first != null) {
buf.append(formatter.format(first));
}
// two or more elements
StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
if (first != null) {
buf.append(formatter.format(first));
}
while (iterator.hasNext()) {
buf.append(separator);
T obj = iterator.next();
if (obj != null) {
buf.append(formatter.format(obj));
}
}
while (iterator.hasNext()) {
buf.append(separator);
T obj = iterator.next();
if (obj != null) {
buf.append(formatter.format(obj));
}
}
return buf.toString();
}
return buf.toString();
}
}
package com.ctrip.apollo.demo;
import com.ctrip.apollo.client.ApolloConfigManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
......@@ -11,10 +12,10 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.ctrip.apollo.demo")
public class AppConfig {
@Bean
public ApolloConfigManager apolloConfigManager() {
return new ApolloConfigManager();
}
@Bean
public ApolloConfigManager apolloConfigManager() {
return new ApolloConfigManager();
}
// @Bean
// public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
......
......@@ -15,24 +15,24 @@ import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome");
registry.addViewController("/index").setViewName("welcome");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome");
registry.addViewController("/index").setViewName("welcome");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/");
}
}
......@@ -6,8 +6,8 @@ import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.demo.model.Config;
import com.ctrip.apollo.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -25,35 +25,35 @@ import java.util.List;
@RequestMapping("/demo")
@PropertySource("classpath:application.properties")
public class DemoController {
@Autowired
private Environment env;
@Autowired
private DemoService demoService;
//Apollo config client internal impl, not intended to be used by application, only for this test page!
private ConfigUtil configUtil = ConfigUtil.getInstance();
//ApolloConfigManager, not intended to be used by application, only for this test page!
@Autowired
private ApolloConfigManager apolloConfigManager;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public Config queryConfig(@PathVariable String configName) {
return new Config(configName, env.getProperty(configName, "undefined"));
}
@RequestMapping(value = "/injected/config", method = RequestMethod.GET)
public Config queryInjectedConfig() {
return new Config("apollo.foo", demoService.getFoo());
}
@RequestMapping(value = "/client/registries", method = RequestMethod.GET)
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
return configUtil.loadApolloRegistries();
}
@RequestMapping(value = "/refresh", method = RequestMethod.POST)
public List<PropertyChange> refreshBeans() {
List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource();
return changes;
}
@Autowired
private Environment env;
@Autowired
private DemoService demoService;
//Apollo config client internal impl, not intended to be used by application, only for this test page!
private ConfigUtil configUtil = ConfigUtil.getInstance();
//ApolloConfigManager, not intended to be used by application, only for this test page!
@Autowired
private ApolloConfigManager apolloConfigManager;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public Config queryConfig(@PathVariable String configName) {
return new Config(configName, env.getProperty(configName, "undefined"));
}
@RequestMapping(value = "/injected/config", method = RequestMethod.GET)
public Config queryInjectedConfig() {
return new Config("apollo.foo", demoService.getFoo());
}
@RequestMapping(value = "/client/registries", method = RequestMethod.GET)
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
return configUtil.loadApolloRegistries();
}
@RequestMapping(value = "/refresh", method = RequestMethod.POST)
public List<PropertyChange> refreshBeans() {
List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource();
return changes;
}
}
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.demo.exception;
import com.ctrip.apollo.demo.model.ErrorResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
......@@ -15,11 +16,12 @@ import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(Exception.class)
ResponseEntity<ErrorResult> handleWebExceptions(Exception ex,
WebRequest request) throws JsonProcessingException {
ErrorResult error = new ErrorResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(error);
@ExceptionHandler(Exception.class)
ResponseEntity<ErrorResult> handleWebExceptions(Exception ex,
WebRequest request)
throws JsonProcessingException {
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;
* Created by Jason on 2/25/16.
*/
public class Config {
private final String name;
private final String value;
private final String name;
private final String value;
public Config(String name, String value) {
this.name = name;
this.value = value;
}
public Config(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public String getValue() {
return value;
}
}
......@@ -4,19 +4,19 @@ package com.ctrip.apollo.demo.model;
* Created by Jason on 7/6/15.
*/
public class ErrorResult {
private final int code;
private final String msg;
private final int code;
private final String msg;
public ErrorResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public ErrorResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
public String getMsg() {
return msg;
}
}
......@@ -10,14 +10,14 @@ import org.springframework.stereotype.Service;
@Service
@RefreshScope
public class DemoService {
private String foo;
private String foo;
@Value("${apollo.foo}")
private void setFoo(String foo) {
this.foo = foo;
}
@Value("${apollo.foo}")
private void setFoo(String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
public String getFoo() {
return foo;
}
}
......@@ -5,7 +5,7 @@
<PatternLayout pattern="[apollo-demo][%t]%d %-5p [%c] %m%n"/>
</Console>
<Async name="Async" includeLocation="true">
<AppenderRef ref="Console" />
<AppenderRef ref="Console"/>
</Async>
</appenders>
<loggers>
......
......@@ -6,7 +6,7 @@
<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="/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"/>
<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>
......
!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')}]);
\ No newline at end of file
!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')
}]);
......@@ -16,7 +16,7 @@
var self = this;
this.loadRegistries = function() {
this.loadRegistries = function () {
$http.get("demo/client/registries")
.success(function (data) {
self.registries = data;
......@@ -26,43 +26,43 @@
});
};
this.queryConfig = function() {
this.queryConfig = function () {
$http.get("demo/config/" + encodeURIComponent(this.configQuery.configName))
.success(function(data) {
.success(function (data) {
self.configQuery.configValue = data.value;
})
.error(function(data, status) {
.error(function (data, status) {
toastr.error((data && data.msg) || 'Load config failed');
});
};
this.queryInjectedConfig = function () {
$http.get("demo/injected/config")
.success(function(data) {
.success(function (data) {
self.injectedConfigValue = data.value;
})
.error(function(data, status) {
.error(function (data, status) {
toastr.error((data && data.msg) || 'Load injected config failed');
});
};
this.refreshConfig = function() {
this.refreshConfig = function () {
$http.post("demo/refresh")
.success(function(data) {
.success(function (data) {
self.assembleRefreshResult(data);
})
.error(function(data, status) {
.error(function (data, status) {
toastr.error((data && data.msg) || 'Refresh config failed');
});
};
this.assembleRefreshResult = function(changedPropertyArray) {
if(!changedPropertyArray || !changedPropertyArray.length) {
this.assembleRefreshResult = function (changedPropertyArray) {
if (!changedPropertyArray || !changedPropertyArray.length) {
this.refreshResult = NONE;
return;
}
this.refreshResult = _.map(changedPropertyArray, function(propertyChange) {
this.refreshResult = _.map(changedPropertyArray, function (propertyChange) {
return propertyChange.propertyName + '(' + propertyChange.changeType + ')';
});
};
......
......@@ -4,4 +4,95 @@
* Copyright (c) 2015 Wes Cruver
* 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}}]})}();
\ No newline at end of file
!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
}
}]
})
}();
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}}
\ No newline at end of file
.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
}
}
#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)}}
\ No newline at end of file
#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)
}
}
......@@ -44,7 +44,8 @@
<div id="config-value-wrapper" class="clearfix">
<label class="col-sm-2 control-label">Config Value</label>
<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>
......
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-portal</artifactId>
<name>Apollo Portal</name>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-portal</artifactId>
<name>Apollo Portal</name>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......@@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PortalApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(PortalApplication.class, args);
}
......
......@@ -7,11 +7,11 @@ import org.springframework.web.client.RestTemplate;
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) {
ResponseEntity<T> response =
restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<Void>((Void) null), responseType);
return response.getBody();
}
public static <T> T exchangeInGET(String url, Class<T> responseType) {
ResponseEntity<T> response =
restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<Void>((Void) null), responseType);
return response.getBody();
}
}
......@@ -2,5 +2,5 @@ package com.ctrip.apollo.portal.constants;
public interface PortalConstants {
long LASTEST_VERSION_ID = -1;
long LASTEST_VERSION_ID = -1;
}
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.data.domain.Page;
......@@ -11,9 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.service.AppService;
import java.util.List;
@RestController
@RequestMapping("/apps")
......
......@@ -4,6 +4,7 @@ import com.ctrip.apollo.portal.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -13,22 +14,23 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/configs")
public class ConfigController {
@Autowired
private ConfigService configService;
@Autowired
private ConfigService configService;
@RequestMapping("/{appId}/{env}/{versionId}")
public AppConfigVO detail(@PathVariable long appId, @PathVariable String env, @PathVariable long versionId) {
@RequestMapping("/{appId}/{env}/{versionId}")
public AppConfigVO detail(@PathVariable long appId, @PathVariable String env,
@PathVariable long versionId) {
if (appId <= 0) {
throw new NotFoundException();
}
if (appId <= 0) {
throw new NotFoundException();
}
if (versionId == PortalConstants.LASTEST_VERSION_ID) {
return configService.loadLatestConfig(appId);
} else if (versionId > 0) {
return configService.loadReleaseConfig(appId, versionId);
} else {
throw new NotFoundException();
}
if (versionId == PortalConstants.LASTEST_VERSION_ID) {
return configService.loadLatestConfig(appId);
} else if (versionId > 0) {
return configService.loadReleaseConfig(appId, versionId);
} else {
throw new NotFoundException();
}
}
}
package com.ctrip.apollo.portal.controller;
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;
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 com.ctrip.apollo.portal.exception.NotFoundException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -19,7 +11,18 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
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
public class GlobalDefaultExceptionHandler {
......@@ -29,12 +32,13 @@ public class GlobalDefaultExceptionHandler {
}
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex) {
HttpStatus status, Throwable ex) {
return handleError(request, status, ex, ex.getMessage());
}
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex, String message) {
HttpStatus status, Throwable ex,
String message) {
ex = resolveError(ex);
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("status", status.value());
......@@ -49,13 +53,14 @@ public class GlobalDefaultExceptionHandler {
@ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeException.class})
public ResponseEntity<Map<String, Object>> methodNotSupportedException(HttpServletRequest request,
ServletException ex) {
ServletException ex) {
return handleError(request, BAD_REQUEST, ex);
}
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = NOT_FOUND)
public void notFound(HttpServletRequest req, NotFoundException ex) {}
public void notFound(HttpServletRequest req, NotFoundException ex) {
}
private Throwable resolveError(Throwable ex) {
while (ex instanceof ServletException && ex.getCause() != null) {
......
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.service.VersionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -13,11 +14,11 @@ import java.util.List;
@RequestMapping("/version")
public class VersionController {
@Autowired
private VersionService versionService;
@Autowired
private VersionService versionService;
@RequestMapping("/{appId}/{env}")
public List<VersionDTO> versions(@PathVariable long appId, @PathVariable String env){
return versionService.findVersionsByApp(appId, env);
}
@RequestMapping("/{appId}/{env}")
public List<VersionDTO> versions(@PathVariable long appId, @PathVariable String env) {
return versionService.findVersionsByApp(appId, env);
}
}
......@@ -11,7 +11,7 @@ import javax.persistence.Id;
public class App implements Serializable {
/**
*
*
*/
private static final long serialVersionUID = 7348554309210401557L;
......
......@@ -9,177 +9,174 @@ import java.util.List;
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 List<ConfigItemDTO> configs;
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 OverrideAppConfig() {
}
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 long getAppId() {
return appId;
}
public boolean isLatestVersion() {
return versionId == 0;
public void setAppId(long appId) {
this.appId = appId;
}
public static class OverrideAppConfig {
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 List<ConfigItemDTO> getConfigs() {
return configs;
}
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() {
return clusterName;
}
private String clusterName;
private List<ConfigItemDTO> configs;
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public OverrideClusterConfig() {
}
public List<ConfigItemDTO> getConfigs() {
return configs;
}
public String getClusterName() {
return clusterName;
}
public void setConfigs(List<ConfigItemDTO> configs) {
this.configs = configs;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public List<ConfigItemDTO> getConfigs() {
return configs;
}
public void setConfigs(List<ConfigItemDTO> configs) {
this.configs = configs;
}
}
public long getAppId() {
return appId;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public Env getEnv() {
return env;
}
public Env getEnv() {
return env;
}
public void setEnv(Env env) {
this.env = env;
}
public void setEnv(Env env) {
this.env = env;
}
public long getVersionId() {
return versionId;
}
public long getVersionId() {
return versionId;
}
public void setVersionId(long versionId) {
this.versionId = versionId;
}
public void setVersionId(long versionId) {
this.versionId = versionId;
}
public List<ConfigItemDTO> getDefaultClusterConfigs() {
return defaultClusterConfigs;
}
public List<ConfigItemDTO> getDefaultClusterConfigs() {
return defaultClusterConfigs;
}
public void setDefaultClusterConfigs(List<ConfigItemDTO> defaultClusterConfigs) {
this.defaultClusterConfigs = defaultClusterConfigs;
}
public void setDefaultClusterConfigs(List<ConfigItemDTO> defaultClusterConfigs) {
this.defaultClusterConfigs = defaultClusterConfigs;
}
public List<OverrideAppConfig> getOverrideAppConfigs() {
return overrideAppConfigs;
}
public List<OverrideAppConfig> getOverrideAppConfigs() {
return overrideAppConfigs;
}
public void setOverrideAppConfigs(List<OverrideAppConfig> overrideAppConfigs) {
this.overrideAppConfigs = overrideAppConfigs;
}
public void setOverrideAppConfigs(List<OverrideAppConfig> overrideAppConfigs) {
this.overrideAppConfigs = overrideAppConfigs;
}
public List<OverrideClusterConfig> getOverrideClusterConfigs() {
return overrideClusterConfigs;
}
public List<OverrideClusterConfig> getOverrideClusterConfigs() {
return overrideClusterConfigs;
}
public void setOverrideClusterConfigs(List<OverrideClusterConfig> overrideClusterConfigs) {
this.overrideClusterConfigs = overrideClusterConfigs;
}
public void setOverrideClusterConfigs(List<OverrideClusterConfig> overrideClusterConfigs) {
this.overrideClusterConfigs = overrideClusterConfigs;
}
@Override
public String toString() {
return "Config4PortalDTO{" +
"appId=" + appId +
", env=" + env +
", versionId=" + versionId +
", defaultClusterConfigs=" + defaultClusterConfigs +
", overrideAppConfigs=" + overrideAppConfigs +
", overrideClusterConfigs=" + overrideClusterConfigs +
'}';
}
@Override
public String toString() {
return "Config4PortalDTO{" +
"appId=" + appId +
", env=" + env +
", versionId=" + versionId +
", defaultClusterConfigs=" + defaultClusterConfigs +
", overrideAppConfigs=" + overrideAppConfigs +
", overrideClusterConfigs=" + overrideClusterConfigs +
'}';
}
}
......@@ -11,7 +11,7 @@ import javax.persistence.Id;
public class Privilege implements Serializable {
/**
*
*
*/
private static final long serialVersionUID = -430087307622435118L;
......
......@@ -2,31 +2,32 @@ package com.ctrip.apollo.portal.enums;
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) {
this.value = value;
}
Env(String value) {
this.value = value;
}
public static Env valueFrom(String env) {
if (env == null || "".equals(env)) {
return UN_KNOW;
} else if ("dev".equals(env)) {
return DEV;
} else if ("fws".equals(env)) {
return FWS;
} else if ("fat".equals(env)) {
return FAT;
} else if ("uat".equals(env)) {
return UAT;
} else if ("prod".equals(env)) {
return PROD;
} else if ("tools".equals(env)) {
return TOOLS;
} else{
return UN_KNOW;
}
public static Env valueFrom(String env) {
if (env == null || "".equals(env)) {
return UN_KNOW;
} else if ("dev".equals(env)) {
return DEV;
} else if ("fws".equals(env)) {
return FWS;
} else if ("fat".equals(env)) {
return FAT;
} else if ("uat".equals(env)) {
return UAT;
} else if ("prod".equals(env)) {
return PROD;
} else if ("tools".equals(env)) {
return TOOLS;
} else {
return UN_KNOW;
}
}
}
......@@ -2,7 +2,7 @@ package com.ctrip.apollo.portal.exception;
public class NotFoundException extends RuntimeException {
/**
*
*
*/
private static final long serialVersionUID = 7611357629749481796L;
}
package com.ctrip.apollo.portal.repository;
import com.ctrip.apollo.portal.entity.App;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.ctrip.apollo.portal.entity.App;
public interface AppRepository extends PagingAndSortingRepository<App, String> {
Page<App> findAll(Pageable pageable);
......
package com.ctrip.apollo.portal.repository;
import java.util.List;
import com.ctrip.apollo.portal.entity.Privilege;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.ctrip.apollo.portal.entity.Privilege;
import java.util.List;
public interface PrivilegeRepository extends PagingAndSortingRepository<Privilege, Long> {
......
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.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.repository.AppRepository;
import java.util.Date;
@Service
public class AppService {
......
......@@ -4,20 +4,17 @@ import com.ctrip.apollo.portal.entity.AppConfigVO;
public interface ConfigService {
/**
* load config info by appId and versionId
* @param appId
* @param versionId
* @return
*/
AppConfigVO loadReleaseConfig(long appId, long versionId);
/**
* load config info by appId and versionId
*/
AppConfigVO loadReleaseConfig(long appId, long versionId);
/**
*
* @param appId
* @return
*/
AppConfigVO loadLatestConfig(long appId);
/**
*
* @param appId
* @return
*/
AppConfigVO loadLatestConfig(long appId);
}
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.stereotype.Service;
import com.ctrip.apollo.portal.entity.Privilege;
import com.ctrip.apollo.portal.exception.NotFoundException;
import com.ctrip.apollo.portal.repository.PrivilegeRepository;
import java.util.List;
@Service
public class PrivilegeService {
......
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.portal.service;
import com.ctrip.apollo.core.dto.VersionDTO;
import com.ctrip.apollo.portal.RestUtils;
import org.springframework.stereotype.Service;
import java.util.List;
......@@ -9,7 +10,7 @@ import java.util.List;
@Service
public class VersionService {
public List<VersionDTO> findVersionsByApp(long appId, String env){
return RestUtils.exchangeInGET("http://localhost:8090/version/app/" + appId, List.class);
}
public List<VersionDTO> findVersionsByApp(long appId, String env) {
return RestUtils.exchangeInGET("http://localhost:8090/version/app/" + appId, List.class);
}
}
package com.ctrip.apollo.portal.service.impl;
import com.google.common.collect.Maps;
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.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.service.ConfigService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.stereotype.Service;
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
public class ConfigServiceImpl implements ConfigService {
public static final String ADMIN_SERVICE_HOST = "http://localhost:8090";
private ObjectMapper objectMapper = new ObjectMapper();
public static final String ADMIN_SERVICE_HOST = "http://localhost:8090";
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public AppConfigVO loadReleaseConfig(long appId, long versionId) {
@Override
public AppConfigVO loadReleaseConfig(long appId, long versionId) {
if (appId <= 0 || versionId <= 0) {
return null;
}
if (appId <= 0 || versionId <= 0) {
return null;
}
long releaseId = getReleaseIdFromVersionId(versionId);
long releaseId = getReleaseIdFromVersionId(versionId);
ReleaseSnapshotDTO[] releaseSnapShots = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/configs/release/" + releaseId, ReleaseSnapshotDTO[].class);
if (releaseSnapShots == null || releaseSnapShots.length == 0) {
return null;
}
ReleaseSnapshotDTO[] releaseSnapShots = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/configs/release/" + releaseId, ReleaseSnapshotDTO[].class);
if (releaseSnapShots == null || releaseSnapShots.length == 0) {
return null;
}
AppConfigVO appConfigVO = AppConfigVO.newInstance(appId, versionId);
AppConfigVO appConfigVO = AppConfigVO.newInstance(appId, versionId);
for (ReleaseSnapshotDTO snapShot : releaseSnapShots) {
//default cluster
if (Constants.DEFAULT_CLUSTER_NAME.equals(snapShot.getClusterName())) {
for (ReleaseSnapshotDTO snapShot : releaseSnapShots) {
//default cluster
if (Constants.DEFAULT_CLUSTER_NAME.equals(snapShot.getClusterName())) {
collectDefaultClusterConfigs(appId, snapShot, appConfigVO);
collectDefaultClusterConfigs(appId, snapShot, appConfigVO);
} else {//cluster special configs
collectSpecialClusterConfigs(appId, snapShot, appConfigVO);
}
}
return appConfigVO;
} else {//cluster special configs
collectSpecialClusterConfigs(appId, snapShot, appConfigVO);
}
}
private long getReleaseIdFromVersionId(long versionId) {
VersionDTO version =
RestUtils.exchangeInGET(ADMIN_SERVICE_HOST + "/version/" + versionId, VersionDTO.class);
if (version == null) {
return -1;
}
return version.getReleaseId();
return appConfigVO;
}
private long getReleaseIdFromVersionId(long versionId) {
VersionDTO version =
RestUtils.exchangeInGET(ADMIN_SERVICE_HOST + "/version/" + versionId, VersionDTO.class);
if (version == null) {
return -1;
}
return version.getReleaseId();
}
private void collectDefaultClusterConfigs(long appId, ReleaseSnapshotDTO snapShot, AppConfigVO appConfigVO) {
Map<Long, List<ConfigItemDTO>> groupedConfigs =
groupConfigsByApp(snapShot.getConfigurations());
private void collectDefaultClusterConfigs(long appId, ReleaseSnapshotDTO snapShot,
AppConfigVO appConfigVO) {
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs =
appConfigVO.getOverrideAppConfigs();
Map<Long, List<ConfigItemDTO>> groupedConfigs =
groupConfigsByApp(snapShot.getConfigurations());
for (Map.Entry<Long, List<ConfigItemDTO>> entry : groupedConfigs.entrySet()) {
long configAppId = entry.getKey();
List<ConfigItemDTO> kvs = entry.getValue();
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs =
appConfigVO.getOverrideAppConfigs();
if (configAppId == appId) {
appConfigVO.setDefaultClusterConfigs(kvs);
} else {
for (Map.Entry<Long, List<ConfigItemDTO>> entry : groupedConfigs.entrySet()) {
long configAppId = entry.getKey();
List<ConfigItemDTO> kvs = entry.getValue();
AppConfigVO.OverrideAppConfig overrideAppConfig =
new AppConfigVO.OverrideAppConfig();
overrideAppConfig.setAppId(configAppId);
overrideAppConfig.setConfigs(kvs);
overrideAppConfigs.add(overrideAppConfig);
}
}
if (configAppId == appId) {
appConfigVO.setDefaultClusterConfigs(kvs);
} else {
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;
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()) {
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()));
}
/**
* appId -> List<KV>
*/
private Map<Long, List<ConfigItemDTO>> groupConfigsByApp(String configJson) {
if (configJson == null || "".equals(configJson)) {
return Maps.newHashMap();
}
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
}
private Long getAppIdFromKey(String key) {
return Long.valueOf(key.substring(0, key.indexOf(".")));
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()));
}
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);
return appIdMapKVs;
}
private Long getAppIdFromKey(String key) {
return Long.valueOf(key.substring(0, key.indexOf(".")));
}
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
public AppConfigVO loadLatestConfig(long appId) {
if (appId <= 0) {
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));
ClusterDTO[] clusters = RestUtils.exchangeInGET(
ADMIN_SERVICE_HOST + "/cluster/app/" + appId, ClusterDTO[].class);
if (clusters == null || clusters.length == 0) {
return null;
}
private AppConfigVO buildAPPConfigVO(long appId, List<ConfigItemDTO> configItems) {
if (configItems == null || configItems.size() == 0) {
return null;
}
Map<String, List<ConfigItemDTO>> groupedClusterConfigs = groupConfigByCluster(configItems);
AppConfigVO appConfigVO =
AppConfigVO.newInstance(appId, PortalConstants.LASTEST_VERSION_ID);
StringBuilder sb = new StringBuilder();
for (ClusterDTO cluster : clusters) {
sb.append(cluster.getId()).append(",");
}
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 = 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;
}
Map<String, List<ConfigItemDTO>> groupedClusterConfigs = groupConfigByCluster(configItems);
private void groupConfigByAppAndEnrichDTO(Map<String, List<ConfigItemDTO>> groupedClusterConfigs, AppConfigVO appConfigVO) {
long appId = appConfigVO.getAppId();
AppConfigVO appConfigVO =
AppConfigVO.newInstance(appId, PortalConstants.LASTEST_VERSION_ID);
List<ConfigItemDTO> defaultClusterConfigs = appConfigVO.getDefaultClusterConfigs();
groupConfigByAppAndEnrichDTO(groupedClusterConfigs, appConfigVO);
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs =
appConfigVO.getOverrideAppConfigs();
return appConfigVO;
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();
private Map<String, List<ConfigItemDTO>> groupConfigByCluster(List<ConfigItemDTO> configItems) {
Map<String, List<ConfigItemDTO>> groupedClusterConfigs = new HashMap<>();
if (Constants.DEFAULT_CLUSTER_NAME.equals(clusterName)) {
//default cluster configs
collectDefaultClusterConfigs(appId, clusterConfigs, defaultClusterConfigs, overrideAppConfigs);
} else {
//override cluster configs
collectSpecialClusterConfigs(clusterName, clusterConfigs, overrideClusterConfigs);
}
}
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) {
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) {
Map<Long, AppConfigVO.OverrideAppConfig> appIdMapOverrideAppConfig = null;
private void collectDefaultClusterConfigs(long appId, List<ConfigItemDTO> clusterConfigs,
List<ConfigItemDTO> defaultClusterConfigs,
List<AppConfigVO.OverrideAppConfig> overrideAppConfigs) {
for (ConfigItemDTO config : clusterConfigs) {
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<>();
}
Map<Long, AppConfigVO.OverrideAppConfig> appIdMapOverrideAppConfig = null;
AppConfigVO.OverrideAppConfig overrideAppConfig =
appIdMapOverrideAppConfig.get(targetAppId);
for (ConfigItemDTO config : clusterConfigs) {
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) {
overrideAppConfig = new AppConfigVO.OverrideAppConfig();
appIdMapOverrideAppConfig.put(targetAppId, overrideAppConfig);
overrideAppConfigs.add(overrideAppConfig);
}
AppConfigVO.OverrideAppConfig overrideAppConfig =
appIdMapOverrideAppConfig.get(targetAppId);
overrideAppConfig.setAppId(targetAppId);
overrideAppConfig.addConfig(config);
}
if (overrideAppConfig == null) {
overrideAppConfig = new AppConfigVO.OverrideAppConfig();
appIdMapOverrideAppConfig.put(targetAppId, overrideAppConfig);
overrideAppConfigs.add(overrideAppConfig);
}
}
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);
overrideAppConfig.setAppId(targetAppId);
overrideAppConfig.addConfig(config);
}
}
}
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',
templateUrl: '../../views/app/config.html',
controller: 'AppConfigController'
}).state('info', {
templateUrl: '../../views/app/info.html',
controller: 'AppInfoController'
}).state('setting', {
templateUrl: '../../views/app/setting.html'
});
templateUrl: '../../views/app/info.html',
controller: 'AppInfoController'
}).state('setting', {
templateUrl: '../../views/app/setting.html'
});
}]).run(function ($state) {
$state.go('config');
});
......
create_app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService',
function ($scope, $window, toastr, AppService) {
//todo 便于测试,后续删掉
$scope.app = {
appId: 1001,
name: 'lepdou',
ownerPhone: '1111',
ownerMail: 'qqq@qq.com',
owner: 'le'
};
//todo 便于测试,后续删掉
$scope.app = {
appId: 1001,
name: 'lepdou',
ownerPhone: '1111',
ownerMail: 'qqq@qq.com',
owner: 'le'
};
$scope.save = function(){
AppService.add($scope.app).then(function(result){
toastr.success('添加成功!');
setInterval(function(){
$window.location.href = '/views/app/index.html?#appid=' + result.appId;
},1000);
},function(result){
toastr.error('添加失败!');
});
};
$scope.save = function () {
AppService.add($scope.app).then(function (result) {
toastr.success('添加成功!');
setInterval(function () {
$window.location.href = '/views/app/index.html?#appid=' + result.appId;
}, 1000);
}, function (result) {
toastr.error('添加失败!');
});
};
}]);
}]);
......@@ -4,7 +4,7 @@ application_module.controller("AppPageController", ['$rootScope', '$location',
$rootScope.appId = $location.$$url.split("=")[1];
if(!$rootScope.appId){
if (!$rootScope.appId) {
$rootScope.appId = 6666;
}
......
......@@ -4,7 +4,7 @@ body {
padding: 0px !important;
margin: 0px !important;
font-size: 13px;
padding-bottom:50px;
padding-bottom: 50px;
}
.container {
......
......@@ -2,8 +2,64 @@
AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org
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 ("+
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);
*/
(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 (" +
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
......@@ -2,12 +2,180 @@
AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org
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"}}};
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);
*/
(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"}
}
};
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
......@@ -2,14 +2,180 @@
AngularJS v1.4.3
(c) 2010-2015 Google, Inc. http://angularjs.org
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");
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);
*/
(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");
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
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -5,4 +5,29 @@
* @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
* @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);
\ No newline at end of file
!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);
.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}}
\ No newline at end of file
.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
}
}
!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')}]);
\ No newline at end of file
!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')
}]);
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}]);
\ No newline at end of file
"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
}]);
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -3,4 +3,265 @@
*
* 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});
\ No newline at end of file
!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
});
#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)}}
\ No newline at end of file
#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)
}
}
......@@ -4,4 +4,95 @@
* Copyright (c) 2015 Wes Cruver
* 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}}]})}();
\ No newline at end of file
!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
}
}]
})
}();
/**
* @version 2.0.2
* @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);
\ No newline at end of file
/**
* @version 2.0.2
* @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);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*!
angular-xeditable - 0.1.8
Edit-in-place for angular.js
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});
\ No newline at end of file
angular-xeditable - 0.1.8
Edit-in-place for angular.js
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
});
......@@ -11,4 +11,478 @@
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* 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 @@
* Generated using the Bootstrap Customizer (http://v3.bootcss.com/customize/?id=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]);
}}}},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);
\ No newline at end of file
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]);
}
}
}
}, 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 @@
<div class="container-fluid">
<div class="app" ng-controller="AppPageController">
<ol class="breadcrumb">
<li >{{breadcrumb.project}}</li>
<li>{{breadcrumb.project}}</li>
<li>{{breadcrumb.nav}}</li>
<li ng-show="breadcrumb.env">{{breadcrumb.env}}</li>
<!--<li>1.0</li>-->
......
<div class="footer">
<p class="text-center">
<span class="glyphicon glyphicon-copyright-mark" aria-hidden="true"></span>携程 框架研发部<br>
......
......@@ -29,7 +29,8 @@
<div class="form-group">
<label class="col-sm-2 control-label"><font style="color: red">*</font> 应用ID</label>
<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 class="form-group">
......@@ -38,7 +39,7 @@
<input type="text" class="form-control" name="appName" ng-model="app.name" required>
</div>
</div>
<!--接入sso之后将会去除-->
<!--接入sso之后将会去除-->
<div class="form-group">
<label class="col-sm-2 control-label">Owner</label>
<div class="col-sm-3">
......@@ -48,7 +49,8 @@
<div class="form-group">
<label class="col-sm-2 control-label">手机号</label>
<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 class="form-group">
......
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.PrivilegeServiceTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
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)
@SuiteClasses({AppControllerTest.class, AppRepositoryTest.class, PrivilegeServiceTest.class, ConfigServiceTest.class
@SuiteClasses({AppControllerTest.class, AppRepositoryTest.class, PrivilegeServiceTest.class,
ConfigServiceTest.class
})
public class AllTests {
......
package com.ctrip.apollo.portal.controller;
import java.net.URI;
import java.net.URISyntaxException;
import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.repository.AppRepository;
import org.junit.Assert;
import org.junit.Test;
......@@ -13,9 +14,8 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.repository.AppRepository;
import java.net.URI;
import java.net.URISyntaxException;
@WebIntegrationTest
......
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.Test;
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
AppRepository repository;
......
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.constants.PortalConstants;
import com.ctrip.apollo.portal.entity.AppConfigVO;
import com.ctrip.apollo.portal.service.impl.ConfigServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -18,226 +22,236 @@ import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;
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;
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest {
@Mock
private RestTemplate restTemplate;
@Mock
private ResponseEntity releaseSnapShotResponse;
@Mock
private ResponseEntity versionResponse;
@Mock
private ResponseEntity clusterResponse;
@Mock
private ResponseEntity configItemResponse;
private ConfigServiceImpl configService;
@Before
public void setUp() {
ReflectionTestUtils.setField(RestUtils.class, "restTemplate", restTemplate);
configService = new ConfigServiceImpl();
}
@Test
public void testLoadReleaseConfig() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] someReleaseSnapShots = assembleReleaseSnapShots();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, someReleaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 2);
}
@Test
public void testLoadReleaseConfigOnlyDefaultConfigs() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
releaseSnapShots[0] =
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 + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 0);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
}
@Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideApp() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\", \"5555.bar\":\"demo2\", \"22.bar\":\"demo2\"}");
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(2, appConfigVO.getOverrideAppConfigs().size());
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
}
@Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideCluster() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[2];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
releaseSnapShots[1] =
assembleReleaseSnapShot(11112, "cluster1", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(0, appConfigVO.getOverrideAppConfigs().size());
assertEquals(1, appConfigVO.getOverrideClusterConfigs().size());
}
@Test
public void testLoadLastestConfig() {
long appId = 6666;
ClusterDTO[] someClusters = assembleClusters();
ConfigItemDTO[] someConfigItem = assembleConfigItems();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/cluster/app/"
+ appId, ClusterDTO[].class, someClusters, clusterResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST
+ "/configs/latest?clusterIds=100,101", ConfigItemDTO[].class, someConfigItem, configItemResponse);
AppConfigVO appConfigVO = configService.loadLatestConfig(appId);
assertEquals(appConfigVO.getAppId(), 6666);
assertEquals(appConfigVO.getVersionId(), PortalConstants.LASTEST_VERSION_ID);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 3);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 1);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 1);
}
private <T> void restInvoke(String url, Class<T> responseType, T result, ResponseEntity someResponse) {
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 VersionDTO assembleVersion(long appId, String versionName, long releaseId) {
VersionDTO version = new VersionDTO();
version.setAppId(appId);
version.setName(versionName);
version.setReleaseId(releaseId);
return version;
}
private ReleaseSnapshotDTO[] assembleReleaseSnapShots() {
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[3];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name", "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}");
releaseSnapShots[1] =
assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}");
releaseSnapShots[2] =
assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}");
return releaseSnapShots;
}
private ReleaseSnapshotDTO assembleReleaseSnapShot(long releaseId, String clusterName, String configurations) {
ReleaseSnapshotDTO releaseSnapShot = new ReleaseSnapshotDTO();
releaseSnapShot.setReleaseId(releaseId);
releaseSnapShot.setClusterName(clusterName);
releaseSnapShot.setConfigurations(configurations);
return releaseSnapShot;
}
private ClusterDTO[] assembleClusters() {
ClusterDTO[] clusters = new ClusterDTO[2];
clusters[0] = assembleCluster(100, 6666, "default-cluster-name");
clusters[1] = assembleCluster(101, 6666, "cluster1");
return clusters;
}
private ClusterDTO assembleCluster(long id, long appId, String name) {
ClusterDTO cluster = new ClusterDTO();
cluster.setAppId(appId);
cluster.setId(id);
cluster.setName(name);
return cluster;
}
private ConfigItemDTO[] assembleConfigItems() {
ConfigItemDTO[] configItems = new ConfigItemDTO[5];
configItems[0] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k1", "6666.v1");
configItems[1] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k2", "6666.v2");
configItems[2] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k3", "6666.v3");
configItems[3] =
assembleConfigItem(100, "default-cluster-name", 5555, "5555.k1", "5555.v1");
configItems[4] = assembleConfigItem(101, "cluster1", 6666, "6666.k1", "6666.v1");
return configItems;
}
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;
}
@Mock
private RestTemplate restTemplate;
@Mock
private ResponseEntity releaseSnapShotResponse;
@Mock
private ResponseEntity versionResponse;
@Mock
private ResponseEntity clusterResponse;
@Mock
private ResponseEntity configItemResponse;
private ConfigServiceImpl configService;
@Before
public void setUp() {
ReflectionTestUtils.setField(RestUtils.class, "restTemplate", restTemplate);
configService = new ConfigServiceImpl();
}
@Test
public void testLoadReleaseConfig() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] someReleaseSnapShots = assembleReleaseSnapShots();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, someReleaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 2);
}
@Test
public void testLoadReleaseConfigOnlyDefaultConfigs() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
releaseSnapShots[0] =
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 + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 0);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
}
@Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideApp() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[1];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\", \"5555.bar\":\"demo2\", \"22.bar\":\"demo2\"}");
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(2, appConfigVO.getOverrideAppConfigs().size());
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 0);
}
@Test
public void testLoadReleaseConfigDefaultConfigsAndOverrideCluster() {
long appId = 6666;
long versionId = 100;
long releaseId = 11111;
VersionDTO someVersion = assembleVersion(appId, "1.0", releaseId);
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[2];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
releaseSnapShots[1] =
assembleReleaseSnapShot(11112, "cluster1",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\"}");
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/configs/release/"
+ releaseId, ReleaseSnapshotDTO[].class, releaseSnapShots, releaseSnapShotResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/version/"
+ versionId, VersionDTO.class, someVersion, versionResponse);
AppConfigVO appConfigVO = configService.loadReleaseConfig(appId, versionId);
assertEquals(appConfigVO.getAppId(), appId);
assertEquals(appConfigVO.getVersionId(), versionId);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 2);
assertEquals(0, appConfigVO.getOverrideAppConfigs().size());
assertEquals(1, appConfigVO.getOverrideClusterConfigs().size());
}
@Test
public void testLoadLastestConfig() {
long appId = 6666;
ClusterDTO[] someClusters = assembleClusters();
ConfigItemDTO[] someConfigItem = assembleConfigItems();
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST + "/cluster/app/"
+ appId, ClusterDTO[].class, someClusters, clusterResponse);
restInvoke(ConfigServiceImpl.ADMIN_SERVICE_HOST
+ "/configs/latest?clusterIds=100,101", ConfigItemDTO[].class, someConfigItem,
configItemResponse);
AppConfigVO appConfigVO = configService.loadLatestConfig(appId);
assertEquals(appConfigVO.getAppId(), 6666);
assertEquals(appConfigVO.getVersionId(), PortalConstants.LASTEST_VERSION_ID);
assertEquals(appConfigVO.getDefaultClusterConfigs().size(), 3);
assertEquals(appConfigVO.getOverrideAppConfigs().size(), 1);
assertEquals(appConfigVO.getOverrideClusterConfigs().size(), 1);
}
private <T> void restInvoke(String url, Class<T> responseType, T result,
ResponseEntity someResponse) {
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 VersionDTO assembleVersion(long appId, String versionName, long releaseId) {
VersionDTO version = new VersionDTO();
version.setAppId(appId);
version.setName(versionName);
version.setReleaseId(releaseId);
return version;
}
private ReleaseSnapshotDTO[] assembleReleaseSnapShots() {
ReleaseSnapshotDTO[] releaseSnapShots = new ReleaseSnapshotDTO[3];
releaseSnapShots[0] =
assembleReleaseSnapShot(11111, "default-cluster-name",
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}");
releaseSnapShots[1] =
assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}");
releaseSnapShots[2] =
assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}");
return releaseSnapShots;
}
private ReleaseSnapshotDTO assembleReleaseSnapShot(long releaseId, String clusterName,
String configurations) {
ReleaseSnapshotDTO releaseSnapShot = new ReleaseSnapshotDTO();
releaseSnapShot.setReleaseId(releaseId);
releaseSnapShot.setClusterName(clusterName);
releaseSnapShot.setConfigurations(configurations);
return releaseSnapShot;
}
private ClusterDTO[] assembleClusters() {
ClusterDTO[] clusters = new ClusterDTO[2];
clusters[0] = assembleCluster(100, 6666, "default-cluster-name");
clusters[1] = assembleCluster(101, 6666, "cluster1");
return clusters;
}
private ClusterDTO assembleCluster(long id, long appId, String name) {
ClusterDTO cluster = new ClusterDTO();
cluster.setAppId(appId);
cluster.setId(id);
cluster.setName(name);
return cluster;
}
private ConfigItemDTO[] assembleConfigItems() {
ConfigItemDTO[] configItems = new ConfigItemDTO[5];
configItems[0] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k1", "6666.v1");
configItems[1] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k2", "6666.v2");
configItems[2] =
assembleConfigItem(100, "default-cluster-name", 6666, "6666.k3", "6666.v3");
configItems[3] =
assembleConfigItem(100, "default-cluster-name", 5555, "5555.k1", "5555.v1");
configItems[4] = assembleConfigItem(101, "cluster1", 6666, "6666.k1", "6666.v1");
return configItems;
}
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;
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.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.ctrip.apollo.portal.AbstractPortalTest;
import com.ctrip.apollo.portal.entity.App;
import com.ctrip.apollo.portal.entity.Privilege;
import java.util.List;
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