diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java
new file mode 100644
index 00000000..b1759006
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.dashboard.config;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.springframework.lang.NonNull;
+
+/**
+ * Dashboard config support
+ *
+ * Dashboard supports configuration loading by several ways by order:
+ * 1. System.properties
+ * 2. Env
+ *
+ * @author jason
+ * @since 1.5.0
+ *
+ */
+public class DashboardConfig {
+ /**
+ * hide app in sidebar when it had no healthy machine after specific period in millis
+ */
+ public static final String CONFIG_HIDE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.app.hideAppNoMachineMillis";
+ /**
+ * remove app when it had no healthy machine after specific period in millis
+ */
+ public static final String CONFIG_REMOVE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.removeAppNoMachineMillis";
+ /**
+ * unhealthy millis
+ */
+ public static final String CONFIG_UNHEALTHY_MACHINE_MILLIS = "sentinel.dashboard.unhealthyMachineMillis";
+ /**
+ * auto remove unhealthy machine after specific period in millis
+ */
+ public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = "sentinel.dashboard.autoRemoveMachineMillis";
+ private static final ConcurrentMap cacheMap = new ConcurrentHashMap<>();
+
+ @NonNull
+ private static String getConfig(String name) {
+ // env
+ String val = System.getenv(name);
+ if (StringUtils.isNotEmpty(val)) {
+ return val;
+ }
+ // properties
+ val = System.getProperty(name);
+ if (StringUtils.isNotEmpty(val)) {
+ return val;
+ }
+ return "";
+ }
+
+ protected static int getConfigInt(String name, int defaultVal, int minVal) {
+ if (cacheMap.containsKey(name)) {
+ return (int)cacheMap.get(name);
+ }
+ int val = NumberUtils.toInt(getConfig(name));
+ if (val == 0) {
+ val = defaultVal;
+ } else if (val < minVal) {
+ val = minVal;
+ }
+ cacheMap.put(name, val);
+ return val;
+ }
+
+ public static int getHideAppNoMachineMillis() {
+ return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000);
+ }
+
+ public static int getRemoveAppNoMachineMillis() {
+ return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000);
+ }
+
+ public static int getAutoRemoveMachineMillis() {
+ return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000);
+ }
+
+ public static int getUnhealthyMachineMillis() {
+ return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, 60000, 30000);
+ }
+
+ public static void clearCache() {
+ cacheMap.clear();
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java
index 03f6d5dd..b90db2d5 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java
@@ -32,6 +32,7 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
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.ResponseBody;
/**
@@ -79,4 +80,21 @@ public class AppController {
});
return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list));
}
+
+ @ResponseBody
+ @RequestMapping(value = "/{app}/machine/remove.json")
+ Result removeMachineById(
+ @PathVariable("app") String app,
+ @RequestParam(name = "ip") String ip,
+ @RequestParam(name = "port") int port) {
+ AppInfo appInfo = appManagement.getDetailApp(app);
+ if (appInfo == null) {
+ return Result.ofSuccess(null);
+ }
+ if (appManagement.removeMachine(app, ip, port)) {
+ return Result.ofSuccessMsg("success");
+ } else {
+ return Result.ofFail(1, "remove failed");
+ }
+ }
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java
index fc84e733..8610e40c 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java
@@ -15,8 +15,6 @@
*/
package com.alibaba.csp.sentinel.dashboard.controller;
-import java.util.Date;
-
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.util.StringUtil;
@@ -57,14 +55,15 @@ public class MachineRegistryController {
return Result.ofFail(-1, "your port not set yet");
}
String sentinelVersion = StringUtil.isEmpty(v) ? "unknown" : v;
- long timestamp = version == null ? System.currentTimeMillis() : version;
+ version = version == null ? System.currentTimeMillis() : version;
try {
MachineInfo machineInfo = new MachineInfo();
machineInfo.setApp(app);
machineInfo.setHostname(hostname);
machineInfo.setIp(ip);
machineInfo.setPort(port);
- machineInfo.setTimestamp(new Date(timestamp));
+ machineInfo.setHeartbeatVersion(version);
+ machineInfo.setLastHeatbeat(System.currentTimeMillis());
machineInfo.setVersion(sentinelVersion);
appManagement.addMachine(machineInfo);
return Result.ofSuccessMsg("success");
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java
index 677e549b..c1088dee 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java
@@ -103,7 +103,8 @@ public class MachineEntity {
machineInfo.setHostname(hostname);
machineInfo.setIp(ip);
machineInfo.setPort(port);
- machineInfo.setTimestamp(timestamp);
+ machineInfo.setLastHeatbeat(timestamp.getTime());
+ machineInfo.setHeartbeatVersion(timestamp.getTime());
return machineInfo;
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java
index b5290306..98aeefb3 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java
@@ -15,12 +15,29 @@
*/
package com.alibaba.csp.sentinel.dashboard.discovery;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Optional;
+import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;
+
public class AppInfo {
+ private static final Comparator COMPARATOR_BY_MACHINE_HEARTBEAT_DESC = new Comparator() {
+
+ @Override
+ public int compare(MachineInfo o1, MachineInfo o2) {
+ if (o1.getLastHeatbeat() < o2.getLastHeatbeat()) {
+ return -1;
+ }
+ if (o1.getLastHeatbeat() > o2.getLastHeatbeat()) {
+ return 1;
+ }
+ return 0;
+ }
+ };
private String app = "";
@@ -59,10 +76,58 @@ public class AppInfo {
machines.remove(machineInfo);
return machines.add(machineInfo);
}
-
+
+ public synchronized boolean removeMachine(String ip, int port) {
+ Iterator it = machines.iterator();
+ while (it.hasNext()) {
+ MachineInfo machine = it.next();
+ if (machine.getIp().equals(ip) && machine.getPort() == port) {
+ it.remove();
+ return true;
+ }
+ }
+ return false;
+ }
+
public Optional getMachine(String ip, int port) {
return machines.stream()
.filter(e -> e.getIp().equals(ip) && e.getPort().equals(port))
.findFirst();
}
+
+ private boolean heartbeatJudge(int threshold) {
+ if (machines.size() == 0) {
+ return false;
+ }
+ if (threshold > 0) {
+ long healthyCount = machines.stream()
+ .filter(m -> m.isHealthy())
+ .count();
+ if (healthyCount == 0) {
+ // no machine
+ long recentHeartBeat = machines.stream()
+ .max(COMPARATOR_BY_MACHINE_HEARTBEAT_DESC).get().getLastHeatbeat();
+ return System.currentTimeMillis() - recentHeartBeat < threshold;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * having no healthy machine and should not be displayed
+ *
+ * @return
+ */
+ public boolean isShown() {
+ return heartbeatJudge(DashboardConfig.getHideAppNoMachineMillis());
+ }
+
+ /**
+ * having no healthy machine and should be removed
+ *
+ * @return
+ */
+ public boolean isDead() {
+ return !heartbeatJudge(DashboardConfig.getRemoveAppNoMachineMillis());
+ }
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java
index 89b7481c..c158dcdc 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java
@@ -52,6 +52,11 @@ public class AppManagement implements MachineDiscovery {
public long addMachine(MachineInfo machineInfo) {
return machineDiscovery.addMachine(machineInfo);
}
+
+ @Override
+ public boolean removeMachine(String app, String ip, int port) {
+ return machineDiscovery.removeMachine(app, ip, port);
+ }
@Override
public List getAppNames() {
@@ -62,5 +67,10 @@ public class AppManagement implements MachineDiscovery {
public AppInfo getDetailApp(String app) {
return machineDiscovery.getDetailApp(app);
}
+
+ @Override
+ public void removeApp(String app) {
+ machineDiscovery.removeApp(app);
+ }
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java
index 88cdbd5e..635c7b60 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java
@@ -20,7 +20,6 @@ import java.util.Set;
public interface MachineDiscovery {
- long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED";
List getAppNames();
@@ -28,6 +27,10 @@ public interface MachineDiscovery {
Set getBriefApps();
AppInfo getDetailApp(String app);
+
+ void removeApp(String app);
long addMachine(MachineInfo machineInfo);
+
+ boolean removeMachine(String app, String ip, int port);
}
\ No newline at end of file
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java
index c4ba88c3..10db4377 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java
@@ -15,9 +15,9 @@
*/
package com.alibaba.csp.sentinel.dashboard.discovery;
-import java.util.Date;
import java.util.Objects;
+import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
public class MachineInfo implements Comparable {
@@ -26,7 +26,8 @@ public class MachineInfo implements Comparable {
private String hostname = "";
private String ip = "";
private Integer port = -1;
- private Date timestamp;
+ private long lastHeatbeat;
+ private long heartbeatVersion;
/**
* Indicates the version of Sentinel client (since 0.2.0).
@@ -77,12 +78,12 @@ public class MachineInfo implements Comparable {
this.ip = ip;
}
- public Date getTimestamp() {
- return timestamp;
+ public long getHeartbeatVersion() {
+ return heartbeatVersion;
}
-
- public void setTimestamp(Date timestamp) {
- this.timestamp = timestamp;
+
+ public void setHeartbeatVersion(long heartbeatVersion) {
+ this.heartbeatVersion = heartbeatVersion;
}
public String getVersion() {
@@ -93,6 +94,32 @@ public class MachineInfo implements Comparable {
this.version = version;
return this;
}
+
+ public boolean isHealthy() {
+ long delta = System.currentTimeMillis() - lastHeatbeat;
+ return delta < DashboardConfig.getUnhealthyMachineMillis();
+ }
+
+ /**
+ * whether dead should be removed
+ *
+ * @return
+ */
+ public boolean isDead() {
+ if (DashboardConfig.getAutoRemoveMachineMillis() > 0) {
+ long delta = System.currentTimeMillis() - lastHeatbeat;
+ return delta > DashboardConfig.getAutoRemoveMachineMillis();
+ }
+ return false;
+ }
+
+ public long getLastHeatbeat() {
+ return lastHeatbeat;
+ }
+
+ public void setLastHeatbeat(long lastHeatbeat) {
+ this.lastHeatbeat = lastHeatbeat;
+ }
@Override
public int compareTo(MachineInfo o) {
@@ -110,14 +137,16 @@ public class MachineInfo implements Comparable {
@Override
public String toString() {
- return "MachineInfo{" +
- "app='" + app + '\'' +
- ", hostname='" + hostname + '\'' +
- ", ip='" + ip + '\'' +
- ", port=" + port +
- ", timestamp=" + timestamp +
- ", version='" + version + '\'' +
- '}';
+ return new StringBuilder("MachineInfo {")
+ .append("app='").append(app).append('\'')
+ .append(", hostname='").append(hostname).append('\'')
+ .append(", ip='").append(ip).append('\'')
+ .append(", port=").append(port)
+ .append(", heartbeatVersion=").append(heartbeatVersion)
+ .append(", lastHeartbeat=").append(lastHeatbeat)
+ .append(", version='").append(version).append('\'')
+ .append(", healthy=").append(isHealthy())
+ .append('}').toString();
}
@Override
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java
index b0f1f63b..e6751074 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java
@@ -36,6 +36,15 @@ public class SimpleMachineDiscovery implements MachineDiscovery {
appInfo.addMachine(machineInfo);
return 1;
}
+
+ @Override
+ public boolean removeMachine(String app, String ip, int port) {
+ AppInfo appInfo = apps.get(app);
+ if (appInfo != null) {
+ return appInfo.removeMachine(ip, port);
+ }
+ return false;
+ }
@Override
public List getAppNames() {
@@ -52,4 +61,9 @@ public class SimpleMachineDiscovery implements MachineDiscovery {
return new HashSet<>(apps.values());
}
+ @Override
+ public void removeApp(String app) {
+ apps.remove(app);
+ }
+
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java
index 1c7ef854..41c400fb 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java
@@ -16,10 +16,8 @@
package com.alibaba.csp.sentinel.dashboard.domain.vo;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import com.alibaba.csp.sentinel.dashboard.discovery.MachineDiscovery;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
/**
@@ -30,9 +28,10 @@ public class MachineInfoVo {
private String app;
private String hostname;
private String ip;
- private Integer port;
- private Date timestamp;
- private boolean health;
+ private int port;
+ private long heartbeatVersion;
+ private long lastHeartbeat;
+ private boolean healthy;
private String version;
@@ -50,11 +49,10 @@ public class MachineInfoVo {
vo.setHostname(machine.getHostname());
vo.setIp(machine.getIp());
vo.setPort(machine.getPort());
- vo.setTimestamp(machine.getTimestamp());
+ vo.setLastHeartbeat(machine.getLastHeatbeat());
+ vo.setHeartbeatVersion(machine.getHeartbeatVersion());
vo.setVersion(machine.getVersion());
- if (System.currentTimeMillis() - machine.getTimestamp().getTime() < MachineDiscovery.MAX_CLIENT_LIVE_TIME_MS) {
- vo.setHealth(true);
- }
+ vo.setHealthy(machine.isHealthy());
return vo;
}
@@ -82,20 +80,28 @@ public class MachineInfoVo {
this.ip = ip;
}
- public Integer getPort() {
+ public int getPort() {
return port;
}
- public void setPort(Integer port) {
+ public void setPort(int port) {
this.port = port;
}
- public Date getTimestamp() {
- return timestamp;
+ public long getLastHeartbeat() {
+ return lastHeartbeat;
}
-
- public void setTimestamp(Date timestamp) {
- this.timestamp = timestamp;
+
+ public void setLastHeartbeat(long lastHeartbeat) {
+ this.lastHeartbeat = lastHeartbeat;
+ }
+
+ public void setHeartbeatVersion(long heartbeatVersion) {
+ this.heartbeatVersion = heartbeatVersion;
+ }
+
+ public long getHeartbeatVersion() {
+ return heartbeatVersion;
}
public String getVersion() {
@@ -107,11 +113,11 @@ public class MachineInfoVo {
return this;
}
- public boolean isHealth() {
- return health;
+ public boolean isHealthy() {
+ return healthy;
}
- public void setHealth(boolean health) {
- this.health = health;
+ public void setHealthy(boolean healthy) {
+ this.healthy = healthy;
}
}
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java
index 0941e549..2cce06e2 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java
@@ -37,13 +37,13 @@ import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;
+import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.node.metric.MetricNode;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;
-import com.alibaba.csp.sentinel.dashboard.util.MachineUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
@@ -168,14 +168,21 @@ public class MetricFetcher {
if (maxWaitSeconds <= 0) {
throw new IllegalArgumentException("maxWaitSeconds must > 0, but " + maxWaitSeconds);
}
- Set machines = appManagement.getDetailApp(app).getMachines();
+ AppInfo appInfo = appManagement.getDetailApp(app);
+ // auto remove for app
+ if (appInfo.isDead()) {
+ logger.info("Dead app removed: {}", app);
+ appManagement.removeApp(app);
+ return;
+ }
+ Set machines = appInfo.getMachines();
logger.debug("enter fetchOnce(" + app + "), machines.size()=" + machines.size()
+ ", time intervalMs [" + startTime + ", " + endTime + "]");
if (machines.isEmpty()) {
return;
}
final String msg = "fetch";
- AtomicLong dead = new AtomicLong();
+ AtomicLong unhealthy = new AtomicLong();
final AtomicLong success = new AtomicLong();
final AtomicLong fail = new AtomicLong();
@@ -184,10 +191,15 @@ public class MetricFetcher {
final Map metricMap = new ConcurrentHashMap<>(16);
final CountDownLatch latch = new CountDownLatch(machines.size());
for (final MachineInfo machine : machines) {
- // dead
- if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MachineUtils.getMaxClientTimeout()) {
+ // auto remove
+ if (machine.isDead()) {
+ appManagement.getDetailApp(app).removeMachine(machine.getIp(), machine.getPort());
+ logger.info("Dead machine removed: {}:{} of {}", machine.getIp(), machine.getPort(), app);
+ continue;
+ }
+ if (!machine.isHealthy()) {
latch.countDown();
- dead.incrementAndGet();
+ unhealthy.incrementAndGet();
continue;
}
final String url = "http://" + machine.getIp() + ":" + machine.getPort() + "/" + METRIC_URL_PATH
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java
index cf5d9b2a..632bc8a8 100644
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java
@@ -22,7 +22,6 @@ import java.util.stream.Collectors;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
-import com.alibaba.csp.sentinel.dashboard.util.MachineUtils;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
@@ -48,11 +47,11 @@ public class FlowRuleApiProvider implements DynamicRuleProvider list = appManagement.getDetailApp(appName).getMachines()
.stream()
- .filter(MachineUtils::isMachineHealth)
+ .filter(e -> e.isHealthy())
.sorted((e1, e2) -> {
- if (e1.getTimestamp().before(e2.getTimestamp())) {
+ if (e1.getLastHeatbeat() < e2.getLastHeatbeat()) {
return 1;
- } else if (e1.getTimestamp().after(e2.getTimestamp())) {
+ } else if (e1.getLastHeatbeat() > e2.getLastHeatbeat()) {
return -1;
} else {
return 0;
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java
index 2f653798..9afbcf34 100644
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java
@@ -24,7 +24,6 @@ import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
-import com.alibaba.csp.sentinel.dashboard.util.MachineUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -51,7 +50,7 @@ public class FlowRuleApiPublisher implements DynamicRulePublisher set = appManagement.getDetailApp(app).getMachines();
for (MachineInfo machine : set) {
- if (!MachineUtils.isMachineHealth(machine)) {
+ if (!machine.isHealthy()) {
continue;
}
// TODO: parse the results
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java
index ef8037ab..1691deb0 100644
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java
@@ -38,7 +38,6 @@ import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalS
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
-import com.alibaba.csp.sentinel.dashboard.util.MachineUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -111,7 +110,7 @@ public class ClusterConfigService {
}
List> futures = appInfo.getMachines().stream()
- .filter(MachineUtils::isMachineHealth)
+ .filter(e -> e.isHealthy())
.map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())
.thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))
.collect(Collectors.toList());
@@ -129,7 +128,7 @@ public class ClusterConfigService {
}
boolean machineOk = appInfo.getMachines().stream()
- .filter(MachineUtils::isMachineHealth)
+ .filter(e -> e.isHealthy())
.map(e -> e.getIp() + '@' + e.getPort())
.anyMatch(e -> e.equals(machineId));
if (!machineOk) {
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java
index 1bfbe91d..9020435c 100644
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java
@@ -17,7 +17,6 @@ package com.alibaba.csp.sentinel.dashboard.util;
import java.util.Optional;
-import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.function.Tuple2;
@@ -25,13 +24,6 @@ import com.alibaba.csp.sentinel.util.function.Tuple2;
* @author Eric Zhao
*/
public final class MachineUtils {
-
- public static final long DEFAULT_MAX_CLIENT_PING_TIMEOUT = 60 * 1000;
-
- public static long getMaxClientTimeout() {
- return DEFAULT_MAX_CLIENT_PING_TIMEOUT;
- }
-
public static Optional parseCommandPort(String machineIp) {
try {
if (!machineIp.contains("@")) {
@@ -61,11 +53,4 @@ public final class MachineUtils {
return Optional.empty();
}
}
-
- public static boolean isMachineHealth(MachineInfo machine) {
- if (machine == null) {
- return false;
- }
- return System.currentTimeMillis() - machine.getTimestamp().getTime() < getMaxClientTimeout();
- }
}
diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/machine.js b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/machine.js
index 08c9f71f..16180470 100755
--- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/machine.js
+++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/machine.js
@@ -19,27 +19,47 @@ app.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService',
$scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
$scope.propertyName = propertyName;
};
-
- MachineService.getAppMachines($scope.app).success(
- function (data) {
- // console.log('get machines: ' + data.data[0].hostname)
- if (data.code == 0 && data.data) {
- $scope.machines = data.data;
- var health = 0;
- $scope.machines.forEach(function (item) {
- if (item.health) {
- health++;
- }
- if (!item.hostname) {
- item.hostname = '未知'
- }
- })
- $scope.healthCount = health;
- $scope.machinesPageConfig.totalCount = $scope.machines.length;
- } else {
- $scope.machines = [];
- $scope.healthCount = 0;
+
+ $scope.reloadMachines = function() {
+ MachineService.getAppMachines($scope.app).success(
+ function (data) {
+ // console.log('get machines: ' + data.data[0].hostname)
+ if (data.code == 0 && data.data) {
+ $scope.machines = data.data;
+ var healthy = 0;
+ $scope.machines.forEach(function (item) {
+ if (item.healthy) {
+ healthy++;
+ }
+ if (!item.hostname) {
+ item.hostname = '未知'
+ }
+ })
+ $scope.healthyCount = healthy;
+ $scope.machinesPageConfig.totalCount = $scope.machines.length;
+ } else {
+ $scope.machines = [];
+ $scope.healthyCount = 0;
+ }
}
+ );
+ };
+
+ $scope.removeMachine = function(ip, port) {
+ if (!confirm("confirm to remove machine [" + ip + ":" + port + "]?")) {
+ return;
}
- );
+ MachineService.removeAppMachine($scope.app, ip, port).success(
+ function(data) {
+ if (data.code == 0) {
+ $scope.reloadMachines();
+ } else {
+ alert("remove failed");
+ }
+ }
+ );
+ };
+
+ $scope.reloadMachines();
+
}]);
diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html b/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
index f6889213..0f5b82c6 100755
--- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
+++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
@@ -19,7 +19,7 @@
{{entry.app}}
- ({{entry.heathCount}}/{{entry.machines.length}})
+ ({{entry.heathyCount}}/{{entry.machines.length}})
diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js b/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js
index 55f63548..bfa6dd75 100755
--- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js
+++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js
@@ -21,20 +21,19 @@ angular.module('sentinelDashboardApp')
function (data) {
if (data.code === 0) {
let initHashApp = $location.path().split('/')[3];
- let currTime = moment(new Date()).utc().add(-1000*60*5).format('YYYY-MM-DDTHH:mm:ss')
$scope.apps = data.data;
$scope.apps = $scope.apps.map(function (item) {
if (item.app === initHashApp) {
item.active = true;
}
- var heathCount = 0;
+ var heathyCount = 0;
for (var i in item.machines) {
- if (item.machines[i].timestamp>currTime) {
- heathCount++;
+ if (item.machines[i].healthy) {
+ heathyCount++;
}
}
- item.heathCount = heathCount;
- if (heathCount>0) {
+ item.heathyCount = heathyCount;
+ if (item.shown) {
return item;
}
});
diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/services/machineservice.js b/sentinel-dashboard/src/main/webapp/resources/app/scripts/services/machineservice.js
index 807f06f8..64531d65 100755
--- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/services/machineservice.js
+++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/services/machineservice.js
@@ -1,10 +1,23 @@
var app = angular.module('sentinelDashboardApp');
-app.service('MachineService', ['$http', function ($http) {
+app.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) {
this.getAppMachines = function (app) {
return $http({
url: 'app/' + app + '/machines.json',
method: 'GET'
});
};
+ this.removeAppMachine = function (app, ip, port) {
+ return $http({
+ url: 'app/' + app + '/machine/remove.json',
+ method: 'POST',
+ headers: {
+ "Content-type": 'application/x-www-form-urlencoded; charset=UTF-8'
+ },
+ data: $httpParamSerializerJQLike({
+ ip: ip,
+ port: port
+ })
+ });
+ };
}]);
diff --git a/sentinel-dashboard/src/main/webapp/resources/app/views/machine.html b/sentinel-dashboard/src/main/webapp/resources/app/views/machine.html
index ca6fd791..3cf69e59 100755
--- a/sentinel-dashboard/src/main/webapp/resources/app/views/machine.html
+++ b/sentinel-dashboard/src/main/webapp/resources/app/views/machine.html
@@ -3,8 +3,8 @@
-
-
+
+
@@ -16,7 +16,7 @@
-
+
@@ -26,7 +26,7 @@
',this.loadingBarTemplate='
',this.$get=["$injector","$document","$timeout","$rootScope",function(i,o,a,s){function l(e){if(m){var t=100*e+"%";f.css("width",t),v=e,y&&(a.cancel(c),c=a(function(){n()},250))}}function n(){if(!(1<=r())){var e,t=r();e=0<=t&&t<.25?(3*Math.random()+3)/100:.25<=t&&t<.65?3*Math.random()/100:.65<=t&&t<.9?2*Math.random()/100:.9<=t&&t<.99?.005:0,l(r()+e)}}function r(){return v}function t(){v=0,m=!1}var u,c,d,p=this.parentSelector,h=angular.element(this.loadingBarTemplate),f=h.find("div").eq(0),g=angular.element(this.spinnerTemplate),m=!1,v=0,y=this.autoIncrement,w=this.includeSpinner,b=this.includeBar,$=this.startSize;return{start:function(){if(u||(u=i.get("$animate")),a.cancel(d),!m){var e=o[0],t=e.querySelector?e.querySelector(p):o.find(p)[0];t||(t=e.getElementsByTagName("body")[0]);var n=angular.element(t),r=t.lastChild&&angular.element(t.lastChild);s.$broadcast("cfpLoadingBar:started"),m=!0,b&&u.enter(h,n,r),w&&u.enter(g,n,h),l($)}},set:l,status:r,inc:n,complete:function(){u||(u=i.get("$animate")),s.$broadcast("cfpLoadingBar:completed"),l(1),a.cancel(d),d=a(function(){var e=u.leave(h,t);e&&e.then&&e.then(t),u.leave(g)},500)},autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(),angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,s,l){function e(e){for(var t in e)if(void 0!==n.style[t])return e[t]}var u=function(e,t,n){n=n||{};var r=a.defer(),i=u[n.animation?"animationEndEventName":"transitionEndEventName"],o=function(){l.$apply(function(){e.unbind(i,o),r.resolve(e)})};return i&&e.bind(i,o),s(function(){angular.isString(t)?e.addClass(t):angular.isFunction(t)?t(e):angular.isObject(t)&&e.css(t),i||r.resolve(e)}),r.promise.cancel=function(){i&&e.unbind(i,o),r.reject("Transition cancelled")},r.promise},n=document.createElement("trans");return u.transitionEndEventName=e({WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"}),u.animationEndEventName=e({WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"}),u}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(l){return{link:function(e,r,t){function n(e){function t(){a===n&&(a=void 0)}var n=l(r,e);return a&&a.cancel(),(a=n).then(t,t),n}function i(){r.removeClass("collapsing"),r.addClass("collapse in"),r.css({height:"auto"})}function o(){r.removeClass("collapsing"),r.addClass("collapse")}var a,s=!0;e.$watch(t.collapse,function(e){e?s?(s=!1,o(),r.css({height:0})):(r.css({height:r[0].scrollHeight+"px"}),r[0].offsetWidth,r.removeClass("collapse in").addClass("collapsing"),n({height:0}).then(o)):s?(s=!1,i()):(r.removeClass("collapse").addClass("collapsing"),n({height:r[0].scrollHeight+"px"}).then(i))})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(e,n,r){this.groups=[],this.closeOthers=function(t){(angular.isDefined(n.closeOthers)?e.$eval(n.closeOthers):r.closeOthers)&&angular.forEach(this.groups,function(e){e!==t&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on("$destroy",function(){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);-1!==t&&this.groups.splice(t,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(e){this.heading=e}},link:function(t,e,n,r){r.addGroup(t),t.$watch("isOpen",function(e){e&&r.closeOthers(t)}),t.toggleOpen=function(){t.isDisabled||(t.isOpen=!t.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(e,t,n,r,i){r.setHeading(i(e,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(e,t,n,r){e.$watch(function(){return r[n.accordionTransclude]},function(e){e&&(t.html(""),t.append(e))})}}}),angular.module("ui.bootstrap.alert",[]).controller("AlertController",["$scope","$attrs",function(e,t){e.closeable="close"in t,this.close=e.close}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}).directive("dismissOnTimeout",["$timeout",function(i){return{require:"alert",link:function(e,t,n,r){i(function(){r.close()},parseInt(n.dismissOnTimeout,10))}}}]),angular.module("ui.bootstrap.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(e,t,n){t.addClass("ng-binding").data("$binding",n.bindHtmlUnsafe),e.$watch(n.bindHtmlUnsafe,function(e){t.html(e||"")})}}),angular.module("ui.bootstrap.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(e){this.activeClass=e.activeClass||"active",this.toggleEvent=e.toggleEvent||"click"}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(t,n,r,e){var i=e[0],o=e[1];o.$render=function(){n.toggleClass(i.activeClass,angular.equals(o.$modelValue,t.$eval(r.btnRadio)))},n.bind(i.toggleEvent,function(){var e=n.hasClass(i.activeClass);(!e||angular.isDefined(r.uncheckable))&&t.$apply(function(){o.$setViewValue(e?null:t.$eval(r.btnRadio)),o.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(r,e,t,n){function i(){return o(t.btnCheckboxTrue,!0)}function o(e,t){var n=r.$eval(e);return angular.isDefined(n)?n:t}var a=n[0],s=n[1];s.$render=function(){e.toggleClass(a.activeClass,angular.equals(s.$modelValue,i()))},e.bind(a.toggleEvent,function(){r.$apply(function(){s.$setViewValue(e.hasClass(a.activeClass)?o(t.btnCheckboxFalse,!1):i()),s.$render()})})}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$interval","$transition",function(a,t,n,s){function l(){r();var e=+a.interval;!isNaN(e)&&0
=d.length?d[t-1]:d[t]):t
");e.attr({"ng-model":"date","ng-change":"dateSelection()"});var c=angular.element(e.children()[0]);i.datepickerOptions&&angular.forEach(r.$parent.$eval(i.datepickerOptions),function(e,t){c.attr(o(t),e)}),r.watchData={},angular.forEach(["minDate","maxDate","datepickerMode"],function(t){if(i[t]){var e=g(i[t]);if(r.$parent.$watch(e,function(e){r.watchData[t]=e}),c.attr(o(t),"watchData."+t),"datepickerMode"===t){var n=e.assign;r.$watch("watchData."+t,function(e,t){e!==t&&n(r.$parent,e)})}}}),i.dateDisabled&&c.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),n.$parsers.unshift(a),r.dateSelection=function(e){angular.isDefined(e)&&(r.date=e),n.$setViewValue(r.date),n.$render(),l&&(r.isOpen=!1,t[0].focus())},t.bind("input change keyup",function(){r.$apply(function(){r.date=n.$modelValue})}),n.$render=function(){var e=n.$viewValue?y(n.$viewValue,s):"";t.val(e),r.date=a(n.$modelValue)};var d=function(e){r.isOpen&&e.target!==t[0]&&r.$apply(function(){r.isOpen=!1})},p=function(e){r.keydown(e)};t.bind("keydown",p),r.keydown=function(e){27===e.which?(e.preventDefault(),e.stopPropagation(),r.close()):40!==e.which||r.isOpen||(r.isOpen=!0)},r.$watch("isOpen",function(e){e?(r.$broadcast("datepicker.focus"),r.position=u?v.offset(t):v.position(t),r.position.top=r.position.top+t.prop("offsetHeight"),m.bind("click",d)):m.unbind("click",d)}),r.select=function(e){if("today"===e){var t=new Date;angular.isDate(n.$modelValue)?(e=new Date(n.$modelValue)).setFullYear(t.getFullYear(),t.getMonth(),t.getDate()):e=new Date(t.setHours(0,0,0,0))}r.dateSelection(e)},r.close=function(){r.isOpen=!1,t[0].focus()};var h=f(e)(r);e.remove(),u?m.find("body").append(h):t.after(h),r.$on("$destroy",function(){h.remove(),t.unbind("keydown",p),m.unbind("click",d)})}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/datepicker/popup.html",link:function(e,t){t.bind("click",function(e){e.preventDefault(),e.stopPropagation()})}}}),angular.module("ui.bootstrap.dropdown",[]).constant("dropdownConfig",{openClass:"open"}).service("dropdownService",["$document",function(t){var n=null;this.open=function(e){n||(t.bind("click",r),t.bind("keydown",i)),n&&n!==e&&(n.isOpen=!1),n=e},this.close=function(e){n===e&&(n=null,t.unbind("click",r),t.unbind("keydown",i))};var r=function(e){if(n){var t=n.getToggleElement();e&&t&&t[0].contains(e.target)||n.$apply(function(){n.isOpen=!1})}},i=function(e){27===e.which&&(n.focusToggleElement(),r())}}]).controller("DropdownController",["$scope","$attrs","$parse","dropdownConfig","dropdownService","$animate",function(n,t,r,e,i,o){var a,s=this,l=n.$new(),u=e.openClass,c=angular.noop,d=t.onToggle?r(t.onToggle):angular.noop;this.init=function(e){s.$element=e,t.isOpen&&(a=r(t.isOpen),c=a.assign,n.$watch(a,function(e){l.isOpen=!!e}))},this.toggle=function(e){return l.isOpen=arguments.length?!!e:!l.isOpen},this.isOpen=function(){return l.isOpen},l.getToggleElement=function(){return s.toggleElement},l.focusToggleElement=function(){s.toggleElement&&s.toggleElement[0].focus()},l.$watch("isOpen",function(e,t){o[e?"addClass":"removeClass"](s.$element,u),e?(l.focusToggleElement(),i.open(l)):i.close(l),c(n,e),angular.isDefined(e)&&e!==t&&d(n,{open:!!e})}),n.$on("$locationChangeSuccess",function(){l.isOpen=!1}),n.$on("$destroy",function(){l.$destroy()})}]).directive("dropdown",function(){return{controller:"DropdownController",link:function(e,t,n,r){r.init(t)}}}).directive("dropdownToggle",function(){return{require:"?^dropdown",link:function(t,n,r,i){if(i){i.toggleElement=n;var e=function(e){e.preventDefault(),n.hasClass("disabled")||r.disabled||t.$apply(function(){i.toggle()})};n.bind("click",e),n.attr({"aria-haspopup":!0,"aria-expanded":!1}),t.$watch(i.isOpen,function(e){n.attr("aria-expanded",!!e)}),t.$on("$destroy",function(){n.unbind("click",e)})}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var r=[];return{add:function(e,t){r.push({key:e,value:t})},get:function(e){for(var t=0;t");i.attr("backdrop-class",t.backdropClass),h=c(i)(f),n.append(h)}var o=angular.element("
");o.attr({"template-url":t.windowTemplateUrl,"window-class":t.windowClass,size:t.size,index:m.length()-1,animate:"animate"}).html(t.content);var a=c(o)(t.scope);m.top().value.modalDomEl=a,n.append(a),n.addClass(g)},n.close=function(e,t){var n=m.get(e);n&&(n.value.deferred.resolve(t),r(e))},n.dismiss=function(e,t){var n=m.get(e);n&&(n.value.deferred.reject(t),r(e))},n.dismissAll=function(e){for(var t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},n.getTop=function(){return m.top()},n}]).provider("$modal",function(){var g={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(l,u,c,d,p,h,f){var e={};return e.open=function(o){var a=c.defer(),e=c.defer(),s={result:a.promise,opened:e.promise,close:function(e){f.close(s,e)},dismiss:function(e){f.dismiss(s,e)}};if((o=angular.extend({},g.options,o)).resolve=o.resolve||{},!o.template&&!o.templateUrl)throw new Error("One of template or templateUrl options is required.");var t,n,r,i=c.all([(r=o,r.template?c.when(r.template):d.get(angular.isFunction(r.templateUrl)?r.templateUrl():r.templateUrl,{cache:p}).then(function(e){return e.data}))].concat((t=o.resolve,n=[],angular.forEach(t,function(e){(angular.isFunction(e)||angular.isArray(e))&&n.push(c.when(l.invoke(e)))}),n)));return i.then(function(n){var e=(o.scope||u).$new();e.$close=s.close,e.$dismiss=s.dismiss;var t,r={},i=1;o.controller&&(r.$scope=e,r.$modalInstance=s,angular.forEach(o.resolve,function(e,t){r[t]=n[i++]}),t=h(o.controller,r),o.controllerAs&&(e[o.controllerAs]=t)),f.open(s,{scope:e,deferred:a,content:n[0],backdrop:o.backdrop,keyboard:o.keyboard,backdropClass:o.backdropClass,windowClass:o.windowClass,windowTemplateUrl:o.windowTemplateUrl,size:o.size})},function(e){a.reject(e)}),i.then(function(){e.resolve(!0)},function(){e.reject(!1)}),s},e}]};return g}),angular.module("ui.bootstrap.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse",function(n,r,i){var o=this,a={$setViewValue:angular.noop},t=r.numPages?i(r.numPages).assign:angular.noop;this.init=function(e,t){a=e,this.config=t,a.$render=function(){o.render()},r.itemsPerPage?n.$parent.$watch(i(r.itemsPerPage),function(e){o.itemsPerPage=parseInt(e,10),n.totalPages=o.calculateTotalPages()}):this.itemsPerPage=t.itemsPerPage},this.calculateTotalPages=function(){var e=this.itemsPerPage<1?1:Math.ceil(n.totalItems/this.itemsPerPage);return Math.max(e||0,1)},this.render=function(){n.page=parseInt(a.$viewValue,10)||1},n.selectPage=function(e){n.page!==e&&0e?n.selectPage(e):a.$render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(s,l){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@"},require:["pagination","?ngModel"],controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(e,t,n,r){function c(e,t,n){return{number:e,text:t,active:n}}var i=r[0],o=r[1];if(o){var d=angular.isDefined(n.maxSize)?e.$parent.$eval(n.maxSize):l.maxSize,p=angular.isDefined(n.rotate)?e.$parent.$eval(n.rotate):l.rotate;e.boundaryLinks=angular.isDefined(n.boundaryLinks)?e.$parent.$eval(n.boundaryLinks):l.boundaryLinks,e.directionLinks=angular.isDefined(n.directionLinks)?e.$parent.$eval(n.directionLinks):l.directionLinks,i.init(o,l),n.maxSize&&e.$parent.$watch(s(n.maxSize),function(e){d=parseInt(e,10),i.render()});var a=i.render;i.render=function(){a(),0';return{restrict:"EA",compile:function(){var _=o(i);return function(r,t,i){function e(){m.isOpen?o():n()}function n(){var e,t,n;(!g||r.$eval(i[S+"Enable"]))&&(n=i[S+"Placement"],m.placement=angular.isDefined(n)?n:D.placement,e=i[S+"PopupDelay"],t=parseInt(e,10),m.popupDelay=isNaN(t)?D.popupDelay:t,m.popupDelay?p||(p=x(a,m.popupDelay,!1)).then(function(e){e()}):a()())}function o(){r.$apply(function(){s()})}function a(){return p=null,d&&(x.cancel(d),d=null),m.content?(u&&l(),c=m.$new(),(u=_(c,function(e){h?O.find("body").append(e):t.after(e)})).css({top:0,left:0,display:"block"}),m.$digest(),v(),m.isOpen=!0,m.$digest(),v):angular.noop}function s(){m.isOpen=!1,x.cancel(p),p=null,m.animation?d||(d=x(l,500)):l()}function l(){d=null,u&&(u.remove(),u=null),c&&(c.$destroy(),c=null)}var u,c,d,p,h=!!angular.isDefined(D.appendToBody)&&D.appendToBody,f=k(void 0),g=angular.isDefined(i[S+"Enable"]),m=r.$new(!0),v=function(){var e=T.positionElements(t,u,m.placement,h);e.top+="px",e.left+="px",u.css(e)};m.isOpen=!1,i.$observe(C,function(e){!(m.content=e)&&m.isOpen&&s()}),i.$observe(S+"Title",function(e){m.title=e});var y,w=function(){t.unbind(f.show,n),t.unbind(f.hide,o)};y=i[S+"Trigger"],w(),(f=k(y)).show===f.hide?t.bind(f.show,e):(t.bind(f.show,n),t.bind(f.hide,o));var b=r.$eval(i[S+"Animation"]);m.animation=angular.isDefined(b)?!!b:D.animation;var $=r.$eval(i[S+"AppendToBody"]);(h=angular.isDefined($)?$:h)&&r.$on("$locationChangeSuccess",function(){m.isOpen&&s()}),r.$on("$destroy",function(){x.cancel(d),x.cancel(p),w(),l(),m=null})}}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(e){return e("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(e){return e("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$tooltip",function(e){return e("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig",function(n,e,t){var r=this,i=angular.isDefined(e.animate)?n.$parent.$eval(e.animate):t.animate;this.bars=[],n.max=angular.isDefined(e.max)?n.$parent.$eval(e.max):t.max,this.addBar=function(t,e){i||e.css({transition:"none"}),this.bars.push(t),t.$watch("value",function(e){t.percent=+(100*e/n.max).toFixed(2)}),t.$on("$destroy",function(){e=null,r.removeBar(t)})},this.removeBar=function(e){this.bars.splice(this.bars.indexOf(e),1)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},templateUrl:"template/progressbar/progress.html"}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(e,t,n,r){r.addBar(e,t)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(e,t,n,r){r.addBar(e,angular.element(t.children()[0]))}}}),angular.module("ui.bootstrap.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","ratingConfig",function(n,r,i){var o={$setViewValue:angular.noop};this.init=function(e){(o=e).$render=this.render,this.stateOn=angular.isDefined(r.stateOn)?n.$parent.$eval(r.stateOn):i.stateOn,this.stateOff=angular.isDefined(r.stateOff)?n.$parent.$eval(r.stateOff):i.stateOff;var t=angular.isDefined(r.ratingStates)?n.$parent.$eval(r.ratingStates):new Array(angular.isDefined(r.max)?n.$parent.$eval(r.max):i.max);n.range=this.buildTemplateObjects(t)},this.buildTemplateObjects=function(e){for(var t=0,n=e.length;t");v.attr({id:t,matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(e.typeaheadTemplateUrl)&&v.attr("template-url",e.typeaheadTemplateUrl);var y=function(){m.matches=[],m.activeIdx=-1,a.attr("aria-expanded",!1)},w=function(e){return t+"-option-"+e};m.$watch("activeIdx",function(e){e<0?a.removeAttr("aria-activedescendant"):a.attr("aria-activedescendant",w(e))});var b=function(r){var i={$viewValue:r};u(o,!0),x.when(g.source(o,i)).then(function(e){var t=r===s.$viewValue;if(t&&l)if(0=n?0$&"):e}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion-group.html",'\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion.html",'
')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(e){e.put("template/alert/alert.html",'\n')}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(e){e.put("template/carousel/carousel.html",'\n')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(e){e.put("template/carousel/slide.html","
\n")}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/datepicker.html",'\n \n \n \n
')}]),angular.module("template/datepicker/day.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/day.html",'\n \n \n \n {{title}} \n \n \n \n \n {{label.abbr}} \n \n \n \n \n {{ weekNumbers[$index] }} \n \n {{dt.label}} \n \n \n \n
\n')}]),angular.module("template/datepicker/month.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/month.html",'\n \n \n \n {{title}} \n \n \n \n \n \n \n {{dt.label}} \n \n \n \n
\n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/popup.html",'\n')}]),angular.module("template/datepicker/year.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/year.html",'\n \n \n \n {{title}} \n \n \n \n \n \n \n {{dt.label}} \n \n \n \n
\n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(e){e.put("template/modal/backdrop.html",'
\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(e){e.put("template/modal/window.html",'')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pager.html",'')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pagination.html",'')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-html-unsafe-popup.html",'\n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-popup.html",'\n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(e){e.put("template/popover/popover.html",'\n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/bar.html",'
')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progress.html",'
')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progressbar.html",'')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(e){e.put("template/rating/rating.html",'\n \n ({{ $index < value ? \'*\' : \' \' }}) \n \n ')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tab.html",'\n {{heading}} \n \n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tabset.html",'\n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(e){e.put("template/timepicker/timepicker.html",'\n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-match.html",' ')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-popup.html",'\n')}]),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function p(){return e.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function l(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function h(e,t){var n,r=[];for(n=0;n>>0,r=0;rCe(e)?(o=e+1,s-Ce(e)):(o=e,s),{year:o,dayOfYear:a}}function Ne(e,t,n){var r,i,o=He(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+qe(i=e.year()-1,t,n):a>qe(e.year(),t,n)?(r=a-qe(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function qe(e,t,n){var r=He(e,t,n),i=He(e+1,t,n);return(Ce(e)-r+i)/7}N("w",["ww",2],"wo","week"),N("W",["WW",2],"Wo","isoWeek"),P("week","w"),P("isoWeek","W"),V("week",5),V("isoWeek",5),le("w",Q),le("ww",Q,B),le("W",Q),le("WW",Q,B),he(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=S(e)});N("d",0,"do","day"),N("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),N("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),N("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),N("e",0,0,"weekday"),N("E",0,0,"isoWeekday"),P("day","d"),P("weekday","e"),P("isoWeekday","E"),V("day",11),V("weekday",11),V("isoWeekday",11),le("d",Q),le("e",Q),le("E",Q),le("dd",function(e,t){return t.weekdaysMinRegex(e)}),le("ddd",function(e,t){return t.weekdaysShortRegex(e)}),le("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:v(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,r){t[r]=S(e)});var Ue="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var Be="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var We=ae;var Ge=ae;var Ke=ae;function Qe(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=m([2e3,1]).day(t),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),o=this.weekdays(n,""),a.push(r),s.push(i),l.push(o),u.push(r),u.push(i),u.push(o);for(a.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=ce(s[t]),l[t]=ce(l[t]),u[t]=ce(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Ze(){return this.hours()%12||12}function Xe(e,t){N(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Je(e,t){return t._meridiemParse}N("H",["HH",2],0,"hour"),N("h",["hh",2],0,Ze),N("k",["kk",2],0,function(){return this.hours()||24}),N("hmm",0,0,function(){return""+Ze.apply(this)+F(this.minutes(),2)}),N("hmmss",0,0,function(){return""+Ze.apply(this)+F(this.minutes(),2)+F(this.seconds(),2)}),N("Hmm",0,0,function(){return""+this.hours()+F(this.minutes(),2)}),N("Hmmss",0,0,function(){return""+this.hours()+F(this.minutes(),2)+F(this.seconds(),2)}),Xe("a",!0),Xe("A",!1),P("hour","h"),V("hour",13),le("a",Je),le("A",Je),le("H",Q),le("h",Q),le("k",Q),le("HH",Q,B),le("hh",Q,B),le("kk",Q,B),le("hmm",Z),le("hmmss",X),le("Hmm",Z),le("Hmmss",X),pe(["H","HH"],ve),pe(["k","kk"],function(e,t,n){var r=S(e);t[ve]=24===r?0:r}),pe(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe(["h","hh"],function(e,t,n){t[ve]=S(e),v(n).bigHour=!0}),pe("hmm",function(e,t,n){var r=e.length-2;t[ve]=S(e.substr(0,r)),t[ye]=S(e.substr(r)),v(n).bigHour=!0}),pe("hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=S(e.substr(0,r)),t[ye]=S(e.substr(r,2)),t[we]=S(e.substr(i)),v(n).bigHour=!0}),pe("Hmm",function(e,t,n){var r=e.length-2;t[ve]=S(e.substr(0,r)),t[ye]=S(e.substr(r))}),pe("Hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=S(e.substr(0,r)),t[ye]=S(e.substr(r,2)),t[we]=S(e.substr(i))});var et,tt=xe("Hours",!0),nt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Pe,monthsShort:Me,week:{dow:0,doy:6},weekdays:Ue,weekdaysMin:Be,weekdaysShort:ze,meridiemParse:/[ap]\.?m?\.?/i},rt={},it={};function ot(e){return e?e.toLowerCase().replace("_","-"):e}function at(e){var t=null;if(!rt[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=et._abbr,require("./locale/"+e),st(t)}catch(e){}return rt[e]}function st(e,t){var n;return e&&((n=u(t)?ut(e):lt(e,t))?et=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),et._abbr}function lt(e,t){if(null===t)return delete rt[e],null;var n,r=nt;if(t.abbr=e,null!=rt[e])x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=rt[e]._config;else if(null!=t.parentLocale)if(null!=rt[t.parentLocale])r=rt[t.parentLocale]._config;else{if(null==(n=at(t.parentLocale)))return it[t.parentLocale]||(it[t.parentLocale]=[]),it[t.parentLocale].push({name:e,config:t}),null;r=n._config}return rt[e]=new E(T(r,t)),it[e]&&it[e].forEach(function(e){lt(e.name,e.config)}),st(e),rt[e]}function ut(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return et;if(!s(e)){if(t=at(e))return t;e=[e]}return function(e){for(var t,n,r,i,o=0;o=t&&a(i,n,!0)>=t-1)break;t--}o++}return et}(e)}function ct(e){var t,n=e._a;return n&&-2===v(e).overflow&&(t=n[ge]<0||11Ee(n[fe],n[ge])?me:n[ve]<0||24qe(n,o,a)?v(e)._overflowWeeks=!0:null!=l?v(e)._overflowWeekday=!0:(s=Ye(n,r,i,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=dt(e._a[fe],r[fe]),(e._dayOfYear>Ce(o)||0===e._dayOfYear)&&(v(e)._overflowDayOfYear=!0),n=je(o,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ve]&&0===e._a[ye]&&0===e._a[we]&&0===e._a[be]&&(e._nextDay=!0,e._a[ve]=0),e._d=(e._useUTC?je:function(e,t,n,r,i,o,a){var s=new Date(e,t,n,r,i,o,a);return e<100&&0<=e&&isFinite(s.getFullYear())&&s.setFullYear(e),s}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ve]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(v(e).weekdayMismatch=!0)}}var ht=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ft=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,gt=/Z|[+-]\d\d(?::?\d\d)?/,mt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],yt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,r,i,o,a,s=e._i,l=ht.exec(s)||ft.exec(s);if(l){for(v(e).iso=!0,t=0,n=mt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},un.isLocal=function(){return!!this.isValid()&&!this._isUTC},un.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},un.isUtc=Ht,un.isUTC=Ht,un.zoneAbbr=function(){return this._isUTC?"UTC":""},un.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},un.dates=n("dates accessor is deprecated. Use date instead.",nn),un.months=n("months accessor is deprecated. Use month instead",Ie),un.years=n("years accessor is deprecated. Use year instead",De),un.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),un.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e={};if(b(e,this),(e=kt(e))._a){var t=e._isUTC?m(e._a):xt(e._a);this._isDSTShifted=this.isValid()&&0-1/0&&t.selectable&&c[e]){var r=c[e](t.utcDateValue),i=[];if(r.weeks)for(var o=0;on+9,past:u.year()r.indexOf(e.startView))throw new Error("startView must be greater than minView");if(!a.isNumber(e.minuteStep))throw new Error("minuteStep must be numeric");if(e.minuteStep<=0||60<=e.minuteStep)throw new Error("minuteStep must be greater than zero and less than 60");if(null!==e.configureOn&&!a.isString(e.configureOn))throw new Error("configureOn must be a string");if(null!==e.configureOn&&e.configureOn.length<1)throw new Error("configureOn must not be an empty string");if(null!==e.renderOn&&!a.isString(e.renderOn))throw new Error("renderOn must be a string");if(null!==e.renderOn&&e.renderOn.length<1)throw new Error("renderOn must not be an empty string");if(null!==e.modelType&&!a.isString(e.modelType))throw new Error("modelType must be a string");if(null!==e.modelType&&e.modelType.length<1)throw new Error("modelType must not be an empty string");"Date"!==e.modelType&&"moment"!==e.modelType&&"milliseconds"!==e.modelType&&(e.parseFormat=e.modelType);if(null!==e.dropdownSelector&&!a.isString(e.dropdownSelector))throw new Error("dropdownSelector must be a string");null===e.dropdownSelector||"undefined"!=typeof jQuery&&"function"==typeof jQuery().dropdown||(i.error("Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\nThe dropdownSelector configuration option is being removed because it will not function properly."),delete e.dropdownSelector)}}}a.module("ui.bootstrap.datetimepicker",[]).service("dateTimePickerConfig",function(){var e={bg:{previous:"предишна",next:"следваща"},ca:{previous:"anterior",next:"següent"},da:{previous:"forrige",next:"næste"},de:{previous:"vorige",next:"weiter"},"en-au":{previous:"previous",next:"next"},"en-gb":{previous:"previous",next:"next"},en:{previous:"previous",next:"next"},"es-us":{previous:"atrás",next:"siguiente"},es:{previous:"atrás",next:"siguiente"},fi:{previous:"edellinen",next:"seuraava"},fr:{previous:"précédent",next:"suivant"},hu:{previous:"előző",next:"következő"},it:{previous:"precedente",next:"successivo"},ja:{previous:"前へ",next:"次へ"},ml:{previous:"മുൻപുള്ളത്",next:"അടുത്തത്"},nl:{previous:"vorige",next:"volgende"},pl:{previous:"poprzednia",next:"następna"},"pt-br":{previous:"anteriores",next:"próximos"},pt:{previous:"anterior",next:"próximo"},ro:{previous:"anterior",next:"următor"},ru:{previous:"предыдущая",next:"следующая"},sk:{previous:"predošlá",next:"ďalšia"},sv:{previous:"föregående",next:"nästa"},tr:{previous:"önceki",next:"sonraki"},uk:{previous:"назад",next:"далі"},"zh-cn":{previous:"上一页",next:"下一页"},"zh-tw":{previous:"上一頁",next:"下一頁"}}[b.locale().toLowerCase()];return a.extend({},{configureOn:null,dropdownSelector:null,minuteStep:5,minView:"minute",modelType:"Date",parseFormat:"YYYY-MM-DDTHH:mm:ss.SSSZZ",renderOn:null,startView:"day"},{screenReader:e})}).service("dateTimePickerValidator",t).directive("datetimepicker",e),e.$inject=["dateTimePickerConfig","dateTimePickerValidator"],t.$inject=["$log"]}),angular.module("rzTable",[]),angular.module("rzTable").directive("rzTable",["resizeStorage","$injector","$parse",function(g,i,u){function e(e){}function c(n,r,i){return function(e,t){!0!==i.busy&&void 0!==t&&t!==e&&(d(n),p(n,r,i))}}function d(e){k=!0,l.map(function(e){e.remove()}),l=[]}function p(e,t,n){if(!n.busy){b=$(e).find("th"),v=n.mode,y=!angular.isDefined(n.saveTableSizes)||n.saveTableSizes,w=n.profile;var r=function(t,e){try{var n=e.rzMode?t.mode:"BasicResizer",r=i.get(n);return r}catch(e){return console.error("The resizer "+t.mode+" was not found"),null}}(n,t);r&&(S=new r(e,b,h),y&&(D=g.loadTableSizes(e,n.mode,n.profile)),s=S.handles(b),a=S.ctrlColumns,S.setup(),(o=D)&&($(C).width("auto"),a.each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr("id"),i=o[r];$(t).css({width:i})}),S.onTableReady()),s.each(function(e,t){!function(e,t,n){var r=$("",{class:e.options.handleClass||"rz-handle"});$(n).prepend(r),l.push(r);var i=S.handleMiddleware(r,n);p=e,h=r,f=i,$(h).mousedown(function(e){k&&(S.onFirstDrag(f,h),S.onTableReady(),k=!1),p.options.onResizeStarted&&p.options.onResizeStarted(f);var t={};S.intervene&&(((t=S.intervene.selector(f)).column=t).orgWidth=$(t).width()),e.preventDefault(),$(h).addClass(p.options.handleClassActive||"rz-handle-active");var n,r,i,o,a,s,l,u,c=e.clientX,d=$(f).width();o=p,a=f,s=c,l=d,u=t,_=function(e){var t=e.clientX,n=t-s,r=S.calculate(l,n);if(!(r
t)||(this.fixedColumn.width()<=this.getMinWidth(this.fixedColumn)?(this.bound=t,$(this.fixedColumn).width(this.minWidth),!0):void 0)},e.prototype.onEndDrag=function(){this.bound=!1},e.prototype.calculate=function(e,t){return e-t},e}]),angular.module("rzTable").factory("OverflowResizer",["ResizerModel",function(r){function e(e,t,n){r.call(this,e,t,n)}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflow:"auto"})},e.prototype.onTableReady=function(){$(this.table).width(1)},e}]),function(e,t){"function"==typeof define&&define.amd?define(["angular"],t):"object"==typeof module&&module.exports?module.exports=t(require("angular")):e.angularClipboard=t(e.angular)}(this,function(i){return i.module("angular-clipboard",[]).factory("clipboard",["$document","$window",function(s,l){return{copyText:function(e,t){var n,r,i=l.pageXOffset||s[0].documentElement.scrollLeft,o=l.pageYOffset||s[0].documentElement.scrollTop,a=(n=e,(r=s[0].createElement("textarea")).style.position="absolute",r.style.fontSize="12pt",r.style.border="0",r.style.padding="0",r.style.margin="0",r.style.left="-10000px",r.style.top=(l.pageYOffset||s[0].documentElement.scrollTop)+"px",r.textContent=n,r);s[0].body.appendChild(a),function(e){try{s[0].body.style.webkitUserSelect="initial";var t=s[0].getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n),e.select(),e.setSelectionRange(0,999999);try{if(!s[0].execCommand("copy"))throw"failure copy"}finally{t.removeAllRanges()}}finally{s[0].body.style.webkitUserSelect=""}}(a),l.scrollTo(i,o),s[0].body.removeChild(a)},supported:"queryCommandSupported"in s[0]&&s[0].queryCommandSupported("copy")}}]).directive("clipboard",["clipboard",function(r){return{restrict:"A",scope:{onCopied:"&",onError:"&",text:"=",supported:"=?"},link:function(t,n){t.supported=r.supported,n.on("click",function(e){try{r.copyText(t.text,n[0]),i.isFunction(t.onCopied)&&t.$evalAsync(t.onCopied())}catch(e){i.isFunction(t.onError)&&t.$evalAsync(t.onError({err:e}))}})}}}])}),function(e,t){"function"==typeof define&&define.amd?define("sifter",t):"object"==typeof exports?module.exports=t():e.Sifter=t()}(this,function(){var e=function(e,t){this.items=e,this.settings=t||{diacritics:!0}};e.prototype.tokenize=function(e){if(!(e=s(String(e||"").toLowerCase()))||!e.length)return[];var t,n,r,i,o=[],a=e.split(/ +/);for(t=0,n=a.length;t /g,">").replace(/"/g,""")},t={before:function(e,t,n){var r=e[t];e[t]=function(){return n.apply(e,arguments),r.apply(e,arguments)}},after:function(t,e,n){var r=t[e];t[e]=function(){var e=r.apply(t,arguments);return n.apply(t,arguments),e}}},n=function(t,n,e){var r,i=t.trigger,o={};for(r in t.trigger=function(){var e=arguments[0];if(-1===n.indexOf(e))return i.apply(t,arguments);o[e]=arguments},e.apply(t,[]),t.trigger=i,o)o.hasOwnProperty(r)&&i.apply(t,o[r])},f=function(e){var t={};if("selectionStart"in e)t.start=e.selectionStart,t.length=e.selectionEnd-t.start;else if(document.selection){e.focus();var n=document.selection.createRange(),r=document.selection.createRange().text.length;n.moveStart("character",-e.value.length),t.start=n.text.length-r,t.length=r}return t},O=function(p){var h=null,e=function(e,t){var n,r,i,o,a,s,l,u,c,d;(t=t||{},(e=e||window.event||{}).metaKey||e.altKey)||(t.force||!1!==p.data("grow"))&&(n=p.val(),e.type&&"keydown"===e.type.toLowerCase()&&(i=48<=(r=e.keyCode)&&r<=57||65<=r&&r<=90||96<=r&&r<=111||186<=r&&r<=222||32===r,46===r||8===r?(u=f(p[0])).length?n=n.substring(0,u.start)+n.substring(u.start+u.length):8===r&&u.start?n=n.substring(0,u.start-1)+n.substring(u.start+1):46===r&&void 0!==u.start&&(n=n.substring(0,u.start)+n.substring(u.start+1)):i&&(s=e.shiftKey,l=String.fromCharCode(e.keyCode),n+=l=s?l.toUpperCase():l.toLowerCase())),o=p.attr("placeholder"),!n&&o&&(n=o),d=p,(a=((c=n)?(w.$testInput||(w.$testInput=S(" ").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).appendTo("body")),w.$testInput.text(c),function(e,t,n){var r,i,o={};if(n)for(r=0,i=n.length;r").addClass(g.wrapperClass).addClass(s).addClass(a),t=S("").addClass(g.inputClass).addClass("items").appendTo(e),n=S('
').appendTo(t).attr("tabindex",w.is(":disabled")?"-1":f.tabIndex),o=S(g.dropdownParent||e),r=S("
").addClass(g.dropdownClass).addClass(a).hide().appendTo(o),i=S("
").addClass(g.dropdownContentClass).appendTo(r),(u=w.attr("id"))&&(n.attr("id",u+"-selectized"),S("label[for='"+u+"']").attr("for",u+"-selectized")),f.settings.copyClassesToDropdown&&r.addClass(s),e.css({width:w[0].style.width}),f.plugins.names.length&&(l="plugin-"+f.plugins.names.join(" plugin-"),e.addClass(l),r.addClass(l)),(null===g.maxItems||1
[data-selectable]",function(e){e.stopImmediatePropagation()}),r.on("mouseenter","[data-selectable]",function(){return f.onOptionHover.apply(f,arguments)}),r.on("mousedown click","[data-selectable]",function(){return f.onOptionSelect.apply(f,arguments)}),d="mousedown",p="*:not(input)",h=function(){return f.onItemSelect.apply(f,arguments)},(c=t).on(d,p,function(e){for(var t=e.target;t&&t.parentNode!==c[0];)t=t.parentNode;return e.currentTarget=t,h.apply(this,[e])}),O(n),t.on({mousedown:function(){return f.onMouseDown.apply(f,arguments)},click:function(){return f.onClick.apply(f,arguments)}}),n.on({mousedown:function(e){e.stopPropagation()},keydown:function(){return f.onKeyDown.apply(f,arguments)},keyup:function(){return f.onKeyUp.apply(f,arguments)},keypress:function(){return f.onKeyPress.apply(f,arguments)},resize:function(){f.positionDropdown.apply(f,[])},blur:function(){return f.onBlur.apply(f,arguments)},focus:function(){return f.ignoreBlur=!1,f.onFocus.apply(f,arguments)},paste:function(){return f.onPaste.apply(f,arguments)}}),y.on("keydown"+m,function(e){f.isCmdDown=e[$?"metaKey":"ctrlKey"],f.isCtrlDown=e[$?"altKey":"ctrlKey"],f.isShiftDown=e.shiftKey}),y.on("keyup"+m,function(e){e.keyCode===C&&(f.isCtrlDown=!1),16===e.keyCode&&(f.isShiftDown=!1),e.keyCode===_&&(f.isCmdDown=!1)}),y.on("mousedown"+m,function(e){if(f.isFocused){if(e.target===f.$dropdown[0]||e.target.parentNode===f.$dropdown[0])return!1;f.$control.has(e.target).length||e.target===f.$control[0]||f.blur(e.target)}}),v.on(["scroll"+m,"resize"+m].join(" "),function(){f.isOpen&&f.positionDropdown.apply(f,arguments)}),v.on("mousemove"+m,function(){f.ignoreHover=!1}),this.revertSettings={$children:w.children().detach(),tabindex:w.attr("tabindex")},w.attr("tabindex",-1).hide().after(f.$wrapper),S.isArray(g.items)&&(f.setValue(g.items),delete g.items),D&&w.on("invalid"+m,function(e){e.preventDefault(),f.isInvalid=!0,f.refreshState()}),f.updateOriginalInput(),f.refreshItems(),f.refreshState(),f.updatePlaceholder(),f.isSetup=!0,w.is(":disabled")&&f.disable(),f.on("change",this.onChange),w.data("selectize",f),w.addClass("selectized"),f.trigger("initialize"),!0===g.preload&&f.onSearchChange("")},setupTemplates:function(){var n=this.settings.labelField,r=this.settings.optgroupLabelField,e={optgroup:function(e){return''+e.html+"
"},optgroup_header:function(e,t){return'"},option:function(e,t){return''+t(e[n])+"
"},item:function(e,t){return''+t(e[n])+"
"},option_create:function(e,t){return'Add '+t(e.input)+" …
"}};this.settings.render=S.extend({},e,this.settings.render)},setupCallbacks:function(){var e,t,n={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in n)n.hasOwnProperty(e)&&(t=this.settings[n[e]])&&this.on(e,t)},onClick:function(e){this.isFocused&&this.isOpen||(this.focus(),e.preventDefault())},onMouseDown:function(e){var t=this,n=e.isDefaultPrevented();S(e.target);if(t.isFocused){if(e.target!==t.$control_input[0])return"single"===t.settings.mode?t.isOpen?t.close():t.open():n||t.setActiveItem(null),!1}else n||window.setTimeout(function(){t.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(e){var i=this;i.isFull()||i.isInputHidden||i.isLocked?e.preventDefault():i.settings.splitOn&&setTimeout(function(){var e=i.$control_input.val();if(e.match(i.settings.splitOn))for(var t=S.trim(e).split(i.settings.splitOn),n=0,r=t.length;n=this.settings.maxItems},updateOriginalInput:function(e){var t,n,r,i,o=this;if(e=e||{},1===o.tagType){for(r=[],t=0,n=o.items.length;t'+s(i)+"");r.length||this.$input.attr("multiple")||r.push(' '),o.$input.html(r.join(""))}else o.$input.val(o.getValue()),o.$input.attr("value",o.$input.val());o.isSetup&&(e.silent||o.trigger("change",o.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var e=this.$control_input;this.items.length?e.removeAttr("placeholder"):e.attr("placeholder",this.settings.placeholder),e.triggerHandler("update",{force:!0})}},open:function(){var e=this;e.isLocked||e.isOpen||"multi"===e.settings.mode&&e.isFull()||(e.focus(),e.isOpen=!0,e.refreshState(),e.$dropdown.css({visibility:"hidden",display:"block"}),e.positionDropdown(),e.$dropdown.css({visibility:"visible"}),e.trigger("dropdown_open",e.$dropdown))},close:function(){var e=this,t=e.isOpen;"single"===e.settings.mode&&e.items.length&&(e.hideInput(),e.isBlurring||e.$control_input.blur()),e.isOpen=!1,e.$dropdown.hide(),e.setActiveOption(null),e.refreshState(),t&&e.trigger("dropdown_close",e.$dropdown)},positionDropdown:function(){var e=this.$control,t="body"===this.settings.dropdownParent?e.offset():e.position();t.top+=e.outerHeight(!0),this.$dropdown.css({width:e[0].getBoundingClientRect().width,top:t.top,left:t.left})},clear:function(e){var t=this;t.items.length&&(t.$control.children(":not(input)").remove(),t.items=[],t.lastQuery=null,t.setCaret(0),t.setActiveItem(null),t.updatePlaceholder(),t.updateOriginalInput({silent:e}),t.refreshState(),t.showInput(),t.trigger("clear"))},insertAtCaret:function(e){var t=Math.min(this.caretPos,this.items.length),n=e[0],r=this.buffer||this.$control[0];0===t?r.insertBefore(n,r.firstChild):r.insertBefore(n,r.childNodes[t]),this.setCaret(t+1)},deleteSelection:function(e){var t,n,r,i,o,a,s,l,u,c=this;if(r=e&&8===e.keyCode?-1:1,i=f(c.$control_input[0]),c.$activeOption&&!c.settings.hideSelected&&(s=c.getAdjacentOption(c.$activeOption,-1).attr("data-value")),o=[],c.$activeItems.length){for(u=c.$control.children(".active:"+(0 '}},e),n.setup=(t=n.setup,function(){t.apply(n,arguments),n.$dropdown_header=S(e.html(e)),n.$dropdown.prepend(n.$dropdown_header)})}),w.define("optgroup_columns",function(s){var o,l=this;s=S.extend({equalizeWidth:!0,equalizeHeight:!0},s),this.getAdjacentOption=function(e,t){var n=e.closest("[data-group]").find("[data-selectable]"),r=n.index(e)+t;return 0<=r&&r
',e=e.firstChild,n.body.appendChild(e),t=u.width=e.offsetWidth-e.clientWidth,n.body.removeChild(e)),t},e=function(){var e,t,n,r,i,o,a;if((t=(a=S("[data-group]",l.$dropdown_content)).length)&&l.$dropdown_content.width()){if(s.equalizeHeight){for(e=n=0;e
'+t.label+"",o.setup=(n=r.setup,function(){if(t.append){var i=r.settings.render.item;r.settings.render.item=function(e){return t=i.apply(o,arguments),n=a,r=t.search(/(<\/[^>]+>\s*)$/),t.substring(0,r)+n+t.substring(r);var t,n,r}}n.apply(o,arguments),o.$control.on("click","."+t.className,function(e){if(e.preventDefault(),!r.isLocked){var t=S(e.currentTarget).parent();r.setActiveItem(t),r.deleteSelection()&&r.setCaret(r.items.length)}})})):function(i,t){t.className="remove-single";var n,o=i,a=''+t.label+" ";i.setup=(n=o.setup,function(){if(t.append){var e=S(o.$input.context).attr("id"),r=(S("#"+e),o.settings.render.item);o.settings.render.item=function(e){return t=r.apply(i,arguments),n=a,S("").append(t).append(n);var t,n}}n.apply(i,arguments),i.$control.on("click","."+t.className,function(e){e.preventDefault(),o.isLocked||o.clear()})})}(this,e)}),w.define("restore_on_backspace",function(r){var i,e=this;r.text=r.text||function(e){return e[this.settings.labelField]},this.onKeyDown=(i=e.onKeyDown,function(e){var t,n;return 8===e.keyCode&&""===this.$control_input.val()&&!this.$activeItems.length&&0<=(t=this.caretPos-1)&&t",{class:function(){var e=[];return e.push(i.options.state?"on":"off"),i.options.size&&e.push(i.options.size),i.options.disabled&&e.push("disabled"),i.options.readonly&&e.push("readonly"),i.options.indeterminate&&e.push("indeterminate"),i.options.inverse&&e.push("inverse"),i.$element.attr("id")&&e.push("id-"+i.$element.attr("id")),e.map(i._getClass.bind(i)).concat([i.options.baseClass],i._getClasses(i.options.wrapperClass)).join(" ")}}),this.$container=s("",{class:this._getClass("container")}),this.$on=s("",{html:this.options.onText,class:this._getClass("handle-on")+" "+this._getClass(this.options.onColor)}),this.$off=s("",{html:this.options.offText,class:this._getClass("handle-off")+" "+this._getClass(this.options.offColor)}),this.$label=s("",{html:this.options.labelText,class:this._getClass("label")}),this.$element.on("init.bootstrapSwitch",this.options.onInit.bind(this,r)),this.$element.on("switchChange.bootstrapSwitch",function(){for(var e=arguments.length,t=Array(e),n=0;n-n._handleWidth/2;n._dragEnd=!1,n.state(n.options.inverse?!t:t)}else n.state(!n.options.state);n._dragStart=!1}},"mouseleave.bootstrapSwitch":function(){n.$label.trigger("mouseup.bootstrapSwitch")}})}},{key:"_externalLabelHandler",value:function(){var t=this,n=this.$element.closest("label");n.on("click",function(e){e.preventDefault(),e.stopImmediatePropagation(),e.target===n[0]&&t.toggleState()})}},{key:"_formHandler",value:function(){var e=this.$element.closest("form");e.data("bootstrap-switch")||e.on("reset.bootstrapSwitch",function(){window.setTimeout(function(){e.find("input").filter(function(){return s(this).data("bootstrap-switch")}).each(function(){return s(this).bootstrapSwitch("state",this.checked)})},1)}).data("bootstrap-switch",!0)}},{key:"_getClass",value:function(e){return this.options.baseClass+"-"+e}},{key:"_getClasses",value:function(e){return s.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),t}();s.fn.bootstrapSwitch=function(o){for(var e=arguments.length,a=Array(1
');var r,i=f.overlay?"":" ngdialog-no-overlay";if((d=T('
')).html(f.overlay?'
'+t+"
":''+t+"
"),d.data("$ngDialogOptions",f),c.ngDialogId=u,f.data&&O.isString(f.data)){var o=f.data.replace(/^\s*/,"")[0];c.ngDialogData="{"===o||"["===o?O.fromJson(f.data):new String(f.data),c.ngDialogData.ngDialogId=u}else f.data&&O.isObject(f.data)&&(c.ngDialogData=f.data,c.ngDialogData.ngDialogId=u);(f.className&&d.addClass(f.className),f.appendClassName&&d.addClass(f.appendClassName),f.width&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.width)?h.style.width=f.width:h.style.width=f.width+"px"),f.height&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.height)?h.style.height=f.height:h.style.height=f.height+"px"),f.disableAnimation&&d.addClass("ngdialog-disabled-animation"),p=f.appendTo&&O.isString(f.appendTo)?O.element(document.querySelector(f.appendTo)):b.body,$.applyAriaAttributes(d,f),f.preCloseCallback)&&(O.isFunction(f.preCloseCallback)?r=f.preCloseCallback:O.isString(f.preCloseCallback)&&c&&(O.isFunction(c[f.preCloseCallback])?r=c[f.preCloseCallback]:c.$parent&&O.isFunction(c.$parent[f.preCloseCallback])?r=c.$parent[f.preCloseCallback]:m&&O.isFunction(m[f.preCloseCallback])&&(r=m[f.preCloseCallback])),r&&d.data("$ngDialogPreCloseCallback",r));if(c.closeThisDialog=function(e){$.closeDialog(d,e)},f.controller&&(O.isString(f.controller)||O.isArray(f.controller)||O.isFunction(f.controller))){var a;f.controllerAs&&O.isString(f.controllerAs)&&(a=f.controllerAs);var s=w(f.controller,O.extend(n,{$scope:c,$element:d}),!0,a);f.bindToController&&O.extend(s.instance,{ngDialogId:c.ngDialogId,ngDialogData:c.ngDialogData,closeThisDialog:c.closeThisDialog,confirm:c.confirm}),"function"==typeof s?d.data("$ngDialogControllerController",s()):d.data("$ngDialogControllerController",s)}if(v(function(){var e=document.querySelectorAll(".ngdialog");$.deactivateAll(e),g(d)(c);var t=y.innerWidth-b.body.prop("clientWidth");b.html.addClass(f.bodyClassName),b.body.addClass(f.bodyClassName);var n=t-(y.innerWidth-b.body.prop("clientWidth"));0window.innerHeight&&(l=v,t++,e=0);var u=l?0===e?l:l+w:v,c=n+t*(b+s);o.css(o._positionY,u+"px"),"center"==o._positionX?o.css("left",parseInt(window.innerWidth/2-s/2)+"px"):o.css(o._positionX,c+"px"),r[o._positionY+o._positionX]=u+a,0m.maxCount&&0===i&&o.scope().kill(!0),e++}}},i=c(e)(n);i._positionY=h.positionY,i._positionX=h.positionX,i._priority=h.priority,i.addClass(h.type);var o=function(e){("click"===(e=e.originalEvent||e).type||"opacity"===e.propertyName&&1<=e.elapsedTime)&&(n.onClose&&n.$apply(n.onClose(i)),i.remove(),$.splice($.indexOf(i),1),n.$destroy(),r())};h.closeOnClick&&(i.addClass("clickable"),i.bind("click",o)),i.bind("webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",o),angular.isNumber(h.delay)&&u(function(){i.addClass("killed")},h.delay),t("none"),angular.element(document.querySelector(h.container)).append(i);var a=-(parseInt(i[0].offsetHeight)+50);if(i.css(i._positionY,a+"px"),$.push(i),"center"==h.positionX){var s=parseInt(i[0].offsetWidth);i.css("left",parseInt(window.innerWidth/2-s/2)+"px")}u(function(){t("")}),n._templateElement=i,n.kill=function(e){e?(n.onClose&&n.$apply(n.onClose(n._templateElement)),$.splice($.indexOf(n._templateElement),1),n._templateElement.remove(),n.$destroy(),u(r)):n._templateElement.addClass("killed")},u(r),_||(angular.element(g).bind("resize",function(e){u(r)}),_=!0),l.resolve(n)}var l=a.defer();"object"==typeof h&&null!==h||(h={message:h}),h.scope=h.scope?h.scope:o,h.template=h.templateUrl?h.templateUrl:m.templateUrl,h.delay=angular.isUndefined(h.delay)?s:h.delay,h.type=e||h.type||m.type||"",h.positionY=h.positionY?h.positionY:m.positionY,h.positionX=h.positionX?h.positionX:m.positionX,h.replaceMessage=h.replaceMessage?h.replaceMessage:m.replaceMessage,h.onClose=h.onClose?h.onClose:m.onClose,h.closeOnClick=null!==h.closeOnClick&&void 0!==h.closeOnClick?h.closeOnClick:m.closeOnClick,h.container=h.container?h.container:m.container,h.priority=h.priority?h.priority:m.priority;var n=i.get(h.template);return n?t(n):r.get(h.template,{cache:!0}).then(function(e){t(e.data)}).catch(function(e){throw new Error("Template ("+h.template+") could not be loaded. "+e)}),l.promise};return t.primary=function(e){return this(e,"primary")},t.error=function(e){return this(e,"error")},t.success=function(e){return this(e,"success")},t.info=function(e){return this(e,"info")},t.warning=function(e){return this(e,"warning")},t.clearAll=function(){angular.forEach($,function(e){e.addClass("killed")})},t}]}),angular.module("ui-notification").run(["$templateCache",function(e){e.put("angular-ui-notification.html",'')}]),function(){var w="__default";angular.module("angularUtils.directives.dirPagination",[]).directive("dirPaginate",["$compile","$parse","paginationService",function(m,v,y){return{terminal:!0,multiElement:!0,priority:100,compile:function(e,t){var f=t.dirPaginate,n=f.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),r=/\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;if(null===n[2].match(r))throw"pagination directive: the 'itemsPerPage' filter must be set.";var i=n[2].replace(r,""),g=v(i);o=e,angular.forEach(o,function(e){1===e.nodeType&&angular.element(e).attr("dir-paginate-no-compile",!0)});var o;var a=t.paginationId||w;return y.registerInstance(a),function(e,t,n){var r=v(n.paginationId)(e)||n.paginationId||w;y.registerInstance(r);var i,o,a,s,l,u,c,d=(u=r,c=!!(l=f).match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/),u===w||c?l:l.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/,"$1 : '"+u+"'"));o=n,a=d,(i=t)[0].hasAttribute("dir-paginate-start")||i[0].hasAttribute("data-dir-paginate-start")?(o.$set("ngRepeatStart",a),i.eq(i.length-1).attr("ng-repeat-end",!0)):o.$set("ngRepeat",a),s=t,angular.forEach(s,function(e){1===e.nodeType&&angular.element(e).removeAttr("dir-paginate-no-compile")}),s.eq(0).removeAttr("dir-paginate-start").removeAttr("dir-paginate").removeAttr("data-dir-paginate-start").removeAttr("data-dir-paginate"),s.eq(s.length-1).removeAttr("dir-paginate-end").removeAttr("data-dir-paginate-end");var p=m(t),h=function(e,t,n){var r;if(t.currentPage)r=v(t.currentPage);else{var i=(n+"__currentPage").replace(/\W/g,"_");e[i]=1,r=v(i)}return r}(e,n,r);y.setCurrentPageParser(r,h,e),void 0!==n.totalItems?(y.setAsyncModeTrue(r),e.$watch(function(){return v(n.totalItems)(e)},function(e){0<=e&&y.setCollectionLength(r,e)})):(y.setAsyncModeFalse(r),e.$watchCollection(function(){return g(e)},function(e){if(e){var t=e instanceof Array?e.length:Object.keys(e).length;y.setCollectionLength(r,t)}})),p(e)}}}}]).directive("dirPaginateNoCompile",function(){return{priority:5e3,terminal:!0}}).directive("dirPaginationControls",["paginationService","paginationTemplate",function(d,n){var p=/^\d+$/,e={restrict:"AE",scope:{maxSize:"=?",onPageChange:"&?",paginationId:"=?",autoHide:"=?"},link:function(r,e,t){var n=t.paginationId||w,i=r.paginationId||t.paginationId||w;if(!d.isRegistered(i)&&!d.isRegistered(n)){var o=i!==w?" (id: "+i+") ":" ";window.console&&console.warn("Pagination directive: the pagination controls"+o+"cannot be used without the corresponding pagination directive, which was not found at link time.")}r.maxSize||(r.maxSize=9);r.autoHide=void 0===r.autoHide||r.autoHide,r.directionLinks=!angular.isDefined(t.directionLinks)||r.$parent.$eval(t.directionLinks),r.boundaryLinks=!!angular.isDefined(t.boundaryLinks)&&r.$parent.$eval(t.boundaryLinks);var a=Math.max(r.maxSize,5);function s(e){if(d.isRegistered(i)&&c(e)){var t=r.pagination.current;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,u(),r.onPageChange&&r.onPageChange({newPageNumber:e,oldPageNumber:t})}}function l(){if(d.isRegistered(i)){var e=parseInt(d.getCurrentPage(i))||1;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,r.pagination.last=r.pages[r.pages.length-1],r.pagination.last