3 Commits 017e3c6c03 ... 76df744061

Autor SHA1 Mensaje Fecha
  junggilpark 76df744061 update 2025-09-16 hace 1 mes
  junggilpark 0cf8f0a1f9 2024-12-31 update hace 10 meses
  junggilpark 9801f46d53 2024-12-31 update hace 10 meses
Se han modificado 31 ficheros con 9221 adiciones y 196 borrados
  1. 1 0
      .gitignore
  2. 13 4
      src/main/java/com/tsi/sig/server/controller/LoginController.java
  3. 20 8
      src/main/java/com/tsi/sig/server/controller/MainController.java
  4. 3 0
      src/main/java/com/tsi/sig/server/mapper/MainMapper.java
  5. 1 1
      src/main/java/com/tsi/sig/server/security/WebSecurityConfig.java
  6. 29 1
      src/main/java/com/tsi/sig/server/service/MainService.java
  7. 14 0
      src/main/java/com/tsi/sig/server/vo/NodeStatisticsVo.java
  8. 1 1
      src/main/resources/application.yml
  9. 48 0
      src/main/resources/mybatis/mapper/main.xml
  10. 84 24
      src/main/resources/static/css/main.css
  11. 4253 0
      src/main/resources/static/css/main_new.css
  12. 164 0
      src/main/resources/static/css/tree.css
  13. BIN
      src/main/resources/static/images/chevron-down.png
  14. BIN
      src/main/resources/static/images/chevron-right.png
  15. 198 0
      src/main/resources/static/js/common/cvibDetail_new.js
  16. 64 59
      src/main/resources/static/js/map.js
  17. 18 19
      src/main/resources/static/js/signal.js
  18. 4 3
      src/main/resources/static/js/signalInfo.js
  19. 387 0
      src/main/resources/static/js/signal_new.js
  20. 19 22
      src/main/webapp/WEB-INF/jsp/bottomListFrame.jsp
  21. 118 0
      src/main/webapp/WEB-INF/jsp/bottomListFrame_new.jsp
  22. 1 1
      src/main/webapp/WEB-INF/jsp/common.jsp
  23. 10 8
      src/main/webapp/WEB-INF/jsp/cvibInfoDetail.jsp
  24. 597 0
      src/main/webapp/WEB-INF/jsp/cvibInfoDetail_new.jsp
  25. 3 1
      src/main/webapp/WEB-INF/jsp/denied.jsp
  26. 4 4
      src/main/webapp/WEB-INF/jsp/login.jsp
  27. 51 33
      src/main/webapp/WEB-INF/jsp/main.jsp
  28. 666 0
      src/main/webapp/WEB-INF/jsp/main_new.jsp
  29. 7 7
      src/main/webapp/WEB-INF/jsp/treeListFrame.jsp
  30. 1044 0
      src/main/webapp/WEB-INF/jsp/treeListFrame_Tree.jsp
  31. 1399 0
      src/main/webapp/WEB-INF/jsp/treeListFrame_new.jsp

+ 1 - 0
.gitignore

@@ -38,3 +38,4 @@ build/
 .git
 conf/
 *.bat
+*.zip

+ 13 - 4
src/main/java/com/tsi/sig/server/controller/LoginController.java

@@ -1,6 +1,7 @@
 package com.tsi.sig.server.controller;
 
 import com.tsi.sig.server.objects.UserVO;
+import com.tsi.sig.server.security.TSIEncoder;
 import com.tsi.sig.server.service.LoginServiceImpl;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -29,6 +30,7 @@ import java.io.IOException;
 public class LoginController {
     private final LoginServiceImpl loginServiceImpl;
     private final String REDIR2DENY = "redirect:/denied.do";
+    private final TSIEncoder encoder;
 
     @RequestMapping({"/showlogin.do"})
     public String showlogin() throws Exception, NotFoundException {
@@ -46,19 +48,25 @@ public class LoginController {
 
     @PostMapping({"/login.do"})
     public String login(UserVO userVO, RedirectAttributes redir) throws IOException {
+
         if (userVO == null) {
             return "redirect:/denied.do";
         } else {
             String reqId = userVO.getUserId();
             String reqPw = userVO.getUserPswd();
             UserVO userDetails = this.loginServiceImpl.loadUserByUsername(reqId);
-           if (userDetails.getUserId() == null) {
+           if (userDetails == null){
                 return "redirect:/denied.do";
-            } else {
+           }
+           else {
                 redir.addFlashAttribute("userVO", userDetails);
                 if (userDetails.getUseyn().equals("N")) {
                     return "redirect:/denied.do";
-                } else {
+                }
+                else if (!encoder.matches(reqPw, userDetails.getUserPswd())) {
+                    return "redirect:/denied.do";
+                }
+                else {
                     this.loginServiceImpl.initFailCnt(reqId);
                     SecurityContext context = SecurityContextHolder.createEmptyContext();
                     UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, reqPw, userDetails.getAuthorities());
@@ -73,7 +81,8 @@ public class LoginController {
     @GetMapping({"/login.do"})
     public String showDashBoard(Model model, @Value("${kakaokey}") String kakaokey) {
         model.addAttribute("kakaokey", kakaokey);
-        return "main";
+//        return "main";
+        return "main_new";
     }
 
     @GetMapping({"/denied.do"})

+ 20 - 8
src/main/java/com/tsi/sig/server/controller/MainController.java

@@ -9,10 +9,7 @@ import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.*;
 
 import javax.naming.AuthenticationException;
 import javax.servlet.http.HttpServletRequest;
@@ -51,7 +48,8 @@ public class MainController {
         HttpSession session = request.getSession();
         SecurityContext member = (SecurityContext)session.getAttribute("SPRING_SECURITY_CONTEXT");
         model.addAttribute("kakaokey", kakaokey);
-        return member == null ? "login" : "main";
+        log.info("/main.do");
+        return member == null ? "login" : "main_new";
     }
 
     @RequestMapping({"/refreshNodeList.do"})
@@ -68,14 +66,16 @@ public class MainController {
     @RequestMapping(value = "/getBottomListFrame.do")
     public String getBottomListFrame() {
         //log.info("CALL.........................................: getBottomListFrame");
-        return "bottomListFrame";
+        return "bottomListFrame_new";
     }
 
     @RequestMapping(value = "/getTreeListFrame.do")
     public String getTreeListFrame(Model model) {
         //log.info("CALL.........................................: getTreeListFrame");
         model.addAttribute("center", this.mainService.getCenterCode());
-        return "treeListFrame";
+        return "treeListFrame_Tree";
+//        return "treeListFrame_new";
+//        return "treeListFrame";
     }
 
     @RequestMapping(value = "/getIntTreeList.do", method = RequestMethod.POST)
@@ -123,7 +123,7 @@ public class MainController {
         }
         model.addAttribute("node", node);
         model.addAttribute("kakaokey", kakaokey);
-        return "cvibInfoDetail";
+        return "cvibInfoDetail_new";
     }
 
     @RequestMapping(value = "/getCvibList.do", method = RequestMethod.POST)
@@ -178,4 +178,16 @@ public class MainController {
     public List<EvpCenterVo> getCenterCode() {
         return mainService.getCenterCode();
     }
+
+    @PostMapping("/getNodeStatistics.do")
+    @ResponseBody
+    public List<NodeStatisticsVo> getNodeStatistics(@RequestParam Map<String, Object> paramMap){
+        return mainService.getNodeStatistics(paramMap);
+    }
+
+    @PostMapping("/getNodeStatusCntStat.do")
+    @ResponseBody
+    public List<NodeStatisticsVo> getNodeStatusCntStat(@RequestParam Map<String, Object> paramMap){
+        return mainService.getNodeStatusCntStat(paramMap);
+    }
 }

+ 3 - 0
src/main/java/com/tsi/sig/server/mapper/MainMapper.java

@@ -31,4 +31,7 @@ public interface MainMapper {
     List<EvpSignalVo> getEvpSignalCurrList(Map<String, Object> paramMap);
     List<EvpEventVo> getEvpEventCurrList(Map<String, Object> paramMap);
     List<EvpCenterVo> getEvpCenterCode();
+    List<NodeStatisticsVo> getNodeStatusStat(Map<String, Object> paramMap);
+    List<NodeStatisticsVo> getNodeStatusCntStat(Map<String, Object> paramMap);
+
 }

+ 1 - 1
src/main/java/com/tsi/sig/server/security/WebSecurityConfig.java

@@ -52,7 +52,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     }
 
     public void configure(WebSecurity web) throws Exception, WebServiceException {
-        web.ignoring().antMatchers(new String[]{"/css/**", "/js/**", "/images/**", "/actuator/**"});
+        web.ignoring().antMatchers("/css/**", "/js/**", "/images/**", "/actuator/**", "/denied.do");
     }
 
     public void configure(AuthenticationManagerBuilder auth) throws Exception, WebServiceException {

+ 29 - 1
src/main/java/com/tsi/sig/server/service/MainService.java

@@ -11,6 +11,9 @@ import org.springframework.stereotype.Service;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
 
 
@@ -207,9 +210,27 @@ public class MainService {
     public List<EvpServiceVo> getEvpServiceList() {
         Map<String, EvpsData> evpDataMap = repo.getEvpDataMap();
         List<EvpsData> list = new ArrayList<>(evpDataMap.values());
+        List<String> keys = new ArrayList<>(evpDataMap.keySet());
         List<EvpServiceVo> result = new ArrayList<>();
+        int idx = 0;
         for (EvpsData data : list) {
-            result.add(data.getEvpServiceVo());
+            EvpServiceVo vo = data.getEvpServiceVo();
+            String clctDt = vo.getClctDt();
+            if (clctDt.equals("") || clctDt == null) {
+               repo.getEvpDataMap().remove(keys.get(idx));
+               continue;
+            }
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            LocalDateTime targetTime = LocalDateTime.parse(clctDt, formatter);
+            LocalDateTime now = LocalDateTime.now();
+            long minutesDiff = ChronoUnit.MINUTES.between(targetTime, now);
+            if (minutesDiff >= 10) {
+                repo.getEvpDataMap().remove(keys.get(idx));
+            }
+            else {
+                result.add(vo);
+            }
+            idx++;
         }
         return result;
     }
@@ -241,4 +262,11 @@ public class MainService {
     public List<EvpCenterVo> getCenterCode() {
         return this.mainMapper.getEvpCenterCode();
     }
+
+    public List<NodeStatisticsVo> getNodeStatistics(Map<String, Object> paramMap) {
+        return this.mainMapper.getNodeStatusStat(paramMap);
+    }
+    public List<NodeStatisticsVo> getNodeStatusCntStat(Map<String, Object> paramMap) {
+        return this.mainMapper.getNodeStatusCntStat(paramMap);
+    }
 }

+ 14 - 0
src/main/java/com/tsi/sig/server/vo/NodeStatisticsVo.java

@@ -0,0 +1,14 @@
+package com.tsi.sig.server.vo;
+
+import lombok.Data;
+
+@Data
+public class NodeStatisticsVo {
+    private String nodeid;
+    private String eventDt;
+    private String name;
+    private String status;
+    private String regionNm;
+    private String serverid;
+    private String dataCnt;
+}

+ 1 - 1
src/main/resources/application.yml

@@ -98,7 +98,7 @@ application:
         groupId: tsi-evp-was
         topic: utic-evps
 kakaokey: 8d9fdadc20c00319444ac31b9a2b9a3b
-
+#kakaokey: 00c3f7d32c1f1822dc98ad72e35e0223
 ---
 spring:
   config:

+ 48 - 0
src/main/resources/mybatis/mapper/main.xml

@@ -412,4 +412,52 @@
 		  FROM tb_evp_center
 		 WHERE use_yn = 'Y'
 	</select>
+
+
+	<select id="getNodeStatusStat" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.NodeStatisticsVo">
+		SELECT A.nodeid,
+		CONCAT(DATE_FORMAT(A.eventdt, '%Y-%m-%d %H:%i:%S.'), LEFT(DATE_FORMAT(A.eventdt, '%f'), 3)) AS event_dt,
+		B.name,
+		CONCAT('[', A.status, '] ', CASE A.status WHEN '1' THEN 'ONLINE' WHEN '0' THEN 'OFFLINE' ELSE 'Unknow' END) AS status,
+		CONCAT('[',B.region_id, '] ', C.region_name) AS region_nm,
+		IFNULL(A.serverid, '-') AS serverid
+		FROM tb_tsc_node_status_hs A
+		JOIN tb_tsc_node B
+		ON A.nodeid = B.nodeid
+		JOIN tb_tsc_region C
+		ON B.region_id = C.region_id
+		WHERE A.eventdt BETWEEN CONCAT(#{fromDt}, ' 00:00:00') AND CONCAT(#{toDt}, ' 23:59:59')
+		<if test="regionId != '전체'">
+			AND B.region_id = #{regionId}
+		</if>
+		<if test="nodeId != null and nodeId != ''">
+			AND (A.nodeid = #{nodeId}
+			OR B.name like concat('%', #{nodeId}, '%'))
+		</if>
+		ORDER BY A.eventdt DESC
+	</select>
+
+	<select id="getNodeStatusCntStat" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.NodeStatisticsVo">
+		SELECT A.nodeid,
+		B.name,
+		CONCAT('[', A.status, '] ', CASE A.status WHEN '1' THEN 'ONLINE' WHEN '0' THEN 'OFFLINE' ELSE 'Unknow' END) AS status,
+		CONCAT('[',B.region_id, '] ', C.region_name) AS region_nm,
+		COUNT(1) AS data_cnt
+		FROM tb_tsc_node_status_hs A
+		JOIN tb_tsc_node B
+		ON A.nodeid = B.nodeid
+		JOIN tb_tsc_region C
+		ON B.region_id = C.region_id
+		WHERE A.eventdt BETWEEN CONCAT(#{fromDt}, ' 00:00:00') AND CONCAT(#{toDt}, ' 23:59:59')
+		<if test="regionId != '전체'">
+			AND B.region_id = #{regionId}
+		</if>
+		<if test="nodeId != null and nodeId != ''">
+			AND (A.nodeid = #{nodeId}
+			OR B.name like concat('%', #{nodeId}, '%'))
+		</if>
+		GROUP BY A.nodeid, A.status
+		ORDER BY B.region_id, B.nodeid, status
+	</select>
+
 </mapper>

+ 84 - 24
src/main/resources/static/css/main.css

@@ -16,7 +16,7 @@ body, html {
     color: #515151;
     height: 100%;
     width: 100%;
-    min-width: 640px;
+    /*min-width: 640px;*/
     overflow: hidden;
 }
 body {
@@ -52,7 +52,11 @@ textarea {
 
 table {
     border-collapse: collapse;
-    border-spacing: 0px;
+    border-spacing: 0;
+}
+
+table#cvibBottomBodyTable {
+    border-collapse: separate;
 }
 
 img {
@@ -84,17 +88,17 @@ table {
     width: 400px;
     border-collapse: collapse;
     margin: auto;
-    background: #fff;
+    /*background: #fff;*/
 }
 
 #mapWrapper {
-    width: calc(100% - 338px);
+    width: calc(100% - 287px);
     height: calc(100% - 230px);
     /* border-right: 1px solid #bbb;
     border-top: 1px solid #bbb;
     border-left: 1px solid #bbb; */
     border: 1px solid #595959;
-    margin: 5px 0 0 332px;
+    margin: 5px 0 0 287px;
 }
 
 .detailMap {
@@ -126,11 +130,11 @@ table {
     right: 0px;
     width: 40%;
     /*height: 70%;*/
-    height: calc(75% - 10px);
+    height: calc(75% - 8px);
     float: left;
     display: none;
     border: 1px solid #bbb;
-    margin-right: 5px
+    /*margin-right: 5px*/
 }
 
 .roadview {
@@ -1138,7 +1142,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .iframeTreeList {
     float: left;
-    width: 325px;
+    width: 280px;
     height: 100%;
     margin-left: 5px;
     overflow: hidden;
@@ -1148,7 +1152,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .iframeBottomList {
     margin-left: 2px;
-    width: calc(100% - 337px);
+    width: calc(100% - 287px);
     height: 222px;
     overflow: hidden;
     border: 0;
@@ -1529,20 +1533,53 @@ select#region {
 .wideMap {
     position: absolute;
     /*top: 40%;*/
-    top: 10px;
-    left: -10px;
+    top : 0;
+    left: 0;
+    /*top: 10px;*/
+    /*left: -8px;*/
     cursor: pointer;
     z-index: 10;
-    rotate: 90deg;
-
+    rotate: 180deg;
+    background-image: url(/images/chevron-right.png);
+    width: 20px;
+    height: 30px;
+    background-color: #2c2c2c;
+    border-top: 1px solid #595959;
+    border-left: 1px solid #595959;
+    border-bottom: 1px solid #595959;
+    background-position: center;
+    border-radius: 5px 0 0 5px;
+}
+.wideMap.on {
+    rotate: 0deg;
+    border-left: 0;
+    border-right: 1px solid #595959;
+    border-radius: 0 5px 5px 0;
 }
 
 .upDownMap {
     position: absolute;
-    right: 0;
-    bottom: 0px;
+    right: 2px;
+    bottom: 0;
     cursor: pointer;
-    z-index: 10
+    z-index: 10;
+    rotate: 0deg;
+    background-image: url('/images/chevron-down.png');
+    width: 30px;
+    height: 20px;
+    background-color: #2c2c2c;
+    border-top: 1px solid #595959;
+    border-left: 1px solid #595959;
+    border-right: 1px solid #595959;
+    background-position: center;
+    border-radius: 5px 5px 0 0;
+}
+
+.upDownMap.on {
+    rotate: 180deg;
+    border-bottom: 1px solid #595959;
+    border-top: 0;
+    border-radius: 0 0 5px 5px;
 }
 
 .bottomMenu {
@@ -1576,7 +1613,10 @@ select#region {
 
 .bottomMenu .bottomHead table thead,
 .bottomMenu .bottomBody table thead {
-    background-color: #dfe8ef
+    background-color: #595959;
+    position: sticky;
+    top: 0;
+    right: 0;
 }
 
 .bottomMenu .bottomBody table th,
@@ -1586,15 +1626,10 @@ select#region {
     text-align: center;
     font-weight: bold;
     color: #b2b2b2;
-    /*background: url(/images/table_bg.png) repeat-x;*/
     background-color: black;
     line-height: 20px;
-    /*border-top: 1px solid #595959;*/
     border-bottom: 1px solid #595959;
     border-right: 1px solid #595959;
-    position: sticky;
-    top: 0;
-    left: 0;
 }
 
 .bottomMenu .bottomBody {
@@ -3391,6 +3426,7 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 #historyList::-webkit-scrollbar,
 .bottomInfo .bottomInfoBottom .bottomInfoBody::-webkit-scrollbar {
     width: 10px;
+    height: 10px;
     border-right: 1px solid #595959;
     background-color: #eee;
     border-radius: 5px;
@@ -3559,7 +3595,7 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 .evp_legend {
     position: absolute;
     bottom: 228px;
-    right: 50px;
+    right: 34px;
     z-index: 20;
     display: none;
 }
@@ -3637,4 +3673,28 @@ img[src="images/logout.png"]{
 
 #intTree{
     background-color: #212121;
-}
+}
+
+
+/* 전체 스크롤바 스타일 */
+::-webkit-scrollbar {
+    width: 2px; /* 스크롤바 두께 */
+    height: 2px;
+}
+
+/* 스크롤바 트랙 (배경) */
+::-webkit-scrollbar-track {
+    background: #1e1e1e; /* 다크 모드에 어울리는 어두운 배경 */
+    border-radius: 4px;
+}
+
+/* 스크롤바 핸들 (움직이는 부분) */
+::-webkit-scrollbar-thumb {
+    background: #555; /* 중간 회색 */
+    border-radius: 4px;
+}
+
+/* 스크롤바 핸들 호버 효과 */
+::-webkit-scrollbar-thumb:hover {
+    background: #777; /* 더 밝은 회색 */
+}

+ 4253 - 0
src/main/resources/static/css/main_new.css

@@ -0,0 +1,4253 @@
+@charset "utf-8";
+/* CSS 초기화*/
+
+html {
+    height: 100%;
+}
+
+* {
+    margin: 0px;
+    padding: 0px;
+}
+
+.headMenu ul .toggleBtn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.headMenu ul .toggleBtn div {
+    padding: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 5px;
+    font-family: 맑은고딕;
+    font-size: 18px;
+    font-weight: bold;
+}
+.headMenu ul .toggleBtn a.on, .headMenu ul .toggleBtn a:hover{
+    filter: brightness(1.1) !important;
+    color: white;
+    background-color: #404040;
+}
+
+.headMenu ul .toggleBtn i {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #565454;
+    border-radius: 50%;
+    width: 35px;
+    height: 35px;
+}
+body, html {
+    font-size: 10pt;
+    font-family: '돋움', dotum, '굴림', Gulim, Tahoma, serif, sans-serif;
+    color: #515151;
+    height: 100%;
+    width: 100%;
+    /*min-width: 640px;*/
+    overflow: hidden;
+}
+body {
+    background-color: #212121;
+}
+
+a {
+    text-decoration: none;
+    vertical-align: middle;
+}
+
+img, fieldset {
+    border: none;
+}
+
+input, textarea {
+    vertical-align: middle;
+    text-indent: 7px;
+    font-size: 12px;
+    font-family: '돋움', dotum, '굴림', Gulim, Tahoma, serif, sans-serif;
+    color: #515151;
+}
+
+/* input[type="text"] {
+	height: 23px;
+	box-sizing: border-box;
+	border: 1px solid #cccccc;
+} */
+textarea {
+    padding: 5px;
+    text-indent: 0;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+
+table#cvibBottomBodyTable {
+    border-collapse: separate;
+}
+
+img {
+    vertical-align: middle;
+}
+
+table caption {
+    visibility: hidden;
+    height: 0;
+}
+
+ul, ol, li, label {
+    list-style: none;
+}
+
+label {
+    /* visibility: hidden;
+	text-indent: -99999em; */
+}
+
+button {
+    border: 0;
+    background: transparent;
+    cursor: pointer;
+    overflow: visible;
+}
+
+table {
+    width: 400px;
+    border-collapse: collapse;
+    margin: auto;
+    /*background: #fff;*/
+}
+
+#mapWrapper {
+    width: calc(100% - 287px);
+    height: calc(100% - 230px);
+    /* border-right: 1px solid #bbb;
+    border-top: 1px solid #bbb;
+    border-left: 1px solid #bbb; */
+    border: 1px solid #595959;
+    margin: 5px 0 0 287px;
+}
+
+.detailMap {
+    width: auto;
+    height: 100%;
+}
+
+.map {
+    /* height: calc(75% - 10px);
+    border: 1px solid #bbb;
+    margin: 5px; */
+    width: auto;
+    height: 100%;
+}
+
+.map2 {
+    margin: 5px 5px 0 0;
+    width: calc(60% - 310px);
+    /* height: 70%; */
+    height: calc(75% - 10px);
+    /* float: left; */
+    border: 1px solid gray;
+    z-index: 10
+}
+
+#rvWrapper {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    width: 40%;
+    /*height: 70%;*/
+    height: calc(75% - 10px);
+    float: left;
+    display: none;
+    border: 1px solid #bbb;
+    margin-right: 5px
+}
+
+.roadview {
+    width: 100%;
+    height: 100%;
+}
+
+.popMap {
+    position: absolute;
+    width: 908px;
+    height: 250px;
+    border: 1px solid #bbb;
+    margin: 5px;
+    z-index: 1;
+}
+
+.cvibMap {
+    position: relative;
+    width: 908px;
+    /* width: 908px; */
+    /*height: 300px;*/
+    height: 450px;
+    border: 1px solid #bbb;
+    margin: 5px;
+    z-index: 1;
+}
+
+.cvibMapAndInfo {
+    position: relative;
+    width: 100%;
+    /* width: 908px; */
+    /*height: 300px;*/
+    height: 290px;
+    padding: 0 5px;
+    z-index: 1;
+    box-sizing: border-box;
+}
+
+.crossLoadMap {
+    width: 40%;
+    height: 100%;
+    border: 1px solid #595959;
+    float: left;
+
+}
+
+.crossLoadInfo {
+    width: 58.9%;
+    height:450px;
+    border: 1px solid #595959;
+    float: left;
+}
+
+.mapCover {
+    position: absolute;
+    width: 100%;
+    height: 250px;
+    margin: 5px;
+    z-index: 100;
+}
+
+.bottomInfoTop th,
+.bottomInfoTop td,
+.bottomInfoBody th,
+.bottomInfoBody td {
+    font-size: 11px;
+}
+.bottomInfoBody table thead {
+    display: table-header-group;
+    position: sticky;
+    top: 0;
+    left: 0;
+}
+/*인풋박스 ie8 추가 핵*/
+/*#obeKeyword {
+	border: 3px solid #b0b0b0;
+	width: 140px;
+	height: 29px;
+	margin-left: -30px;
+	width: 140px \0/IE8+9;
+	height: 29px \0/IE8+9;
+	padding-top: 2px \0/IE8+9;
+	font-size: 14px;
+	font-weight: bold;
+}
+#input1 {
+	color: #333;
+	background:#f5f5f5 url("/imgase/icon_select_20110225.gif")right: 8px no-repeat;
+	text-decoration: none;
+	height: 29px;
+	width: 150px;
+	text-align: center;
+	border: 1px solid #bobobo;
+	width: 150px \0/IE8+9;
+	height: 29px \0/IE8+9;
+	padding-top: -10px \0/IE8+9;
+	font-size: 14px;
+	font-weight: bold;
+	padding-bottom: 3px \0/IE8+9;
+	;
+}
+#input2 {
+	color: #333;
+	text-decoration: none;
+	height: 29px;
+	width: 150px;
+	text-align: center;
+	background:#f5f5f5 url("/imgase/icon_select_20110225.gif")right: 8px no-repeat;
+	border: 1px solid #bobobo;
+	width: 150px \0/IE8+9;
+	height: 29px \0/IE8+9;
+	padding-top: -10px \0/IE8+9;
+	padding-bottom: 3px \0/IE8+9;
+	font-size: 14px;
+	font-weight: bold;
+	;
+}
+#input3 {
+	color: #333;
+	text-decoration: none;
+	height: 29px;
+	width: 150px;
+	text-align: center;
+	 background:#f5f5f5 url("/imgase/icon_select_20110225.gif")right: 8px no-repeat;
+	border: 1px solid #bobobo;
+	width: 150px \0/IE8+9;
+	height: 29px \0/IE8+9;
+	padding-bottom: 3px \0/IE8+9;
+	padding-top: -10px \0/IE8+9;
+	font-size: 14px;
+	font-weight: bold;
+	;
+}*/
+
+/* float 해제 */
+.f-clear:after {
+    content: ".";
+    display: block;
+    clear: both;
+    height: 0;
+    visibility: hidden;
+}
+
+.f-clear {
+    display: inline-block;
+    height: 1%;
+    display: block;
+}
+
+/*인풋박스 ie8 추가 핵*/
+.sang {
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+.header {
+    overflow: hidden;
+    height: 80px;
+    background: #2f2f2f;
+}
+
+.header > h1 {
+    overflow: hidden;
+    float: left;
+    margin: 0 42px 0 12px;
+}
+
+.header > .i_p00 {
+    float: left;
+    display: block;
+}
+
+.header > .i_p00 > li {
+    float: left;
+}
+
+.header > .i_p01 {
+    float: right;
+    margin-right: 15px;
+    margin-top: 25px;
+}
+
+.header > .i_p01 > li {
+    float: left;
+    margin-right: 7px;
+}
+
+/* .hline{background:#0090fa; height:3px; width:100%; cursor:pointer;} */
+.hline {
+    background: #838285;
+    height: 2px;
+    width: 100%;
+    cursor: pointer;
+}
+
+.obd {
+    float: left;
+    width: 310px;
+}
+
+.obd2 {
+    float: left;
+    width: 100px;
+    height: 29px;
+    border: 1px solid #2f2f2f;
+}
+
+/* #2f2f2f */
+.obd3 {
+    float: left;
+    width: 20px;
+    height: 29px;
+    border: 1px solid #2f2f2f;
+}
+
+.obd > li {
+    float: left;
+    margin-right: 9px;
+}
+
+.container {
+    position: relative;
+    width: 100%;
+    height: calc(100% - 66px);
+    display: block;
+}
+
+.foot {
+    width: 100%;
+    position: fixed;
+    bottom: 0px;
+    z-index: 1000;
+    height: 30px;
+    background: #29292a;
+    color: #ffffff;
+    padding: 15px;
+}
+
+.foot > ul {
+    height: 25px;
+    padding-left: 10px;
+    width: 100%;
+}
+
+.foot > ul > li {
+    font-weight: bold;
+    font-size: 20px;
+    float: left;
+}
+
+.foot > ul > li > span {
+    font-weight: bold;
+    font-size: 20px;
+    margin-left: 10px;
+}
+
+.foot > ul > li > span > a {
+    color: #fff;
+    font-weight: bold;
+}
+
+.footbtn {
+    position: absolute;
+    left: 45%;
+    bottom: 0;
+    cursor: pointer;
+    z-index: 1000;
+    top: -25px;
+}
+
+.footbtn2 {
+    position: absolute;
+    left: 45%;
+    bottom: 0;
+    cursor: pointer;
+    z-index: 1000;
+
+}
+.error-box {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    background-color: #eeeeee;
+    top: 0;
+    left: 0;
+    z-index: 99999;
+    display: none;
+    align-items: center;
+    justify-content: center;
+    background-color: rgba(0, 0, 0, 0.4);
+}
+.error-content {
+    width: 1200px;
+    height: 814px;
+    border: 1px solid #424242;
+    position: relative;
+}
+.error-content .modal-title-box {
+    height: 30px;
+    background-color: #212121;
+}
+.error-content .button {
+    margin-left: auto;
+    border: 1px solid #848484;
+    gap: 5px;
+}
+.error-content input[type="date"] {
+    width: 120px;
+}
+/*.error-content input[type="date"]::-webkit-calendar-picker-indicator {*/
+/*    filter: invert(1); !* 흰색 아이콘 *!*/
+/*}*/
+.error-content table {
+    display: none;
+}
+.error-content table.on {
+    display: table;
+}
+.table-box {
+    background-color: #2c2c2c;
+    width: 100%;
+    height: calc(100% - 110px);
+    box-sizing: border-box;
+    overflow: auto;
+    padding: 5px;
+}
+.table-box > div:nth-child(1) {
+    width: 100%;
+    height: calc(100% - 40px);
+    overflow: auto;
+    box-sizing: border-box;
+    border: 1px solid #424242;
+}
+
+.table-box table thead {
+    position : sticky;
+    top: 0;
+}
+
+.search-box {
+    display: flex;
+    align-items: center;
+    padding: 10px;
+    box-sizing: border-box;
+    gap: 5px;
+    height: 50px;
+    background-color: #2c2c2c;
+    color: white;
+    border-bottom: 1px solid #424242;
+}
+#region_cd {
+    width: 150px;
+}
+
+#int_no {
+    width: 220px;
+}
+
+.table-content table tr:hover:not(.empty-box) {
+    background-color: #596f8f !important;
+}
+
+.table-content table tr:hover:not(.empty-box) td {
+    color: white;
+}
+
+.search-box input,
+.search-box select {
+    background-color: #2a2a2a;
+    padding: 5px;
+    box-sizing: border-box;
+    border: 1px solid #575757;
+    color: #e9e9e9;
+    height: 100%;
+    color-scheme : dark;
+}
+
+.mapToggle {
+    z-index: 100;
+    height: 24px;
+    width: 430px;
+    position: absolute;
+    left: 40px;
+    top: 3px;
+}
+
+.roadview2 {
+    z-index: 20;
+    height: 24px;
+    width: 70px;
+    position: absolute;
+    left: 20px;
+    top: 3px;
+}
+
+.arrow_ims {
+    position: absolute;
+    left: 0;
+    right: 0;
+    text-align: center;
+    width: 410px;
+    margin-top: 5px;
+}
+
+.arrow_sig {
+    text-align: center;
+    width: 400px;
+    position: relative;
+    /* top:61px; */
+    overflow: hidden;
+}
+
+.pop_box {
+    border: 2px solid #4b9fbf;
+    padding: 1px;
+    background: #ffffff;
+    width: 410px;
+    over-flow: hidden;
+    position: relative;
+    padding-bottom: 5px;
+    z-index: 20
+}
+
+.pop_box2 {
+    padding: 1px;
+    background: #3a4049;
+    width: 350px;
+    height: 250px;
+    over-flow: hidden;
+    position: relative;
+}
+
+/* .sigPop_box {
+	border: 2px solid #4b9fbf;
+	padding: 5px;
+	background: #ffffff;
+	width: 250px !important;
+} */
+
+.sigPop_box {
+    border: 2px solid #4b9fbf;
+    padding: 5px;
+    background: #ffffff;
+    width: 400px !important;
+    height: 360px !important;
+    position: relative;
+}
+
+.pop_ims_image {
+    margin: 3px;
+    width: 310px;
+    height: 230px;
+}
+
+caption {
+    display: none;
+}
+
+.pop_table {
+    width: 100% !important
+}
+
+.pop_table tr td {
+    height: 30px;
+    font-size: 13px !important
+}
+
+.pop_table thead tr th {
+    font-size: 14px;
+}
+
+.pop_table tr td span {
+    font-weight: bold;
+    text-align: center;
+
+
+}
+
+.pop_table tr th {
+    height: 26px;
+    border-bottom: 2px solid #4b9fbf;
+    padding: 5px;
+    text-align: left;
+    color: #2c2c2c;
+    font-size: 13px !important
+
+}
+
+.pop_table tbody tr td {
+    height: 20px;
+    border-bottom: 2px solid #4b9fbf;
+    padding: 5px;
+    text-align: left;
+    color: #2c2c2c;
+    font-size: 14px;
+
+}
+
+.pop_table tfoot tr td {
+    padding: 5px;
+    color: #2c2c2c;
+    height: 20px;
+    text-align: left
+}
+
+/* .pop_table tfoot tr td span{margin-left:5px;} */
+.pop_table tr th img {
+    margin-right: 5px;
+}
+
+/*동영상 추가 css */
+
+.pop_table2 {
+    width: 330px;
+}
+
+.pop_table2 tr td {
+    background: #3a4049;
+    padding-top: 2px;
+    width: 300px;
+    height: 240px;
+    text-align: center;
+    solid: #d3d3d3;
+}
+
+.pop_table2 tr td span {
+    font-weight: bold;
+    text-align: center;
+    padding-left: 5px;
+}
+
+.pop_table2 tr th {
+    height: 16px;
+    background: #3a4049;
+
+    padding-top: 4px;
+    text-align: left;
+    color: #ffffff;
+    padding-left: 10px;
+}
+
+.pop_table2 tr th img {
+    margin-right: 10px;
+
+}
+
+.pop_table2 tr th span {
+    float: right;
+    margin-right: -5px;
+}
+
+.pop_table2 tfoot tr td {
+    height: 2px;
+    background: #3a4049;
+}
+
+.tooltip_close {
+    height: 21px;
+    position: absolute;
+    top: -2px;
+    right: 0px
+
+}
+
+#menu_wrap .option button {
+    margin-left: 5px;
+}
+
+html input[type=button], input[type=email], input[type=password], input[type=reset], input[type=search], input[type=submit], input[type=tel], input[type=text] {
+    -webkit-appearance: none;
+    border-radius: 0
+}
+
+body, button, input, select, td, textarea, th {
+    /*line-height: 1.6;*/
+    /*height: 20px;*/
+}
+
+#menu_wrap {
+    display: none;
+    position: absolute;
+    top: 30px;
+    left: 30px;
+    bottom: 0;
+    width: 260px;
+    /*border: 1px solid #4b9fbf;*/
+    margin: 10px 0 30px 10px;
+    padding: 5px;
+    z-index: 1000000;
+    font-size: 12px;
+    height: 550px;
+    min-height: 150px;
+    overflow: auto;
+    border-radius: 10px;
+    /*filter: alpha(Opacity:100, Finishopacity: 85,  style: 2);*/
+    border: 0px;
+    background: rgba(255, 255, 255, 0.7);
+}
+.option input {
+    line-height: 1.6;
+    height: 20px;
+}
+.option button{
+    margin-left: 5px;
+    border: 1px solid #1e65c5;
+    background-color: #1e65c5;
+    color: white;
+    font-weight: bold;
+    cursor: pointer;
+    border-radius: 5px;
+    padding: 4px;
+}
+#menu_wrap .option{
+    text-align: center;
+}
+
+.map_wrap, .map_wrap * {
+    margin: 0;
+    padding: 0;
+    font-family: "Malgun Gothic", dotum, "돋움", sans-serif;
+}
+
+#menu_wrap .option p {
+    margin: 10px 0;
+}
+
+#placesList .item {
+    border-bottom: 1px solid #888;
+    overflow: hidden;
+    consor: pointer;
+    min-height: 65px;
+}
+
+#placesList li {
+    list-style: none;
+}
+
+#placesList .item .marker_1 {
+    background-position: 0 -10px;
+}
+
+#placesList .item .markerbg {
+    float: left;
+    position: absolute;
+    width: 36px;
+    height: 55px;
+    margin: 10px 0 0 10px;
+    background: url(https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png) no-repeat;
+}
+
+#placesList .item {
+    position: relative;
+    border-bottom: 1px solid #888;
+    overflow: hidden;
+    cursor: pointer;
+    min-height: 65px;
+}
+
+#placesList .item .info {
+    padding: 10px 0 10px 25px;
+}
+
+#placesList .item h5, #placesList .item .info {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    top: 0;
+    left: 0;
+}
+
+#placesList .item span {
+    display: block;
+    margin-top: 4px;
+}
+
+#placesList .info .gray {
+    color: #8a8a8a;
+}
+
+#placesList .info .jibun {
+    padding-left: 26px;
+    background: url(http://i1.daumcdn.net/localimg/localimages/07/mapapidoc/places_jibun.png) no-repeat;
+}
+
+#placesList .info .tel {
+    color: #009900;
+}
+
+dl, li, menu, ol, ul {
+    list-style: none;
+}
+
+#menu_wrap hr {
+    display: block;
+    height: 1px;
+    border: 0;
+    border-top: 2px solid #5F5F5F;
+    margin: 3px 0;
+}
+
+.bg_white {
+    background: #fff;
+}
+
+h5 {
+    font-size: 14px;
+    font-weight: bold;
+}
+
+#placesList .item .marker_2 {
+    background-position: 0 -46px;
+}
+
+#placesList .item .marker_3 {
+    background-position: 0 -92px;
+}
+
+#placesList .item .marker_4 {
+    background-position: 0 -138px;
+}
+
+#placesList .item .marker_5 {
+    background-position: 0 -185px;
+}
+
+#pagination {
+    margin: 10px auto;
+    text-align: center;
+}
+
+#pagination .on {
+    font-weight: bold;
+    cusor: default;
+    color: #777;
+}
+
+#pagination a {
+    display: inline-block;
+    margin-right: 10px;
+    text-decoration: none;
+}
+
+.tooltip_close2 {
+    height: 21px;
+    position: absolute;
+    top: 10px;
+    right: 10px
+}
+
+.searchBtn {
+    border: 1px solid #5f5f5f;
+    font-size: 12px;
+    padding: 0 2px;
+    background: #ccc;
+    gap: 5px;
+}
+
+.footerSpan {
+    color: rgb(255, 255, 255);
+    font-size: 18px;
+    font-weight: bold;
+}
+
+
+.dot {
+    overflow: hidden;
+    float: left;
+    width: 12px;
+    height: 12px;
+    background: url('http://i1.daumcdn.net/localimg/localimages/07/mapapidoc/mini_circle.png');
+}
+
+.dotOverlay {
+    position: relative;
+    bottom: 10px;
+    border-radius: 6px;
+    border: 1px solid #ccc;
+    border-bottom: 2px solid #ddd;
+    float: left;
+    font-size: 12px;
+    padding: 5px;
+    background: #fff;
+}
+
+.dotOverlay:nth-of-type(n) {
+    border: 0;
+    box-shadow: 0px 1px 2px #888;
+}
+
+.number {
+    font-weight: bold;
+    color: #ee6152;
+}
+
+.dotOverlay:after {
+    content: '';
+    position: absolute;
+    margin-left: -6px;
+    left: 50%;
+    bottom: -8px;
+    width: 11px;
+    height: 8px;
+    background: url('http://i1.daumcdn.net/localimg/localimages/07/mapapidoc/vertex_white_small.png')
+}
+
+.distanceInfo {
+    position: relative;
+    top: 5px;
+    left: 5px;
+    list-style: none;
+    margin: 0;
+}
+
+.distanceInfo .label {
+    display: inline-block;
+    width: 50px;
+}
+
+.distanceInfo:after {
+    content: none;
+}
+
+.info {
+    position: relative;
+    top: 5px;
+    left: 5px;
+    border-radius: 6px;
+    border: 1px solid #ccc;
+    border-bottom: 2px solid #ddd;
+    font-size: 12px;
+    padding: 5px;
+    background: #fff;
+    list-style: none;
+    margin: 0;
+}
+
+.info:nth-of-type(n) {
+    border: 0;
+    box-shadow: 0px 1px 2px #888;
+}
+
+.info .label {
+    display: inline-block;
+    width: 50px;
+}
+
+.areaNumber {
+    font-weight: bold;
+    color: #00a0e9;
+}
+
+.textInfo {
+    position: relative;
+    top: 5px;
+    left: 5px;
+    border-radius: 6px;
+    border: 1px solid #ccc;
+    border-bottom: 2px solid #ddd;
+    font-weight: bold;
+    color: #000000;
+    font-size: 12px;
+    padding: 5px;
+    background: #cccccc;
+    list-style: none;
+    margin: 0;
+}
+
+.textInfo:nth-of-type(n) {
+    border: 0;
+    box-shadow: 0px 1px 2px #888;
+}
+
+
+div.stateBox {
+    visibility: hidden;
+    z-index: 1000;
+    position: absolute;
+    bottom: 65px;
+    right: 5px;
+    z-index: 3;
+    background: url('images/bg_traffic02.png') no-repeat left 0;
+    width: 281px;
+    height: 50px;
+    padding: 5px 9px 0 10px;
+    overflow: visible;
+}
+
+ul.stateInfo {
+    overflow: hidden;
+    float: left;
+    width: 100%;
+    position: relative;
+    margin-top: -3px;
+}
+
+ul.stateInfo li {
+    color: #fff;
+    float: left;
+    font-size: 11px;
+}
+
+ul.stateInfo li.fir {
+    width: 265px;
+}
+
+ul.stateInfo li.re {
+    padding-right: 10px;
+    margin-right: 6px;
+    background: url('images/bg_traffic_lpop02.gif') no-repeat right 0;
+}
+
+ul.stateInfo li.re img {
+    margin-right: 1px;
+}
+
+ul.stateInfo li a {
+    font-size: 11px;
+    color: #7884a7;
+}
+
+ul.stateLine {
+    overflow: hidden;
+    float: left;
+    width: 100%;
+    background: url('images/bg_traffic_details.gif') no-repeat left 0;
+    margin: 5px 0 5px 0;
+}
+
+ul.stateLine li {
+    font-size: 11px;
+    float: left;
+    margin-top: 9px;
+}
+
+ul.stateLine li a {
+    font-size: 11px;
+    margin-right: 57px;
+}
+
+ul.stateLine li.m01 a {
+    color: #66a726;
+}
+
+ul.stateLine li.m02 a {
+    color: #ffba00;
+}
+
+ul.stateLine li.m03 a {
+    color: #d21400;
+}
+
+ul.stateLine li.m04 a {
+    color: #cecece;
+    margin-right: 0px;
+}
+
+ul.stateComment {
+    clear: both;
+}
+
+ul.stateComment li {
+    color: #667091;
+    font-size: 11px;
+    word-spacing: -1px;
+    letter-spacing: -1px;
+    background: url('images/ico_sblue.png') no-repeat 0 4px;
+    padding-left: 8px;
+    line-height: 16px;
+    overflow: visible;
+}
+
+div.trafficPop {
+    padding-bottom: 10px;
+    z-index: 500;
+    overflow: visible;
+    position: absolute;
+    bottom: 32px;
+    z-index: 3;
+}
+
+div.trafficGoods {
+    right: 220px;
+}
+
+div.trafficNormal {
+    right: 130px;
+}
+
+div.trafficBad {
+    right: 50px;
+}
+
+div.trafficPop p {
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    margin-left: -8px;
+}
+
+.trafficPop {
+    border: none;
+}
+
+.trafficPop ul {
+    padding: 8px 10px;
+    border: 1px solid #111114;
+}
+
+.trafficPop li {
+    margin: 0;
+    padding: 0 0 0 58px;
+    height: 15px;
+    white-space: nowrap;
+    position: relative;
+    width: 62px;
+}
+
+.trafficPop strong {
+    left: 0;
+    font-weight: normal;
+    position: absolute;
+    color: #000;
+    float: left;
+}
+
+.trafficPop span {
+    font-weight: normal;
+    color: #fff;
+}
+
+div.trafficGoods ul {
+    background: #4c9800;
+    border: 1px solid #111114;
+}
+
+div.trafficNormal ul {
+    background: #fab700;
+    border: 1px solid #111114;
+}
+
+div.trafficBad ul {
+    background: #c41301;
+    border: 1px solid #111114;
+}
+
+/*
+
+div.popState dl.construction {margin-right:6px; background:#fff;}
+div.popState dl.construction {min-width:219px;_width:219px; background:url('images/btn_repair.png') no-repeat 1px 30px;}
+div.popState dl.construction dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.construction dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.construction dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px; text-overflow:ellipsis; overflow:hidden;}
+div.popState dl.accident {margin-right:6px; background:#fff;}
+div.popState dl.accident {min-width:219px;_width:219px; background:url('images/btn_accident.png') no-repeat 1px 30px;}
+div.popState dl.accident dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.accident dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.accident dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px; text-overflow:ellipsis; overflow:hidden;}
+div.popState dl.event {margin-right:6px; background:#fff;}
+div.popState dl.event {min-width:219px;_width:219px; background:url('images/btn_event.png') no-repeat 1px 30px;}
+div.popState dl.event dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.event dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.event dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px; text-overflow:ellipsis; overflow:hidden;}
+div.popState dl.trouble {margin-right:6px; background:#fff;}
+div.popState dl.trouble {min-width:219px;_width:219px; background:url('images/btn_trouble.png') no-repeat 1px 30px;}
+div.popState dl.trouble dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.trouble dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.trouble dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px; text-overflow:ellipsis; overflow:hidden;}
+div.popState dl.alert {margin-right:6px; background:#fff;}
+div.popState dl.alert {min-width:219px;_width:219px; background:url('images/btn_alert.gif') no-repeat 1px 30px;}
+div.popState dl.alert dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.alert dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.alert dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px; text-overflow:ellipsis; overflow:hidden;}
+div.popState dl.death {margin-right:6px; background:#fff;}
+div.popState dl.death {min-width:219px;_width:219px; background:url('images/btn_deathaccident.png') no-repeat 1px 30px;}
+div.popState dl.death dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.death dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.death dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px;}
+div.popState dl.death dd strong.rred {color:#bc1200; text-align:right;}
+div.popState dl.death dd strong.lblue {color:#607cd4;}
+div.popState dl.death dd strong.rblue {color:#607cd4; text-align:right;}
+div.popState dl.jaywork {margin-right:6px; background:#fff;}
+div.popState dl.jaywork {min-width:219px;_width:219px; background:url('images/btn_jayworking.png') no-repeat 1px 30px;}
+div.popState dl.jaywork dt {position:relative;font-size:12px; font-weight:bold; color:#607cd4; padding-bottom:7px;  margin-bottom:7px;border-bottom:1px solid #e5e5e5;}
+div.popState dl.jaywork dd {white-space:nowrap; line-height:12px; color:#505050; font-size:11px; margin-bottom:4px; padding-left:40px;}
+div.popState dl.jaywork dd.nm {font-weight:bold; margin-bottom:5px; padding-top:4px;}
+div.popState dl.jaywork dd strong.rred {color:#bc1200; text-align:right;}
+div.popState dl.jaywork dd strong.lblue {color:#607cd4;}
+div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
+*/
+.headMenu{width:100%;height:61px;background-color : #000000; display: flex; align-items: center; }
+.headMenu h1{float:left;width:329px; margin-top: -5px; margin-left: 5px;}
+.headMenu ul li {
+    float: left
+}
+
+.headMenu span {
+    position: absolute;
+    right: 20px;
+    line-height: 61px
+}
+
+.bumImage {
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+    background-color: #ffffff;
+    border: 1px solid #bbbbbb;
+    z-index: 20;
+    overflow: hidden
+}
+
+.iframeTreeList {
+    float: left;
+    width: 280px;
+    height: 100%;
+    margin-left: 5px;
+    overflow: hidden;
+    border: 0;
+    z-index: 50
+}
+
+.iframeBottomList {
+    margin-left: 2px;
+    width: calc(100% - 287px);
+    height: 222px;
+    overflow: hidden;
+    border: 0;
+    border-bottom: 1px solid #595959;
+    border-left: 1px solid #595959;
+    z-index: 50;
+    box-sizing: border-box;
+}
+
+.leftMenu {
+    height: calc(100% - 8px);
+    background-color: #fff;
+    border: 1px solid #595959;
+}
+
+.leftMenu h1 {
+    padding: 10px;
+    border-bottom: 1px solid #d8d8d8
+}
+
+.leftMenu .tabs {
+    height: 30px;
+    border-bottom: 2px solid #595959;
+    background-color: #000000;
+}
+
+.leftMenu .tabs li {
+    float: left;
+    height: 100%;
+    padding: 0 10px;
+    border-right: 1px solid #595959;
+    color: #b2b2b2;
+    cursor: pointer;
+    border-bottom: 2px solid #595959;
+}
+.leftMenu .tabs li.on {
+    border-bottom: 2px solid #3396ff;
+}
+.leftMenu .tabs li > div img{
+    margin-bottom: 3px;
+}
+.leftMenu .tabs li > div {
+    display: flex;
+    gap: 5px;
+    align-items: center;
+    height: 100%;
+    font-weight: bold;
+}
+/* 트리리스트 */
+
+/* .leftMenu .iframeTreeList{width:95%;height:calc(100% - 45px);overflow:auto;padding:5px} */
+.leftMenu .historyBox {
+    width: 100%;
+    height: 175px;
+    border-bottom: 1px solid #595959;
+    background-color: #2c2c2c;
+    flex-direction: column;
+    display: none;
+    gap: 5px;
+}
+.leftMenu .evp-cur {
+    width: 100%;
+    height: 160px;
+    display: none;
+    background-color: #212121;
+    border-bottom: 1px solid #595959;
+}
+#evp-cur-list {
+    width: 100%;
+    height: calc(100% - 25px);
+    overflow: auto;
+}
+#evp-cur-list table {
+    width: 100%;
+    border-collapse: separate;
+    border-spacing: 0;
+}
+#evp-cur-list table thead{
+    position: sticky;
+    top: 0;
+    left: 0;
+}
+#historyList table tbody td,
+#evp-cur-list table tbody td {
+    font-size: 12px;
+    text-align: center;
+    color: #b2b2b2;
+    cursor: pointer;
+    height: 22px;
+    line-height: 22px;
+    padding: 5px;
+    box-sizing: border-box;
+    word-break: break-all;
+}
+
+#historyList table tbody tr:nth-child(even) td,
+#evp-cur-list table tbody tr:nth-child(even) td {
+    background-color: #3c3c3c;
+}
+#historyList table tbody tr:nth-child(odd) td,
+#evp-cur-list table tbody tr:nth-child(odd) td {
+    background-color:#292828;
+}
+.leftMenu .title {
+    height: 25px;
+    /*background-color: #3c3c3c;*/
+    background-color: #0065d1;
+    /*color: #b2b2b2;*/
+    color: white;
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    padding-left: 10px;
+    border-bottom: 1px solid #595959;
+    font-weight: bold;
+}
+.leftMenu .historyBox > div:nth-child(2) {
+    display: flex;
+    flex-direction: column;
+    /*align-items: center;*/
+    gap: 5px;
+    padding: 0 10px;
+}
+select#region {
+    width: 223px;
+    height: 29px;
+    box-sizing: border-box;
+    padding : 0 10px;
+}
+.leftMenu .historyBox > div > div > div:nth-child(1){
+    color: #b2b2b2;
+    width: 27px;
+    text-align: right;
+}
+
+.leftMenu .historyBox > div > div {
+    display: flex;
+    gap: 5px;
+    align-items: center;
+}
+.leftMenu .searchBox {
+    width: 100%;
+    height: 40px;
+    border-bottom: 1px solid #595959;
+    display: flex;
+    gap: 12px;
+    /*justify-content: center;*/
+    align-items: center;
+    background-color: black;
+}
+
+.leftMenu .searchBox .search-button {
+    padding: 4px;
+    border: 1px solid #eeeeee;
+    background-color: #3396ff;
+    color: white;
+    width: 50px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 5px;
+    font-weight: bold;
+    cursor: pointer;
+}
+
+.leftMenu .searchBox .search-button:hover{
+    filter: brightness(1.1);
+}
+
+.leftMenu .searchBox input {
+    width: 228px;
+    padding: 5px;
+}
+
+.leftMenu #intTree {
+    /*height: calc(100% - 70px);*/
+    /*overflow: auto;*/
+    /*padding: 5px*/
+    height : calc(100% - 32px);
+}
+
+.leftMenu #historyList {
+    display: none;
+    height: calc(100% - 407px);
+    overflow: auto;
+    background-color: #212121;
+    box-sizing: border-box;
+    width: 100%;
+    border-bottom: 1px solid #595959;
+}
+
+.leftMenu #evp-cur-list table thead th,
+.leftMenu #evp-cur-list table tbody,
+.leftMenu #evp-cur-list table tr td,
+.leftMenu #historyList table thead th,
+.leftMenu #historyList table tbody,
+.leftMenu #historyList table tr td{
+    border-bottom: 1px solid #595959;
+}
+
+.leftMenu #evp-cur-list table tbody tr.on td,
+.leftMenu #historyList table tbody tr.on td{
+    background-color: #3396FF;
+    color : #eeeeee;
+}
+
+.leftMenu #historyList table {
+    width: 100%;
+}
+.leftMenu #historyList thead th,
+.leftMenu #evp-cur-list thead th {
+    background-color: #212121;
+    color: #b2b2b2;
+    line-height: 1.6;
+    height: 20px;
+}
+
+.leftMenu #evp-cur-list .empty-evp {
+    height: 112px;
+    background-color: #2c2c2c;
+    text-align: center;
+    color: #b2b2b2;
+    cursor: default;
+}
+.leftMenu #historyList .empty-history {
+    height: calc(100% - 21.3px);
+    background-color: #2c2c2c;
+    text-align: center;
+    color: #b2b2b2;
+    cursor: default;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.leftMenu #historyList thead th:not(:last-child),
+.leftMenu #evp-cur-list thead th:not(:last-child),
+.leftMenu #historyList tbody td:not(:last-child),
+.leftMenu #evp-cur-list tbody td:not(:last-child) {
+    border-right: 1px solid #595959;
+}
+
+.leftMenu .intSearchMenu {
+    height: 70px;
+    padding: 10px 0 0 20px;
+    border-bottom: 1px solid #595959
+}
+
+.leftMenu .intSearchMenu label {
+    font-weight: bold;
+    color: #2d2d2d;
+    display: inline-block;
+    width: 55px;
+    padding-left: 15px;
+    background: url(images/bul_blue.gif) no-repeat 0 center;
+}
+
+.leftMenu .intSearchMenu #sidoSearch {
+    width: 105px;
+    height: 25px
+}
+
+.leftMenu .intSearchMenu .intSearchDiv {
+    font-size: 100%;
+    font-family: inherit;
+    vertical-align: baseline;
+    padding-top: 10px
+}
+
+.leftMenu .intSearchMenu .intSearchBox {
+    width: 100px;
+    height: 21px
+}
+
+.leftMenu .intSearchMenu .intSearchBtn {
+    color: #fff;
+    /*border-color: #1a55a1 #114e8f #074577 #104d8d;*/
+    /*background-color: #1e65c4;*/
+    background: -webkit-gradient(linear, left top, left bottom, from(#1e65c4), to(#1e5bad));
+    padding: 5px 20px 3px 14px;
+    border: 1px solid #cecdcd;
+    display: inline-block;
+    cursor: pointer;
+    font-weight: bold;
+    border-radius: 2px !important;
+    vertical-align: middle;
+    font-size: 11px;
+    line-height: normal;
+}
+
+.leftMenu .intSearchMenu .intGroupSearchDiv {
+    font-size: 100%;
+    font-family: inherit;
+    vertical-align: baseline;
+    padding-top: 10px
+}
+
+.leftMenu .intSearchMenu .intGroupSearchDiv .intGroupSearchBox {
+    width: 105px;
+    height: 25px
+}
+
+.leftMenu .intList {
+    height: 82%
+}
+
+.leftMenu .intList .intListMenu {
+    border-top: 2px solid #595959;
+    border-bottom: 1px solid #595959;
+    font-weight: bold;
+    color: #2d2d2d;
+    height: 30px;
+    vertical-align: middle;
+    padding: 10px 10px 0px 10px;
+    background: #fafafa;
+}
+
+.leftMenu .intList .intListMenu .intListMenu1 {
+    display: inline-block;
+    width: 130px
+}
+
+.leftMenu .intList .intListMenu .intListMenu2 {
+    display: inline-block;
+    width: 70px
+}
+
+.leftMenu .intList .intListMenu .intListMenu3 {
+    display: inline-block;
+    width: 70px
+}
+
+.leftMenu .intList .intListCon {
+    padding: 10px;
+    height: 100%;
+    overflow-y: auto
+}
+
+.leftMenu .intList .result_title {
+    border-bottom: 1px solid #BBBBBB;
+    padding: 15px 10px;
+}
+
+.leftMenu .intList .result_title .regionTitle {
+    background: url(/images/bul_arrow.gif) no-repeat scroll 0 center rgba(0, 0, 0, 0);
+    padding-left: 10px;
+    float: right
+}
+
+.leftMenu .intList .result_title .clear {
+    clear: both;
+    font-size: 0;
+    height: 0
+}
+
+.leftMenu .intList .intListCon .intListUl li:first-child {
+    padding-top: 0
+}
+
+.leftMenu .intList .intListCon .intListUl li {
+    line-height: 140%;
+    border-bottom: 1px solid #bababa;
+    padding: 10px 0;
+    cursor: pointer;
+    display: block
+}
+
+.leftMenu .intList .intListCon .intListUl li:hover {
+    color: #1e5bae;
+}
+
+.sigInfoBox {
+    z-index: 1000;
+    position: absolute;
+    bottom: 5px;
+    right: 5px;
+    overflow: hidden;
+}
+
+.wideMap {
+    position: absolute;
+    /*top: 40%;*/
+    top : 0;
+    left: 0;
+    /*top: 10px;*/
+    /*left: -8px;*/
+    cursor: pointer;
+    z-index: 10;
+    rotate: 180deg;
+    background-image: url(/images/chevron-right.png);
+    width: 20px;
+    height: 30px;
+    background-color: #2c2c2c;
+    border-top: 1px solid #595959;
+    border-left: 1px solid #595959;
+    border-bottom: 1px solid #595959;
+    background-position: center;
+    border-radius: 5px 0 0 5px;
+}
+.wideMap.on {
+    rotate: 0deg;
+    border-left: 0;
+    border-right: 1px solid #595959;
+    border-radius: 0 5px 5px 0;
+}
+
+.upDownMap {
+    position: absolute;
+    right: 2px;
+    bottom: 0;
+    cursor: pointer;
+    z-index: 10;
+    rotate: 0deg;
+    background-image: url('/images/chevron-down.png');
+    width: 30px;
+    height: 20px;
+    background-color: #2c2c2c;
+    border-top: 1px solid #595959;
+    border-left: 1px solid #595959;
+    border-right: 1px solid #595959;
+    background-position: center;
+    border-radius: 5px 5px 0 0;
+}
+
+.upDownMap.on {
+    rotate: 180deg;
+    border-bottom: 1px solid #595959;
+    border-top: 0;
+    border-radius: 0 0 5px 5px;
+}
+
+
+.bottomMenu {
+    height: 99%;
+    overflow: hidden;
+    margin: 0
+}
+
+.bottomMenu .networkType a:nth-child(2) {
+    position: relative;
+    left: -4px
+}
+
+.bottomMenu .bottomHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.bottomMenu .bottomHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.bottomMenu .bottomHead table {
+    width: 100%;
+    border-collapse: separate;
+    border-spacing: 0px;
+}
+
+.bottomMenu .bottomHead table thead,
+.bottomMenu .bottomBody table thead {
+    background-color: #595959;
+    position: sticky;
+    top: 0;
+    right: 0;
+}
+
+.bottomMenu .bottomBody table th,
+.bottomMenu .bottomHead table th {
+    height: 20px;
+    padding: 5px;
+    text-align: center;
+    font-weight: bold;
+    color: #cccccc;
+    background-color: #000;
+    line-height: 20px;
+    border-bottom: 1px solid #595959;
+    border-right: 1px solid #595959;
+}
+
+.bottomMenu .bottomBody {
+    max-height: 100%;
+    /*overflow-y: scroll*/
+    overflow: auto;
+}
+
+.bottomMenu .bottomBody table {
+    width: 100%;
+    min-width: 1500px;
+}
+
+.bottomMenu .bottomBody table tbody {
+    background-color: #fff
+}
+
+.bottomMenu .bottomBody table td {
+    padding: 5px;
+    text-align: center;
+    /*color: #515151;*/
+    /*border-bottom: 1px solid #bababa;*/
+    /*border-right: 1px solid #bababa;*/
+    color: #cccccc;
+    border-bottom: 1px solid #424242;
+    border-right: 1px solid #424242;
+}
+
+.bottomMenu .bottomBody table a {
+    /*display: inline-block;*/
+    width: 100%;
+    height: 100%;
+    color: #b2b2b2;
+}
+
+.leftPopMenu {
+    display: none;
+    position: absolute;
+    width: 225px;
+    height: 802px;
+    float: left;
+    background-color: #fff;
+    margin: 5px
+}
+
+.leftPopMenu .intResrvPlan {
+    height: 300px;
+    border: 1px solid #595959
+}
+
+.leftPopMenu .intResrvPlan p, .leftPopMenu .holydayPlan p, .bottomPopMenu P {
+    font-weight: bold;
+    color: #2d2d2d;
+    display: inline-block;
+    width: 110px;
+    padding-left: 15px;
+    margin: 10px;
+    background: url(/images/bul_table.gif) no-repeat 5px
+}
+
+.leftPopMenu .intResrvPlan .intResrvPlanCon {
+    width: auto;
+    height: 257px;
+    margin-left: 2px;
+    overflow: hidden
+}
+
+.leftPopMenu .intResrvPlan .intResrvPlanCon .intResrvPlanHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.leftPopMenu .intResrvPlan .intResrvPlanCon .intResrvPlanHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.leftPopMenu .intResrvPlan .intResrvPlanCon .intResrvPlanBody {
+    max-height: 215px;
+    overflow-y: scroll
+}
+
+.leftPopMenu .intResrvPlan table, .holydayPlan table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.leftPopMenu .intResrvPlan table thead, .holydayPlan table thead {
+    background-color: #f4f4f4
+}
+
+.leftPopMenu .intResrvPlan table th, .holydayPlan table th {
+    font-weight: bold;
+    color: #2d2d2d
+}
+
+.leftPopMenu .intResrvPlan table td, .holydayPlan table td {
+    padding: 3px
+}
+
+.leftPopMenu .holydayPlan table th {
+    padding: 6px
+}
+
+.leftPopMenu .intResrvPlan table th, .intResrvPlan table td, .holydayPlan table th, .holydayPlan table td {
+    text-align: center;
+    border: 1px solid #bababa
+}
+
+.intResrvPlan table tr:nth-child(1) td {
+    text-align: center;
+    border-top: 0
+}
+
+.holydayPlan table tr:nth-child(1) td {
+    text-align: center;
+    border-top: 0
+}
+
+.leftPopMenu .holydayPlan {
+    height: 488px;
+    margin-top: 5px;
+    border: 1px solid #595959
+}
+
+.leftPopMenu .holydayPlan .holydayPlanCon {
+    width: auto;
+    height: 446px;
+    margin-left: 2px;
+    overflow: hidden
+}
+
+.leftPopMenu .holydayPlan .holydayPlanCon .holydayPlanHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.leftPopMenu .holydayPlan .holydayPlanCon .holydayPlanHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.leftPopMenu .holydayPlan .holydayPlanCon .holydayPlanBody {
+    max-height: 410px;
+    overflow-y: scroll
+}
+
+.bottomPopMenu {
+    position: absolute;
+    top: 295px;
+    height: 538px;
+    border: 1px solid #595959;
+    margin-left: 5px;
+    margin-top: 2px;
+    width: 908px
+}
+
+.bottomPopMenu .dayPlan {
+    display: none;
+    height: 380px
+}
+
+.bottomPopMenu .dayPlan form {
+    display: inline-block
+}
+
+.bottomPopMenu .dayPlan form input[type="button"] {
+    background: #96a2b2 !important;
+    color: #fff;
+    font-weight: bold;
+    width: 100px;
+    text-align: center;
+    padding: 0px;
+    height: 25px;
+    line-height: 25px;
+    cursor: pointer
+}
+
+.bottomPopMenu .dayPlan span {
+    padding-left: 400px
+}
+
+.bottomPopMenu .dayPlan span label {
+    font-weight: 600
+}
+
+.bottomPopMenu .dayPlan span select {
+    width: 40px
+}
+
+.bottomPopMenu .dayPlanCon {
+    width: auto;
+    height: 89%;
+    overflow: hidden;
+    margin: 0 2px;
+    border-bottom: 1px solid #bababa
+}
+
+.bottomPopMenu .dayPlanCon .dayPlanHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.bottomPopMenu .dayPlanCon .dayPlanHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.bottomPopMenu .dayPlanCon .dayPlanBody {
+    max-height: 255px;
+    overflow-y: scroll
+}
+
+.bottomPopMenu .dayPlanCon table {
+    table-layout: fixed
+}
+
+.bottomPopMenu .dayPlanCon table th, .bottomPopMenu .weekPlanCon table th {
+    padding: 6px;
+    text-align: center;
+    border: 1px solid #bababa
+}
+
+.bottomPopMenu .weekPlanCon table td {
+    padding: 6px;
+    text-align: center;
+    border-left: 1px solid #bababa;
+    border-top: 1px solid #bababa
+}
+
+.bottomPopMenu .dayPlanCon table td {
+    padding: 5px 0;
+    text-align: center;
+    border: 1px solid #bababa
+}
+
+.bottomPopMenu .dayPlanCon table tr:nth-child(1) td {
+    border-top: 0
+}
+
+.bottomPopMenu .dayPlanCon table, .bottomPopMenu .weekPlanCon table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.bottomPopMenu .dayPlanCon table thead, .bottomPopMenu .weekPlanCon table thead {
+    width: 100%;
+    background-color: #f4f4f4
+}
+
+.bottomPopMenu .dayPlanCon table th, .bottomPopMenu .weekPlanCon table th {
+    font-weight: bold;
+    color: #2d2d2d;
+}
+
+.bottomPopMenu .dayPlanCon table td, .bottomPopMenu .weekPlanCon table td {
+    color: #515151
+}
+
+.bottomPopMenu .weekPlan {
+    display: none;
+    height: 110px
+}
+
+.bottomPopMenu .weekPlanCon {
+    width: auto;
+    overflow-y: auto;
+    margin: 2px 2px
+}
+
+/* 페이즈정보 팡벙 */
+.phaseInfoTop {
+    width: 910px;
+    margin: 5px 5px 0 5px
+}
+
+.phaseInfoTop .tabs {
+    height: 30px
+}
+
+.phaseInfoTop .tabs li {
+    float: left;
+}
+
+.phaseInfoTop .tabs li:nth-child(1) {
+    border-top: 1px solid #bbbbbb;
+    border-left: 1px solid #bbbbbb
+}
+
+.phaseInfoTop .tabs li:nth-child(2) {
+    border-top: 1px solid #bbbbbb
+}
+
+.phaseInfoTop .tabs li:nth-child(3) {
+    border-top: 1px solid #bbbbbb
+}
+
+.phaseInfoTop .tabs li:nth-child(4) {
+    border-top: 1px solid #bbbbbb
+}
+
+.phaseInfoTop .tabs .regionIntNm {
+    line-height: 30px;
+    width: 280px;
+    text-align: center;
+    padding-left: 55px;
+    font-size: 15px;
+    color: #b2b2b2;
+}
+
+#regionIntNm {
+    color: #b2b2b2;
+}
+
+.phaseInfoTop .tabs .simulation {
+    width: 140px;
+    font-size: 15px;
+    line-height: 30px;
+    color: red;
+    text-align: center;
+    padding-left: 50px
+}
+
+.phaseInfoTop .tabs > li:hover .subMenu {
+    display: block
+}
+
+.phaseInfoTop .tabs > li .subMenu {
+    display: none;
+    position: absolute;
+    top: 427px;
+    left: 5px;
+    width: 120px;
+    background-color: #122f64;
+    z-index: 10
+}
+
+.phaseInfoTop .tabs > li .subMenu li {
+    width: 121px;
+    margin-right: 40px;
+    font-size: 13px;
+    line-height: 34px;
+    text-align: center;
+}
+
+.phaseInfoTop .tabs > li .subMenu li:nth-child(1) {
+    border-bottom: 1px solid #bababa
+}
+
+.phaseInfoTop .tabs > li .subMenu li a {
+    display: inline-block;
+    color: #adbedf;
+}
+
+
+.bottomPopMenu .bottomRight {
+    width: auto;
+    height: 133px;
+    padding: 5px
+}
+
+.bottomPopMenu .bottomRight table {
+    width: 100%
+}
+
+.bottomPopMenu .bottomRight table thead {
+    width: 880px;
+    background-color: #f4f4f4
+}
+
+.bottomPopMenu .bottomRight table tbody {
+    padding-top: 10px
+}
+
+.bottomPopMenu .bottomRight table th, .bottomPopMenu .bottomRight table td {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa
+}
+
+.bottomPopMenu .bottomRight table th {
+    font-weight: bold;
+    color: #2d2d2d;
+}
+
+.bottomPopMenu .bottomRight table td {
+    color: #515151
+}
+
+.bottomPopMenu .phaseInfo {
+    width: auto;
+    height: 386px;
+    padding: 5px
+}
+
+.bottomPopMenu .phaseInfo .pMapNo select {
+    width: 40px
+}
+
+.bottomPopMenu .phaseInfoCon, .linkInfoCon {
+    width: auto;
+    height: 303px
+}
+
+.bottomPopMenu .phaseInfoCon table, .linkInfoCon table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.bottomPopMenu .phaseInfoCon table thead, .linkInfoCon table thead {
+    width: 100%;
+    background-color: #f4f4f4
+}
+
+.bottomPopMenu .phaseInfoCon table th, .bottomPopMenu .phaseInfoCon table td {
+    padding: 6px;
+    text-align: center;
+    border: 1px solid #bababa;
+    width: 9.1%
+}
+
+.bottomPopMenu .phaseInfoCon table #updatePhaseInfoList1 td {
+    border-bottom: 0
+}
+
+.bottomPopMenu .phaseInfoCon table th, .linkInfoCon table th {
+    font-weight: bold;
+    color: #2d2d2d;
+}
+
+.bottomPopMenu .phaseInfoCon table td {
+    color: #515151;
+    height: 90px
+}
+
+.bottomPopMenu .phaseInfoCon table .aProgress td {
+    height: auto
+}
+
+.bottomPopMenu .phaseInfoCon table .bProgress td {
+    height: auto
+}
+
+.bottomPopMenu .phaseInfoCon input {
+    width: 50px;
+    margin: 0 18px;
+    line-height: 15px;
+    text-align: center;
+    cursor: pointer;
+}
+
+.signalMapDiv {
+    height: 798px;
+    margin: 5px;
+    border: 1px solid #595959
+}
+
+.signalMapDiv .signalMapNo ul li {
+    padding: 10px;
+    font-family: '맑은고딕'
+}
+
+.signalMapDiv .signalMapNo ul li:nth-child(1) {
+    float: left;
+    width: 150px;
+    font-weight: 600;
+    margin-left: 450px
+}
+
+.signalMapDiv .signalMapNo ul li:nth-child(2) {
+    float: left;
+    width: 150px;
+    font-weight: 600
+}
+
+.signalMapDiv .signalMapNo ul li:nth-child(3) {
+    margin-left: 793px;
+    width: 150px;
+    font-weight: 600
+}
+
+/* .signalMapDiv .signalMapNo ul li:nth-child(3) select{text-align:right;margin:10px 20px;font-weight:600} */
+.signalMapDiv .signalMapCon {
+    height: 755px;
+    overflow-y: scroll
+}
+
+.signalMapDiv table {
+    width: 99%
+}
+
+.signalMapDiv table thead td {
+    line-height: 30px;
+    background-color: #aaaaaa;
+    color: #000000;
+    font-weight: 600;
+    border-top: 2px solid #000000;
+    border-bottom: 2px solid #000000
+}
+
+.signalMapDiv table tbody td:nth-child(1) {
+    background-color: #aaaaaa;
+    color: #000000;
+    font-weight: 600
+}
+
+.signalMapDiv table tbody tr:nth-child(32), .signalMapDiv table tbody tr:nth-child(64) td {
+    border-bottom: 2px solid #000000
+}
+
+.signalMapDiv table td {
+    width: 20px;
+    text-align: center;
+    border: 1px solid #000000;
+    font-family: '맑은고딕'
+}
+
+/* .signalMapDiv table tr td.eopClass{background-color:#FF5A5A !important;color:#ffffff !important} */
+.signalMapDiv table tr td.eopClass {
+    background-color: red;
+    color: #ffffff
+}
+
+.signalMapDiv table tr td.codeG1 {
+    background-color: green;
+    color: #ffffff
+}
+
+.signalMapDiv table tr td.codeR1 {
+    background-color: red;
+    color: #ffffff
+}
+
+.signalMapDiv table tr td.codeY1 {
+    background-color: yellow;
+    color: #000000
+}
+
+/* .linkInfoCon table th, .linkInfoCon table td{padding:6px;text-align:center;border:1px solid #bababa;width:10%}
+.linkInfoCon table td{color:#515151;height:40px}
+.linkInfoCon table tr:first-child td:nth-child(n+3){cursor:pointer}
+.linkInfoCon table tr:first-child td:nth-child(n+3):hover{color:#0076c8}
+.linkInfoCon table tr:nth-child(2) td:nth-child(n+2){cursor:pointer}
+.linkInfoCon table tr:nth-child(2) td:nth-child(n+2):hover{color:#0076c8} */
+
+.sigCustom {
+    position: relative;
+    bottom: 30px;
+}
+
+.sigCustom * {
+    display: inline-block;
+    vertical-align: top;
+}
+
+.sigCustom .left {
+    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;
+    display: inline-block;
+    height: 24px;
+    overflow: hidden;
+    vertical-align: top;
+    width: 7px;
+}
+
+.sigCustom .center {
+    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;
+    display: inline-block;
+    height: 24px;
+    font-size: 12px;
+    line-height: 24px;
+}
+
+.sigCustom .right {
+    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;
+    display: inline-block;
+    height: 24px;
+    overflow: hidden;
+    width: 6px;
+}
+
+/*.sigCustom1 {*/
+/*    position: relative;*/
+/*    bottom: 100px;*/
+/*}*/
+
+/*.sigCustom1 * {*/
+/*    display: inline-block;*/
+/*    vertical-align: top;*/
+/*}*/
+
+/*.sigCustom1 .left {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    vertical-align: top;*/
+/*    width: 7px;*/
+/*}*/
+
+/*.sigCustom1 .center {*/
+/*    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    font-size: 12px;*/
+/*    line-height: 24px;*/
+/*}*/
+
+/*.sigCustom1 .right {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    width: 6px;*/
+/*}*/
+
+/*.sigCustom2 {*/
+/*    position: relative;*/
+/*    bottom: 60px;*/
+/*}*/
+
+/*.sigCustom2 * {*/
+/*    display: inline-block;*/
+/*    vertical-align: top;*/
+/*}*/
+
+/*.sigCustom2 .left {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    vertical-align: top;*/
+/*    width: 7px;*/
+/*}*/
+
+/*.sigCustom2 .center {*/
+/*    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    font-size: 12px;*/
+/*    line-height: 24px;*/
+/*}*/
+
+/*.sigCustom2 .right {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    width: 6px;*/
+/*}*/
+
+/*.sigCustom3 {*/
+/*    position: relative;*/
+/*    bottom: 40px;*/
+/*}*/
+
+/*.sigCustom3 * {*/
+/*    display: inline-block;*/
+/*    vertical-align: top;*/
+/*}*/
+
+/*.sigCustom3 .left {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    vertical-align: top;*/
+/*    width: 7px;*/
+/*}*/
+
+/*.sigCustom3 .center {*/
+/*    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    font-size: 12px;*/
+/*    line-height: 24px;*/
+/*}*/
+
+/*.sigCustom3 .right {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    width: 6px;*/
+/*}*/
+
+/*.sigCustom4 {*/
+/*    position: relative;*/
+/*    bottom: 30px;*/
+/*}*/
+
+/*.sigCustom4 * {*/
+/*    display: inline-block;*/
+/*    vertical-align: top;*/
+/*}*/
+
+/*.sigCustom4 .left {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    vertical-align: top;*/
+/*    width: 7px;*/
+/*}*/
+
+/*.sigCustom4 .center {*/
+/*    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    font-size: 12px;*/
+/*    line-height: 24px;*/
+/*}*/
+
+/*.sigCustom4 .right {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    width: 6px;*/
+/*}*/
+
+/*.sigCustom5 {*/
+/*    position: relative;*/
+/*    bottom: 25px;*/
+/*}*/
+
+/*.sigCustom5 * {*/
+/*    display: inline-block;*/
+/*    vertical-align: top;*/
+/*}*/
+
+/*.sigCustom5 .left {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_l.png") no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    vertical-align: top;*/
+/*    width: 7px;*/
+/*}*/
+
+/*.sigCustom5 .center {*/
+/*    background: url(http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_bg.png) repeat-x;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    font-size: 12px;*/
+/*    line-height: 24px;*/
+/*}*/
+
+/*.sigCustom5 .right {*/
+/*    background: url("http://t1.daumcdn.net/localimg/localimages/07/2011/map/storeview/tip_r.png") -1px 0 no-repeat;*/
+/*    display: inline-block;*/
+/*    height: 24px;*/
+/*    overflow: hidden;*/
+/*    width: 6px;*/
+/*}*/
+
+.pMapNo {
+    padding-left: 650px
+}
+
+#customClose {
+    width: 10px;
+    padding-left: 10px;
+    cursor: pointer;
+}
+#customClose > a {
+    color: black;
+}
+
+.footer {
+    position: fixed;
+    bottom: 0px;
+    width: 100%;
+    height: 2%;
+    border-top: 1px solid #a9a9a9;
+    background: #f4f4f4;
+    z-index: 10
+}
+
+/* 신호센터 페이지 */
+.centerContainer {
+    width: 908px;
+    height: 100%;
+    overflow-y: auto
+}
+
+.centerContainer .listCon, .centerContainer .updateCon, .centerContainer .insertCon {
+    margin: 5px;
+    border: 1px solid #595959
+}
+
+.centerContainer p {
+    font-weight: bold;
+    color: #2d2d2d;
+    display: inline-block;
+    width: 110px;
+    padding-left: 15px;
+    margin: 10px;
+    background: url(/images/bul_table.gif) no-repeat 5px;
+}
+
+.centerContainer .listCon table, .updateCon table, .insertCon table {
+    width: 99%;
+    border-collapse: collapse;
+    border-spacing: 0px;
+    margin: 5px
+}
+
+.centerContainer .listCon table thead, .updateCon table thead, .insertCon table thead {
+    background-color: #dfe8ef
+}
+
+.centerContainer .listCon table thead th, .updateCon table thead th, .insertCon table thead th {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa;
+    font-weight: bold;
+    color: #2d2d2d;
+    background: url(/images/table_bg.png) repeat-x;
+}
+
+.centerContainer .listCon table tbody td, .updateCon table tbody td, .insertCon table tbody td {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa;
+}
+
+.centerContainer .listCon .insertCenter {
+    width: 100%;
+    text-align: center;
+    margin: 10px auto
+}
+
+.centerContainer .listCon .insertCenter input {
+    width: 200px
+}
+
+.centerContainer .updateCon {
+    display: none
+}
+
+.centerContainer .updateCon table tbody input, .centerContainer .updateCon table tbody select {
+    width: 100%;
+    padding-left: 0px !important;
+    height: 21px
+}
+
+.centerContainer .updateCon .updateCenter {
+    width: 100%;
+    text-align: center;
+    margin: 10px auto
+}
+
+.centerContainer .updateCon .updateCenter input {
+    width: 200px
+}
+
+.centerContainer .updateCon table tbody select {
+    height: 21px
+}
+
+.centerContainer .insertCon {
+    display: none
+}
+
+.centerContainer .insertCon table tbody input, .centerContainer .insertCon table tbody select {
+    width: 100%;
+    padding-left: 0px !important;
+    height: 21px
+}
+
+.centerContainer .insertCon .insertCenter {
+    width: 100%;
+    text-align: center;
+    margin: 10px auto
+}
+
+.centerContainer .insertCon .insertCenter input {
+    width: 200px
+}
+
+.centerContainer img {
+    cursor: pointer
+}
+
+/* 이벤트이력관리 페이지 */
+.eventContainer {
+    width: 908px;
+    height: 100%
+}
+
+.eventContainer .listCon {
+    height: 98.5%;
+    margin: 5px;
+    border: 1px solid #595959
+}
+
+.eventContainer p {
+    font-weight: bold;
+    color: #2d2d2d;
+    display: inline-block;
+    width: 110px;
+    padding-left: 15px;
+    margin: 10px;
+    background: url(/images/bul_table.gif) no-repeat 5px;
+}
+
+.eventContainer .listCon #eventListCnt {
+    position: absolute;
+    top: 100px;
+    right: 40px
+}
+
+.eventContainer .listCon .eventListTable {
+    width: auto;
+    height: 720px;
+    overflow: hidden
+}
+
+.eventContainer .listCon .eventListTable .eventListHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.eventContainer .listCon .eventListTable .eventListHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.eventContainer .listCon .eventListTable .eventListBody {
+    max-height: 680px;
+    overflow-y: scroll
+}
+
+.eventContainer .listCon table {
+    width: 99%;
+    border-spacing: 0px;
+    margin: 0 5px;
+    border: 1px solid #595959
+}
+
+.eventContainer .listCon table thead {
+    background-color: #dfe8ef
+}
+
+.eventContainer .listCon table thead th {
+    padding: 5px;
+    text-align: center;
+    font-weight: bold;
+    color: #2d2d2d;
+    background: url(/images/table_bg.png) repeat-x;
+    border: 1px solid #595959
+}
+
+.eventContainer .listCon table tbody td {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa;
+}
+
+.eventContainer .listCon .collctDtime, .eventContainer .listCon .searchType {
+    margin: 10px
+}
+
+.eventContainer .listCon .collctDtime {
+    padding-left: 15px
+}
+
+.eventContainer .listCon .searchType {
+    padding-left: 27px
+}
+
+.eventContainer .listCon input, .eventContainer .listCon select {
+    height: 20px
+}
+
+.eventContainer .listCon .collctDtime input {
+    width: 90px !important
+}
+
+.eventContainer .listCon img {
+    cursor: pointer
+}
+
+/* 이벤트이력관리 팝업 페이지 */
+.eventHist p {
+    font-weight: bold;
+    color: #2d2d2d;
+    display: inline-block;
+    width: 110px;
+    padding-left: 15px;
+    margin: 10px;
+    background: url(/images/bul_table.gif) no-repeat 5px;
+}
+
+.eventHist .eventPopListTable {
+    width: auto;
+    height: 455px;
+    overflow: hidden
+}
+
+.eventHist .eventPopListTable .eventPopListHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #f0f0f0;*/
+    /*scrollbar-track-color: #f0f0f0;*/
+    /*scrollbar-face-color: #f0f0f0;*/
+    /*scrollbar-shadow-color: #f0f0f0*/
+}
+
+/*.eventHist .eventPopListTable .eventPopListHead::-webkit-scrollbar {*/
+/*    background-color: #f0f0f0*/
+/*}*/
+
+.eventHist .eventPopListTable .eventPopListBody {
+    max-height: 400px;
+    overflow-y: scroll
+}
+
+.eventHist .eventPopListTable #eventPopListCnt {
+    margin-left: 800px
+}
+
+.eventHist .eventPopListTable table {
+    width: 99%;
+    border-collapse: collapse;
+    border-spacing: 0px
+}
+
+.eventHist .eventPopListTable table thead {
+    background-color: #dfe8ef
+}
+
+.eventHist .eventPopListTable table thead th {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa;
+    font-weight: bold;
+    color: #2d2d2d;
+    background: url("/images/table_bg.png") repeat-x;
+}
+
+.eventHist .eventPopListTable table tbody td {
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bababa;
+}
+
+.eventHist .collctDtime, .eventHist .searchType {
+    margin: 10px
+}
+
+.eventHist .collctDtime {
+    padding-left: 15px
+}
+
+.eventHist input, .eventHist select {
+    height: 20px
+}
+
+.eventHist .collctDtime input {
+    width: 90px !important
+}
+
+.eventHist .collctDtime img {
+    cursor: pointer
+}
+
+
+.loading {
+    display: none;
+    position: absolute;
+    top: 55px;
+    left: 300px
+}
+
+
+/* 20180517 수정 */
+
+input[type="number"]::-webkit-outer-spin-button,
+input[type="number"]::-webkit-inner-spin-button {
+    opacity: 1;
+}
+
+.updateSigInfoDiv .updateSigHeader {
+    display: inline-block
+}
+
+.updateSigInfoDiv .updateSigHeader ul li {
+    float: left;
+    padding: 10px 0 0 10px;
+    font-size: 15px;
+}
+
+.updateSigInfoDiv .updateSigHeader ul li a {
+    border: 1px solid #888888;
+    padding: 5px
+}
+
+.updateSigInfoDiv .updateSigSub {
+    padding: 10px 0 0 130px
+}
+
+.updateSigInfoDiv .updatePhase {
+    display: none;
+    position: relative;
+    top: -5px
+}
+
+.updateSigInfoDiv .updatePhase .updateMapNo {
+    display: inline-block;
+    margin: 5px;
+    position: relative;
+    left: 810px
+}
+
+.updateSigInfoDiv .phaseInfoCon {
+    padding: 0 5px
+}
+
+.updateSigInfoDiv .phaseInfoCon table td span {
+}
+
+.updateSigInfoDiv .phaseInfoCon table .aStrtLinkId td {
+    height: 0 !important
+}
+
+.updateSigInfoDiv .phaseInfoCon table .aEndLinkId td {
+    height: 0 !important
+}
+
+.updateSigInfoDiv .phaseInfoCon table .bStrtLinkId td {
+    height: 0 !important
+}
+
+.updateSigInfoDiv .phaseInfoCon table .bEndLinkId td {
+    height: 0 !important
+}
+
+.updateSigInfoDiv .phaseInfoCon table .aPUpdate td {
+    height: 15px !important
+}
+
+.updateSigInfoDiv .phaseInfoCon table .bPUpdate td {
+    height: 15px !important
+}
+
+.updateSigInfoDiv .linkInfoCon {
+    display: none;
+    padding: 0 5px
+}
+
+.updateSigInfoDiv .updatePhaseDiv {
+    display: block;
+    height: 105px;
+    margin: 10px 5px 0 5px;
+    background-color: #ffffff;
+    border: 1px solid #bababa;
+    z-index: 10
+}
+
+.updateSigInfoDiv .updatePhaseDiv h4 {
+    padding: 10px;
+    text-align: left
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li {
+    padding-left: 5px;
+    float: left
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li .updateDeg {
+    padding-left: 30px
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li input[type=number] {
+    width: 50px
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li input[type=text] {
+    width: 80px
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li input[type=button] {
+    width: 70px;
+    cursor: pointer
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li.phaseResetBt {
+    padding-left: 30px
+}
+
+.updateSigInfoDiv .updatePhaseDiv ul li.phaseSaveBt {
+    padding-left: 10px
+}
+
+.updateSigInfoDiv .updatePhaseDiv .linkMessage {
+    position: absolute;
+    top: 80px;
+    left: 160px
+}
+
+.updateSigInfoDiv .updateAPhaseBack {
+    position: absolute;
+    width: 800px;
+    height: 100px;
+    z-index: 20;
+    left: 105px;
+    top: 170px
+}
+
+.updateSigInfoDiv .updateAPhaseBack ul li {
+    float: left;
+    width: 100px;
+    height: 100px;
+    background-color: #ffffff;
+    opacity: 0;
+    cursor: pointer
+}
+
+.updateSigInfoDiv .updateBPhaseBack {
+    position: absolute;
+    width: 800px;
+    height: 100px;
+    z-index: 20;
+    left: 105px;
+    top: 273px
+}
+
+.updateSigInfoDiv .updateBPhaseBack ul li {
+    float: left;
+    width: 100px;
+    height: 100px;
+    background-color: #ffffff;
+    opacity: 0;
+    cursor: pointer
+}
+
+.signalMapDiv {
+    display: none
+}
+
+
+/* 정보관리 */
+/*.addSigContainer h4{padding:7px}
+.addSigContainer .updateSigTab{display:none;width:100%;height:25px}
+
+.addSigContainer .sigMove{margin:5px;padding:5px;background:#2276cc; height:45px;}
+.addSigContainer .sigMove ul { margin-top:10px; color:#fff; padding:0 5px 0 5px;}
+.addSigContainer .sigMove ul li select {height:30px;}
+.addSigContainer .sigMove .regionList{float:left}
+.addSigContainer .sigMove .intList{margin-left:120px}
+.addSigContainer .sigMove #centerList{width:70px}
+.addSigContainer .sigMove #intList{width:250px}
+.addSigContainer .sigMove label{font-weight:600}
+
+.addSigContainer .updateSigTab ul li{float:left;padding:0 0 0 10px;font-size:15px;}
+.addSigContainer .updateSigTab ul li a{border:1px solid #888888;padding:5px}
+.addSigContainer .addSigMap{height:350px;margin:5px;border:1px solid #bababa}
+.addSigContainer .addSigBottom{height:416px;margin:5px;border:1px solid #bababa}
+.addSigContainer .addSigBottom .addsig_bottom_btns{display:none;margin-top:5px};*/
+
+/* 정보관리 교차로관리 */
+.addSigBottom h4 {
+    font-size: 12px;
+    background: #CCC;
+}
+
+.addSigContainer .addSigBottom .addSigDiv h3 {
+    color: #06F;
+    font-size: 13px;
+}
+
+.addSigContainer .addSigBottom .addSigDiv {
+    margin: 10px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li {
+    line-height: 38px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(1) select {
+    margin-left: 12px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(2) select {
+    width: 80px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(2) {
+    position: relative;
+    left: 20px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(3) {
+    position: relative;
+    left: 260px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(4) {
+    position: relative;
+    left: 277px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(4) strong {
+    margin-left: 10px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(5) input {
+    margin-left: 12px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(6) select {
+    width: 40px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(7) select {
+    width: 40px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(8) input {
+    margin-left: 11px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(9) {
+    float: left
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(9) input {
+    margin-left: 23px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li:nth-child(10) {
+    margin-left: 240px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li input[type=text] {
+    width: 150px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li input[type=checkbox] {
+    width: 15px;
+    height: 15px;
+    margin: 15px 5px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li input[type=number] {
+    width: 150px
+}
+
+.addSigContainer .addSigBottom .addSigDiv ul li input[type=button] {
+    float: left;
+    width: 150px;
+    height: 30px;
+    text-align: center;
+    cursor: pointer
+}
+
+/* 정보관리 상단 메뉴 */
+.addSigContainer .addSigBottom .sigMoveDiv h3 {
+    color: blue
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv {
+    margin: 10px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(1) input {
+    margin-left: 13px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(2) {
+    float: left
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(3) {
+    margin-left: 260px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(4) input {
+    margin-left: 13px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(5) input {
+    margin-left: 13px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(6) input {
+    margin-left: 26px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(7) input {
+    margin-left: 26px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li:nth-child(8) input {
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li {
+    line-height: 38px;
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li input[type=text] {
+    width: 150px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li input[type=checkbox] {
+    width: 15px;
+    height: 15px;
+    margin: 15px 5px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li input[type=number] {
+    width: 150px
+}
+
+.addSigContainer .addSigBottom .sigMoveDiv ul li input[type=button] {
+    width: 150px;
+    height: 30px;
+    text-align: center;
+    cursor: pointer
+}
+
+/* 정보관리 현시관리,링크관리 */
+/* .addSigContainer .addSigBottom .updatePhaseAdd{display:none;position:relative;}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv{display:block;height:65px;margin:5px;background-color:#ffffff;border:1px solid #bababa;z-index:10}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv h4{padding:10px 10px 5px 10px;text-align:left}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv h3{margin:10px 10px 5px 5px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li{padding-left:5px;float:left}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv label{background:none}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li .updateDeg{padding-left:30px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li input[type=number]{width:50px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li input[type=text]{width:80px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li input[type=button]{width:70px;height:25px;cursor:pointer;background:#96a2b2 !important; color:#fff; font-weight:bold;line-height:25px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li.phaseResetBt{padding-left:30px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv ul li.phaseSaveBt{padding-left:10px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updatePhaseDiv .linkMessage{position:absolute;top:37px;left:260px}
+
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon{width:auto;height:100%;padding:5px}
+.addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon{width:auto;height:100%;padding:0 5px}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table, .addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table{width:100%;border-collapse:collapse;}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table thead, .addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table thead{width:100%;background-color:#f4f4f4}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table th, .addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table td{padding:6px;text-align:center;border:1px solid #bababa;width:9.1%}
+.addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table th, .addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table td{padding:6px;text-align:center;border:1px solid #bababa;width:10%}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table th, .addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table th{font-weight: bold;color: #2d2d2d;}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table td{color:#515151;height:90px}
+.addSigContainer .addSigBottom .updatePhaseAdd .linkInfoCon table td{color:#515151;height:40px}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table .aProgress td{height:auto}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon table .bProgress td{height:auto}
+.addSigContainer .addSigBottom .updatePhaseAdd .phaseInfoCon input{width: 50px;margin: 0 18px;line-height: 15px;text-align: center;cursor: pointer;}
+
+.addSigContainer .addSigBottom .updatePhaseAdd .updateMapNo{display:inline-block;margin:5px;position:relative;left:775px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updateMapNo #updateMapNo{width:45px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updateAPhaseBack{position:absolute;width:785px;height:100px;z-index:20;left:105px;top:142px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updateBPhaseBack{position:absolute;width:800px;height:100px;z-index:20;left:105px;top:245px}
+.addSigContainer .addSigBottom .updatePhaseAdd .updateAPhaseBack ul li,.updatePhaseAdd .updateBPhaseBack ul li{float:left;width:98.1px;height:100px;cursor:pointer}
+
+정보관리 업로드
+.addSigContainer .addSigBottom .uploadDiv{display:none}
+.addSigContainer .addSigBottom .uploadDiv h4{display:block !important}
+.addSigContainer .addSigBottom .uploadDiv ul li{padding:10px}
+.addSigContainer .addSigBottom .uploadDiv ul li select{height:27px;overflow:hidden}
+.addSigContainer .addSigBottom .uploadDiv ul li input[type="file"]{display: inline-block;padding: .2em .75em;font-size: inherit;font-family: inherit;line-height: normal;
+vertical-align: middle;background-color: #f5f5f5;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;
+border-radius: .25em;-webkit-appearance: none; 네이티브 외형 감추기
+-moz-appearance: none;appearance: none;}
+.addSigContainer .addSigBottom .uploadDiv ul li input[type="button"]{width:100px;height:30px;cursor:pointer}
+.addSigContainer .addSigBottom .uploadDiv ul li label{display: inline-block;padding: .5em;color: #000000;font-size: inherit;line-height: normal;background-color: #fdfdfd;
+cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-radius: .25em;width:100px;text-align:center} */
+
+/* 엑셀업로드 iframe */
+.excelUpload {
+    width: 878px
+}
+
+.extractTodExcelDiv h4 {
+    display: block !important;
+    padding: 10px;
+    font-size: 14px;
+    background: #CCC;
+}
+
+.extractTodExcelDiv {
+    height: 100%
+}
+
+.extractTodExcelDiv ul li {
+    padding: 10px
+}
+
+.extractTodExcelDiv ul li:nth-child(2) {
+    float: left
+}
+
+.extractTodExcelDiv ul li label {
+    display: inline-block;
+    padding: .5em;
+    color: #000000;
+    font-size: inherit;
+    line-height: normal;
+    background-color: #fdfdfd;
+    cursor: pointer;
+    border: 1px solid #ebebeb;
+    border-bottom-color: #e2e2e2;
+    border-radius: .25em;
+    width: 100px;
+    text-align: center
+}
+
+.extractTodExcelDiv ul li input[type="button"] {
+    width: 100px;
+    height: 30px;
+    cursor: pointer
+}
+
+.extractTodExcelDiv ul li select {
+    height: 27px;
+    overflow: hidden
+}
+
+.showLoading {
+    display: none;
+    position: absolute;
+    top: 73px;
+    left: 310px;
+    z-index: 20
+}
+
+.dataLoading {
+    display: none;
+    position: absolute;
+    top: 200px;
+    left: 300px;
+    z-index: 20
+}
+
+.excelDataLoading {
+    display: none;
+    position: absolute;
+    top: 60px;
+    left: 270px;
+    opacity: 0.7;
+    z-index: 20
+}
+
+.imgDataLoading {
+    display: none;
+    position: absolute;
+    top: 200px;
+    left: 270px;
+    opacity: 0.7;
+    z-index: 20
+}
+
+/*.headMenu ul .toggleBtn {*/
+/*    border-right: 1px solid #bbbbbb*/
+/*}*/
+
+/*.headMenu ul .toggleBtn:nth-child(1) {*/
+/*    border-left: 1px solid #bbbbbb*/
+/*}*/
+
+.sigPop_box .pop_table_phase {
+    margin: 5px 0
+}
+
+.sigPop_box .pop_table_phase tr td:nth-child(1) {
+    width: 10%;
+    padding: 5px
+}
+
+.sigPop_box .pop_table_phase tr td {
+    width: 15%;
+    padding: 5px;
+    text-align: center;
+    border: 1px solid #bbb;
+    font-size: 11px
+}
+
+.sigPop_box .pop_table_phase tr:nth-child(2) {
+    height: 80px
+}
+
+.sigPop_box .pop_table_phase tr:nth-child(4) {
+    height: 80px
+}
+
+.treeLoading {
+    display: none;
+    position: absolute;
+    top: 80px;
+    left: 45%
+}
+.history-loading{
+    width: 100%;
+    height: calc(100% - 21.3px);
+    display: none;
+    align-items: center;
+    justify-content: center;
+}
+
+/* 로드뷰 아이콘 */
+
+#roadviewClose {
+    position: absolute;
+    padding: 4px;
+    top: 5px;
+    left: 5px;
+    cursor: pointer;
+    background: #fff;
+    border-radius: 4px;
+    border: 1px solid #c8c8c8;
+    box-shadow: 0px 1px #888;
+    z-index: 10
+}
+
+#roadviewClose .img {
+    display: block;
+    background: url(http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/rv_close.png) no-repeat;
+    width: 14px;
+    height: 14px;
+}
+
+.board-write {
+    width: 100%;
+    border-top: 2px solid #005ead;
+    border-bottom: 2px solid #005ead;
+}
+
+.board-write th {
+    background: #CCC;
+    border-bottom: 1px solid #595959;
+}
+
+.board-write td {
+    padding: 10px;
+    border-bottom: 1px solid #ddd;
+}
+
+.board-write td #addIntLcType, .board-write td #addIntLampType, .board-write td #addIntMainphase {
+    width: 100px
+}
+
+.btn-tinted-03 {
+    background: #96a2b2 !important;
+    color: #fff;
+    font-weight: bold;
+}
+
+.btn-inline a {
+    display: inline-block;
+    height: 28px;
+    line-height: 28px;
+    padding: 0 20px;
+    background: #333;
+    color: #fff;
+    vertical-align: middle;
+}
+
+/* 페이즈정보 팡벙 */
+.cvibInfoTop {
+    width: 100%;
+    padding: 5px 5px 0 5px;
+    box-sizing: border-box;
+}
+
+.cvibInfoTop .tabs {
+    height: 30px;
+    position: relative;
+    z-index: 50;
+}
+
+.cvibInfoTop .tabs li {
+    float: left;
+}
+
+.cvibInfoTop .tabs li:nth-child(1) {
+    border-top: 1px solid #bbbbbb;
+    border-left: 1px solid #bbbbbb
+}
+
+.cvibInfoTop .tabs li:nth-child(2) {
+    border-top: 1px solid #bbbbbb
+}
+
+.cvibInfoTop .tabs .regionIntNm {
+    line-height: 30px;
+    width: 100%;
+    text-align: center;
+    font-size: 15px
+}
+
+.error-box table tr:nth-child(odd),
+#evpBottomInfo > tr:nth-child(odd),
+#cvibBottomInfo > tr:nth-child(odd) {
+    background-color: #2A2A2A;
+}
+
+.error-box table tr:nth-child(even),
+#evpBottomInfo > tr:nth-child(even),
+#cvibBottomInfo > tr:nth-child(even) {
+    background-color: #1E1E1E;
+}
+
+#evpBottomInfo > tr {
+    box-sizing: border-box;
+    height: 32px;
+    cursor: pointer;
+}
+.bottomMenu .bottomBody table tr.on td {
+    color: #eeeeee;
+}
+#evpBottomInfo > tr.on {
+    background-color: #3396FF;
+}
+
+.bottomInfo {
+    height: 100%;
+    width: 100%;
+    float: left;
+    overflow: auto;
+}
+.error-box table {
+    width: 100%;
+    border-collapse: separate;
+}
+
+.bottomInfo table {
+    width: 100%;
+    border-collapse: collapse
+}
+
+.error-box table th {
+    background-color: #121212;
+}
+.bottomInfo table thead {
+    background-color: #292828;
+}
+#bottomInfoBottom tr,
+#bottomInfoTop tr td{
+    background-color: #3c3c3c;
+}
+
+/*.error-box table tbody tr:last-child td{*/
+/*    border-bottom: 0;*/
+/*}*/
+/*.error-box table th:first-child {*/
+/*    border-left: 1px solid #595959;*/
+/*}*/
+
+/*.error-box table th {*/
+/*    border-top: 1px solid #595959;*/
+/*}*/
+.error-box table td,
+.error-box table th {
+    border-right: 1px solid #424242;
+    border-bottom: 1px solid #424242;
+    line-height: 30px;
+    text-align: center;
+    color: #b2b2b2;
+    padding: 0 10px;
+    box-sizing: border-box;
+}
+.error-box table td:nth-child(1):not(tr.empty-box td:nth-child(1)),
+.error-box table td:nth-child(2) {
+    text-align: left;
+}
+
+.error-box .pagination {
+    display: none;
+    gap: 8px;
+    background-color: #1E1E1E;
+    padding: 5px;
+    border-radius: 8px;
+    height: 40px;
+    box-sizing: border-box;
+}
+
+.pagination.on {
+    display: flex;
+}
+
+.error-box .pagination button.unactive{
+    cursor: default;
+    opacity: 0.5;
+}
+.error-box .pagination button.prev {
+    margin-right : auto;
+}
+.error-box .pagination button.next {
+    margin-left : auto;
+}
+.error-box .pagination button.prev,
+.error-box .pagination button.next {
+    font-size: 24px;
+}
+.error-box .pagination button {
+    background: transparent;
+    color: #ccc;
+    border: none;
+    width: 30px;
+    height: 30px;
+    border-radius: 6px;
+    cursor: pointer;
+    transition: background 0.2s;
+}
+.error-box .pagination button:not(.active):not(.unactive):hover {
+    background: #333;
+}
+.error-box .pagination .active {
+    background: #596f8f;
+    color: #fff;
+}
+
+.bottomInfo table tr th, .bottomInfo table tr td {
+    border: 1px solid #595959;
+    line-height: 30px;
+    text-align: center;
+    color: #b2b2b2
+}
+.error-box .showLoading {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: none;
+    align-items: center;
+    justify-content: center;
+    background-color: rgba(0, 0, 0, 0.3);
+}
+
+.error-box table th,
+.bottomInfo table tr th {
+    font-weight: bold
+}
+
+.bottomInfo .bottomInfoTop {
+    /*margin: 5px*/
+    padding: 5px;
+    box-sizing: border-box;
+}
+
+.bottomInfo .bottomInfoBottom {
+    /*margin: 5px*/
+    padding: 5px;
+    box-sizing: border-box;
+}
+
+.bottomInfo .bottomInfoBottom .bottomInfoHead {
+    overflow-y: scroll;
+    /*scrollbar-arrow-color: #eee;*/
+    /*scrollbar-base-color: #eee;*/
+    /*scrollbar-3dLight-Color: #eee;*/
+    /*scrollbar-Face-Color: #eee;*/
+    /*scrollbar-Track-Color: #eee;*/
+}
+
+/*.bottomInfo .bottomInfoBottom .bottomInfoHead::-webkit-scrollbar {*/
+/*    width: 17px;*/
+/*    background-color: #eee;*/
+/*    border-top: 1px solid #595959;*/
+/*    border-right: 1px solid #595959;*/
+/*    border-bottom: 1px solid #595959;*/
+/*}*/
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody {
+    /*max-height: 439px;*/
+    overflow: auto;
+    height: 138px;
+    border-bottom: 1px solid #595959
+}
+.bottomInfo .bottomInfoBottom .bottomInfoBody table{
+    border-collapse: separate;
+    border-spacing: 0;
+}
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody thead {
+    position: sticky;
+    top: 0;
+    left: 0;
+}
+
+/*.bottomInfo .bottomInfoBottom .bottomInfoBody {*/
+/*    max-height: 370px;*/
+/*    overflow-y: scroll;*/
+/*    border-bottom: 1px solid #595959*/
+/*}*/
+/*.leftMenu #intTree::-webkit-scrollbar,*/
+/*.bottomMenu .bottomBody::-webkit-scrollbar,*/
+/*#historyList::-webkit-scrollbar,*/
+/*.bottomInfo .bottomInfoBottom .bottomInfoBody::-webkit-scrollbar {*/
+/*    width: 10px;*/
+/*    height: 10px;*/
+/*    border-right: 1px solid #595959;*/
+/*    background-color: #eee;*/
+/*    border-radius: 5px;*/
+/*}*/
+
+/*.leftMenu #intTree::-webkit-scrollbar-thumb,*/
+/*.bottomMenu .bottomBody::-webkit-scrollbar-thumb,*/
+/*#historyList::-webkit-scrollbar-thumb,*/
+/*.bottomInfo .bottomInfoBottom .bottomInfoBody::-webkit-scrollbar-thumb {*/
+/*    background-color: #595959;*/
+/*    border-radius: 5px;*/
+/*}*/
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody table tbody tr:nth-child(1) td {
+    border-top: 0
+}
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_1 {
+    background-color: #ff0000;
+    color: white;
+}
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_2 {
+    background-color: #ffff00;
+}
+
+.bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_3 {
+    background-color: #008000;
+    color: white;
+}
+
+.bottomInfo .bottomEvent {
+    display: none;
+    margin: 10px
+}
+
+.bottomInfo .bottomEvent .collctDtime img {
+    cursor: pointer
+}
+
+.bottomInfo .bottomEvent .eventPopListTable {
+    margin-top: 10px
+}
+
+.bottomInfo .bottomEvent .cvibLoading {
+    display: none;
+    margin: 0 auto
+}
+
+.coordBox {
+    /*background-color: white;*/
+    /*gap: 10px;*/
+    /*width: 470px;*/
+    /*height: 40px;*/
+    /*display: none;*/
+    /*align-items: center;*/
+    /*justify-content: center;*/
+    /*position: absolute;*/
+    /*z-index: 2;*/
+    /*left: 115px;*/
+    /*top: 28px;*/
+    /*border: 1px solid #c9c9c9;*/
+    background-color: #2c2c2c;
+    gap: 10px;
+    width: 340px;
+    height: 40px;
+    display: none;
+    align-items: center;
+    justify-content: center;
+    position: absolute;
+    z-index: 2;
+    left: 115px;
+    top: 28px;
+    border: 1px solid #595959;
+    color: #cccccc;
+}
+
+#lctn_x_coord, #lctn_y_coord {
+    font-family: 'Gulim', '굴림', sans-serif;
+    font-size: 12px;
+    color: #dddddd;
+    padding-left: 3px;
+    border: 1px solid #595959;
+    background-color: #313131;
+}
+
+.coordBox.on {
+    display: flex;
+}
+
+.coord-unit {
+    display: flex;
+    gap: 5px;
+    align-items: center;
+    font-weight: bold;
+    font-size: 12px;
+}
+
+.coord-unit input {
+    padding: 3px 5px;
+    font-weight: bold;
+    /*width : 110px;*/
+    width: 180px;
+    font-family: 'Gulim', '굴림', sans-serif;
+    font-size: 12px;
+    color: #dddddd;
+    /*padding-left: 3px;*/
+    border: 1px solid #595959;
+    background-color: #313131;
+}
+
+.coord-btn {
+    border: 1px solid #1e65c5;
+    background-color: #1e65c5;
+    color: white;
+    font-weight: bold;
+    cursor: pointer;
+    border-radius: 5px;
+    padding: 3px 18px;
+}
+
+.coord-btn:hover {
+    filter:brightness(1.1);
+}
+
+@media (max-width: 1000px) {
+    .crossLoadMap {
+        width: 100%;
+        height: 100%;
+        border: 1px solid #595959;
+        float: left;
+
+    }
+
+    .crossLoadInfo {
+        width: 100%;
+        height: 149px;
+        border: 1px solid #595959;
+        float: left;
+    }
+}
+
+.play-box {
+    width: 100%;
+    height: 38px;
+    background-color: #212121;
+}
+#pause.on,
+#stop.on,
+#play.on {
+    cursor: pointer;
+}
+.play-box > div {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: flex-end;
+    padding: 5px 20px;
+    color: #595959;
+    gap: 5px;
+    box-sizing: border-box;
+    align-items: center;
+}
+#map img{
+    /*filter: brightness(0.6) invert(0.8) contrast(2) hue-rotate(180deg) saturate(0) brightness(0.7);*/
+    filter: brightness(0.6) invert(0.8) contrast(2) hue-rotate(180deg) saturate(0) brightness(0.7);
+}
+
+#start_time, #end_time,
+#start, #end {
+    width: 105px;
+    height: 25px;
+}
+
+.history-btn {
+    padding: 4px;
+    border: 1px solid #5f5f5f;
+    background-color: #444444;
+    color: #dddddd;
+    width: 50px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 5px;
+    font-weight: bold;
+    cursor: pointer;
+    margin-right: 20px;
+    margin-left: auto;
+}
+.evp_legend {
+    position: absolute;
+    bottom: 0;
+    right: 34px;
+    z-index: 20;
+    display: none;
+}
+
+#map > div:nth-child(8),
+    /*.coordBox,*/
+#map .mapToggle img,
+img[src="images/tab01_on.png"],
+img[src="images/tab01_off.png"],
+img[src="images/tab02_on.png"],
+img[src="images/tab02_off.png"],
+img[src="images/logout.png"]{
+    filter: brightness(0.6) invert(1) contrast(3) hue-rotate(180deg) saturate(0.3) brightness(0.7);
+}
+#map .mapToggle img.road_on {
+    filter: brightness(0.6) invert(1) contrast(4) hue-rotate(180deg) saturate(0.3) brightness(1.1);
+}
+#map .wideMap img,
+#map .upDownMap img,
+#map img[src="/images/evp_arr.svg"],#map img[src="images/TrafficLight25.png"],
+#map img[src="/images/evp_legend.png"],
+#map img[src="/images/car.svg"],
+#map img[src="images/sig1.png"],
+#map img[src="images/sig2.png"],
+#map img[src="images/sig3.png"],
+#map img[src="images/sig4.png"],
+#map img[src="images/sig5.png"],
+#map img[src="images/sig6.png"],
+#map img[src="/images/CvibLight_0_0.gif"],
+#map img[src="/images/CvibLight_0_9.gif"],
+#map img[src="/images/CvibLight_1_1.gif"],
+#map img[src="/images/CvibLight_1_2.gif"],
+#map img[src="/images/CvibLight_1_3.gif"],
+#map img[src="/images/CvibLight_1_6.gif"],
+#map img[src="/images/CvibLight_1_9.gif"],
+#map img[src="/images/CvibLight_2_1.gif"],
+#map img[src="/images/CvibLight_2_2.gif"],
+#map img[src="/images/CvibLight_2_3.gif"],
+#map img[src="/images/CvibLight_2_9.gif"],
+#map img[src="/images/CvibLight_3_1.gif"],
+#map img[src="/images/CvibLight_3_2.gif"],
+#map img[src="/images/CvibLight_3_3.gif"],
+#map img[src="/images/CvibLight_3_9.gif"],
+#map img[src="/images/CvibLight_5_0.gif"],
+#map img[src="/images/CvibLight_5_9.gif"],
+#map img[src="/images/CvibLight_9_1.gif"],
+#map img[src="/images/CvibLight_9_2.gif"],
+#map img[src="/images/CvibLight_9_3.gif"],
+#map img[src="/images/CvibLight_9_4.gif"],
+#map img[src="/images/car.gif"],
+#map img[src="/images/car2.gif"],
+#map img[src="http://t1.kakaocdn.net/localimg/localimages/07/mapapidoc/roadview_wk.png"],
+#map img[src="images/goglemarker.png"],
+#map img[src="http://t1.daumcdn.net/mapjsapi/images/2x/marker.png"]
+    /*#map .mapToggle img,*/
+    /*#map img[src="images/arrow_left.png"],*/
+    /*#map img[src="images/arrow_down.png"]*/
+{
+    filter: none !important;
+}
+.info-window-content {
+    padding: 5px 24px 5px 10px;
+    box-sizing: border-box;
+    width: 152px;
+    height: 25px;
+    background-color: rgb(85 85 85);
+    color: #cccccc;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+.jstree-children > li:nth-child(odd) > div{
+    background-color: #292828;
+}
+
+.jstree-children > li:nth-child(even) > div{
+    background-color: #3c3c3c;
+}
+
+.jstree-node{
+    color: #b2b2b2;
+}
+
+#intTree{
+    background-color: #212121;
+}
+
+
+.node-folder ~ label::before{
+    background-image: url('/images/chevron-down.png');
+    background-size: 20px 20px;
+    content: "";
+    width: 20px;
+    height: 20px;
+}
+.node-li, .node-group {
+    list-style: none;
+}
+
+.node-group > .node-li:nth-child(even) {
+    background-color: #2A2A2A;
+}
+.node-group > .node-li:nth-child(odd) {
+    background-color: #1E1E1E;
+}
+.node-group .node-li > div {
+    gap: 5px;
+}
+.node-li > div:hover {
+    background: #2c3e49; /* 다크 모드에 어울리는 짙은 회색 */
+    border-radius: 5px; /* 부드러운 모서리 */
+}
+
+.node-li > div {
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    width: 100%;
+    height: 100%;
+    padding: 5px;
+    user-select: none;
+    cursor: pointer;
+}
+.node-li {
+    /* padding: 5px; */
+    font-size: 12px;
+    width: 100%;
+}
+
+.node-li.off {
+    display: none;
+}
+.node-group > .node-li.on {
+    background-color: #2c3e49;
+}
+.node-group > .node-li > div {
+    padding-left: 30px;
+}
+.node-li label {
+    user-select: none;
+    cursor: pointer;
+}
+.node_state {
+    display: none;
+}
+.node-group {
+    display: none;
+}
+
+.node_state + div > div:nth-child(1) {
+    background-image: url('/images/chevron-right.png');
+    width: 20px;
+    height: 20px;
+    background-size: 20px 20px;
+    cursor: pointer;
+}
+.node_state:checked + div > div:nth-child(1){
+    background-image: url('/images/chevron-down.png');
+    /* width: 20px;
+    height: 20px;
+    background-size: 20px 20px; */
+}
+.node_state:checked ~ .node-group{
+    display: list-item;
+}
+
+.tree-container {
+    background-color: #2c2c2c;
+    position: relative;
+    width: 100%;
+    height: 100%;
+    color: #ccc;
+    border-bottom: 1px solid #595959;
+}
+
+.tree-root {
+    width: 100%;
+    height: calc(100% - 40px);
+    overflow: auto;
+}
+.tree-search {
+    width: 100%;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    padding: 5px;
+    gap: 5px;
+    border-bottom: 1px solid #595959;
+}
+
+.error-content .button,
+.tree-search  > .searchBtn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 70px;
+    height: 100%;
+    border-radius: 5px;
+    background-color: #444444;
+    cursor: pointer;
+    color: #dddddd;
+    font-weight: bold;
+}
+.tree-search  > .searchBtn:hover {
+    filter: brightness(1.1);
+}
+#region,
+#start,
+#start_time,
+#end,
+#end_time {
+    background-color: #2a2a2a;
+    color: #e9e9e9;
+    border: 1px solid #444;
+    color-scheme: dark;
+}
+.tree-search > input {
+    /* background-color: #363636; */
+    background-color: #2a2a2a;
+    padding: 5px;
+    box-sizing: border-box;
+    width: calc(100% - 75px);
+    border: 1px solid #595959;
+    color: #e9e9e9;
+    height: 100%;
+}
+.tree-search > input::placeholder {
+    color: #96908b;
+}
+
+#region:focus,
+#start:focus,
+#start_time:focus,
+#end:focus,
+#end_time:focus,
+.tree-search > input:focus{
+    outline: none;
+}
+
+.comm1 {
+    width: 12px;  /* 원 크기 */
+    height: 12px;
+    background: radial-gradient(circle at top left, #66FF66, #1B5E20);
+    border-radius: 50%;
+    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5),  /* 외부 그림자 */
+    inset -2px -2px 4px rgba(0, 0, 0, 0.4), /* 내부 어두운 그림자 */
+    inset 2px 2px 3px rgba(102, 255, 102, 0.6); /* 내부 광택 효과 */
+    display: inline-block;
+}
+
+.comm0 {
+    width: 12px;  /* 원 크기 */
+    height: 12px;
+    background: radial-gradient(circle at top left, #ff6b6b, #8b0000);
+    border-radius: 50%;
+    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5),  /* 외부 그림자 */
+    inset -2px -2px 4px rgba(0, 0, 0, 0.4), /* 내부 어두운 그림자 */
+    inset 2px 2px 3px rgba(255, 100, 100, 0.6); /* 내부 광택 효과 */
+    display: inline-block;
+}
+.comm2 {
+    width: 12px;  /* 원 크기 */
+    height: 12px;
+    background: radial-gradient(circle at top left, #FFD700, #B8860B);  /* 골드 → 딥 옐로우 */
+    border-radius: 50%;
+    box-shadow:
+            2px 2px 5px rgba(0, 0, 0, 0.5),  /* 외부 그림자 */
+            inset -2px -2px 4px rgba(0, 0, 0, 0.4), /* 내부 어두운 그림자 */
+            inset 2px 2px 3px rgba(255, 215, 100, 0.6); /* 내부 광택 효과 */
+    display: inline-block;
+}
+.comm3 {
+    width: 12px;  /* 원 크기 */
+    height: 12px;
+    background: radial-gradient(circle at top left, #B0B0B0, #505050);  /* 밝은 회색 → 어두운 회색 */
+    border-radius: 50%;
+    box-shadow:
+            2px 2px 5px rgba(0, 0, 0, 0.5),  /* 외부 그림자 */
+            inset -2px -2px 4px rgba(0, 0, 0, 0.4), /* 내부 어두운 그림자 */
+            inset 2px 2px 3px rgba(200, 200, 200, 0.6); /* 내부 광택 효과 */
+    display: inline-block;
+}
+
+/* 전체 스크롤바 스타일 */
+::-webkit-scrollbar {
+    width: 8px; /* 스크롤바 두께 */
+    height: 6px;
+}
+
+/* 스크롤바 트랙 (배경) */
+::-webkit-scrollbar-track {
+    background: #1e1e1e; /* 다크 모드에 어울리는 어두운 배경 */
+    border-radius: 4px;
+}
+
+/* 스크롤바 핸들 (움직이는 부분) */
+::-webkit-scrollbar-thumb {
+    background: #555; /* 중간 회색 */
+    border-radius: 4px;
+}
+
+/* 스크롤바 핸들 호버 효과 */
+::-webkit-scrollbar-thumb:hover {
+    background: #777; /* 더 밝은 회색 */
+}
+
+
+.modal-box {
+    width: 710px;
+    height: 610px;
+    position: absolute;
+    display: none;
+    bottom: 0;
+    right: 0;
+    z-index: 999999;
+    background-color: #212121;
+    border: 1px solid #595959;
+    box-shadow: 2px 2px 5px 2px black;
+}
+
+.modal-container {
+    width: 100%;
+    height: calc(100% - 25px);
+}
+.modal-title-box > div:nth-child(1) {
+    font-size: 13px;
+    font-weight: bold;
+}
+
+.modal-title-box {
+    height: 25px;
+    width: 100%;
+    display: flex;
+    box-sizing: border-box;
+    justify-content: space-between;
+    padding: 0 10px;
+    border-bottom: 1px solid #424242;
+    align-items: center;
+    color: #cccccc;
+    background-color: #2c2c2c;
+}
+
+.modal-close {
+    width: 15px;
+    height: 15px;
+    border: 1px solid #5f5f5f;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    border-radius: 4px;
+    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 음영 추가 */
+    user-select: none;
+    background-color: #444444;
+    font-weight: bold;
+}
+
+.modal-close:hover{
+    filter: brightness(1.6);
+}
+
+#iframeModal {
+    border-color: #444444;
+    width: 100%;
+    height: 100%;
+    box-sizing: border-box;
+    border-width: 1px;
+}
+
+.jstree-default > .jstree-no-dots .jstree-open > .jstree-ocl{
+    filter: brightness(4.0);
+}
+
+.empty-box:not(.error-content .empty-box) {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    color: #dddddd;
+}
+
+.error-content .empty-box > td {
+    text-align: center;
+    height: 400px;
+    user-select: none;
+}

+ 164 - 0
src/main/resources/static/css/tree.css

@@ -0,0 +1,164 @@
+.jstree-container-ul > li > ul > li > ul > li:nth-child(even) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.jstree-container-ul > li:nth-child(even) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.fancytree-container li li li :nth-child(even),
+.fancytree-container li:nth-child(even) {
+    background-color:#1E1E1E !important;
+}
+.jstree-container-ul > li > ul > li > ul > li > ul > li:nth-child(odd) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.jstree-container-ul > li > ul > li:nth-child(odd) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.fancytree-container li li li li:nth-child(odd),
+.fancytree-container li li:nth-child(odd) {
+    background-color: #363434 !important;
+}
+
+.jstree-container-ul > li > ul > li > ul > li > ul > li:nth-child(even) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.jstree-container-ul > li > ul > li:nth-child(even) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.fancytree-container li li li li:nth-child(even),
+.fancytree-container li li:nth-child(even) {
+    background-color: #535353 !important;
+}
+.jstree-container-ul > li > ul > li > ul > li:nth-child(odd) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.jstree-container-ul > li:nth-child(odd) > .jstree-wholerow:not(.jstree-wholerow-clicked),
+.fancytree-container li li li:nth-child(odd),
+.fancytree-container li:nth-child(odd) {
+    background-color: #2A2A2A !important;
+}
+#groupTree > ul,
+#intTree > ul {
+    background-color: #212121;
+    border: none !important;
+}
+.fancytree-title {
+    color: #cccccc !important;
+    font-size: 12px !important;
+    font-family: 돋움;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    max-width: 240px;
+    box-sizing: border-box;
+}
+span.fancytree-active {
+    background-color: #49697c !important;
+    /*filter: brightness(0.5);*/
+}
+span.fancytree-active.fancytree-focused {
+    background-color: #49697c !important;
+}
+
+span.fancytree-active span.fancytree-title, span.fancytree-selected span.fancytree-title {
+    background-color: transparent !important;
+}
+.fancytree-focused span.fancytree-title {
+    background-color: transparent !important;
+    outline: none !important;
+}
+.custom-node .count {
+    margin-left: 5px;
+}
+
+.fancytree-exp-cl > .fancytree-expander,
+.fancytree-exp-c > .fancytree-expander {
+    background-image: url('/images/chevron-right.png') !important;
+}
+
+.fancytree-exp-el > .fancytree-expander,
+.fancytree-exp-e > .fancytree-expander {
+    background-image: url('/images/chevron-down.png') !important;
+}
+
+span.fancytree-expander {
+    width: 20px !important;
+    height: 20px !important;
+    background-size: 20px 20px !important;
+    cursor: pointer;
+    background-position : right !important;
+    vertical-align: middle !important;
+}
+ul.fancytree-container ul {
+    padding : 0 !important;
+}
+ul[role="group"] ul[role="group"] .fancytree-exp-n .fancytree-expander,
+ul[role="group"] ul[role="group"] .fancytree-exp-nl .fancytree-expander{
+    padding-left: 10px;
+}
+
+ul[role="group"] ul[role="group"] ul[role="group"] .fancytree-exp-nl .fancytree-expander,
+ul[role="group"] ul[role="group"] ul[role="group"] .fancytree-exp-n .fancytree-expander {
+    padding-left: 20px;
+}
+
+ul[role="group"] .fancytree-has-children .fancytree-expander{
+    padding-left: 15px;
+}
+
+#groupTree ul[role="group"] ul[role="group"] .fancytree-has-children .fancytree-expander {
+    padding-left: 30px;
+}
+
+.leftMenu #groupTree,
+.leftMenu #intTree {
+    overflow: auto;
+}
+
+.guIntTotal,
+.intTotal {
+    color: #4293FF;
+}
+.guIntErrCnt,
+.intErrCnt {
+    color: #EC5D58;
+}
+
+span.fancytree-node {
+    min-height: 25px !important;
+    line-height: 25px !important;
+    cursor: pointer;
+}
+.fancytree-title > .comm0,
+.fancytree-title > .comm2,
+.fancytree-title > .comm3,
+.fancytree-title > .comm1 {
+    margin-left: 0;
+    margin-right: 5px;
+}
+.leftMenu #intTree {
+    height: calc(100% - 72px);
+    border-bottom: 1px solid #595959;
+}
+
+.int_comm_state {
+    margin-left: 5px; width: 20px; height: 20px; vertical-align: middle;
+}
+
+.tree-search{
+   background-color: #212121;
+}
+
+.fancytree-treefocus:focus-visible{outline: none;}
+
+.jstree-icon.jstree-themeicon {
+    display: none !important;
+}
+
+.jstree-default .jstree-node {
+    margin-left: 0px !important;
+    padding-left: 10px !important;
+}
+
+.jstree-container-ul > .jstree-node {
+    margin-left: 0 !important;
+    padding-left: 0px !important;
+}
+
+.jstree-default .jstree-wholerow-clicked {
+    background: #2c3e49 !important;
+}
+
+.jstree-container-ul li {
+    color: #dcdcdd !important;
+}
+
+.jstree-default > .jstree-no-dots .jstree-open > .jstree-ocl {
+    filter: brightness(4.0);
+}

BIN
src/main/resources/static/images/chevron-down.png


BIN
src/main/resources/static/images/chevron-right.png


+ 198 - 0
src/main/resources/static/js/common/cvibDetail_new.js

@@ -0,0 +1,198 @@
+//var oneWs = new SockJS("https://"+window.location.hostname+":7443/oneTopic.do");
+// var oneWs = new SockJS("http://"+window.location.host+"/oneTopic.do");
+//var oneWs = new SockJS("http://"+window.location.hostname+":8443/oneTopic.do");
+
+var _CvibNodeArr = [];
+var _CvibNode;
+const urlParams = new URL(location.href).searchParams;
+// setInterval(()=>getCvibList(urlParams.get("nodeId")), 1000);
+let param = urlParams.get("nodeId");
+let _DetailInterval = null;
+
+$(()=>{
+    getCvibList(param);
+})
+
+
+function getCvibList(paramId) {
+    let start = new Date().getTime();
+    clearTimeout(_DetailInterval);
+    $.ajax({
+        method: 'POST',
+        url : '/getCvibDetail.do',
+        headers:
+            {'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}
+        ,
+        data : {
+            nodeId : paramId
+        },
+        success: (res) => {
+            let end = new Date().getTime();
+            let differ = end - start;
+            if ((differ) >= 1000) {
+                differ = 1000;
+            }
+            recvDetailData(res);
+            _DetailInterval = setTimeout(() => getCvibList(paramId), 1000 - differ);
+        },
+        error: (err) => {
+            console.log(err);
+        }
+    })
+}
+
+//
+// oneWs.onopen = function (message) {
+//     wsOpen(message);
+// }
+// oneWs.onmessage = function (message) {
+//     wsOnMessage(message);
+// }
+// oneWs.close = function (message) {
+//     wsClose(message);
+// }
+// oneWs.onerror = function (message) {
+//     wsError(message);
+//     console.log("oneTopic.error: " + message)
+// }
+
+/*
+  웹소켓 접속시 nodeid 를 보낸다.
+ */
+// function doClose() {
+//     console.log("oneTopic.close")
+//     oneWs.close();
+// }
+//
+// function wsOpen(message) {
+//     console.log("oneTopic.send: " +  nodeId)
+//     _Level = 1;
+//     oneWs.send(nodeId);
+// };
+function recvDetailData(recv) {
+    if (!recv) return;
+    recv.turns.sort(dirCodeSort);
+
+    let {
+        nodeId, localDate, errCenter, errCont, errScu, oprBlink, oprInd,
+        oprManual, oprTrans, oprTurnoff, dataCount, counter, commStatus
+    } = recv;
+    if (!commStatus) {
+        errCenter  = 2;
+        errCont    = 2;
+        errScu     = 2;
+        oprBlink   = 2;
+        oprTrans   = 2;
+        oprInd     = 2;
+        oprManual  = 2;
+        oprTurnoff = 2;
+        dataCount  = "-";
+        localDate  = "-";
+        counter    = "-";
+    }
+    errCenter  = transStatusCodeToWord(errCenter, 0);
+    errCont    = transStatusCodeToWord(errCont, 0);
+    errScu     = transStatusCodeToWord(errScu, 0);
+    oprBlink   = transStatusCodeToWord(oprBlink, 1);
+    oprTrans   = transStatusCodeToWord(oprTrans, 1);
+    oprInd     = transStatusCodeToWord(oprInd, 1);
+    oprManual  = transStatusCodeToWord(oprManual, 1);
+    oprTurnoff = transStatusCodeToWord(oprTurnoff, 1);
+
+    let light = []			// 신호등정보 [직진,좌,보행]
+    let dirAdd = [];			//연등지
+    let timeFlag = [];			//시간정보신뢰성
+    let walker = [];			//보행자
+    let unprotected = [];			//비보호 상태
+    let stts = [];			//신호등상태
+    let dispTm = [];						//표출시간
+    let remainTm = [];						//잔여시간
+    let dirCode = []; 						//방향코드
+
+
+    let cvibSttsArr = [];
+
+    for (let i = 0; i <= 80; i = i + 10) {
+        cvibSttsArr[i] = '9_9_9_9';
+    } // 직진_좌회전_보행_유턴
+
+
+    for (let turn of recv.turns) {
+        //( recv[ii].light == 2 && recv[ii].stts == 3 && recv[ii].unprotected == 1 ) ? stts.push(1) : stts.push(recv[ii].stts);
+        stts.push(turn.stts)
+        light.push(turn.light);
+        dirAdd.push(turn.dirAdd);
+        timeFlag.push(turn.timeFlag);
+        walker.push(turn.walker);
+        unprotected.push(turn.unprotected);
+        dispTm.push(turn.dispTm);
+        remainTm.push(turn.remainTm);
+        dirCode.push(turn.dirCode);
+        cvibSttsArr[turn.dirCode] = setCvibSttsFnc(turn.unprotected, turn.light, turn.dirCode, turn.stts, cvibSttsArr[turn.dirCode]);
+    }
+    // if(_CvibNodeArr.length > 0){
+    //     _CvibNode.emptyCvibCircleImg();
+    //     _CvibNode.emptyCvibInfoImg();
+    // }
+
+    _CvibNode = new CvibInfo(nodeId, nodeName, nodeLat, nodeLng, nodeAddr1,
+        nodeAddr2, nodeAddr3, localDate, errCenter, errCont, errScu, oprBlink, oprInd, oprManual, oprTrans, oprTurnoff, dataCount, counter,
+        cvibSttsArr[10], cvibSttsArr[20], cvibSttsArr[30], cvibSttsArr[40], cvibSttsArr[50], cvibSttsArr[60], cvibSttsArr[70], cvibSttsArr[80],
+        light, dirAdd, timeFlag, walker, unprotected, stts, dispTm, remainTm, dirCode);
+
+//    console.log(_CvibNode);
+    // if (_Level == 0 || _Level == 1 && map != null || map != undefined) {
+    _CvibNode.emptyCvibCircleImg();
+    _CvibNode.emptyCvibInfoImg();
+    _CvibNode.setCircle();
+    _CvibNode.setCvibInfo();
+    // }
+    if (cvibDetailInfoTimeId == null) {
+        showCvibDetailInfo();
+    }
+    if (cvibPopTimeId === null && (map === null || map === undefined)) {
+        drawCvibInfo();
+    } else {
+        clearTimeout(cvibPopTimeId);
+    }
+}
+
+function setCvibSttsFnc(unprotected, light, dirCode, stts, cvibStts) {
+    if (cvibStts === undefined || cvibStts === "" || cvibStts === null) {
+        cvibStts = '9_9_9_9';
+    }
+
+    if (light === 1) {
+        var temp = cvibStts.substring(1);
+        cvibStts = stts + temp;
+
+    } else if (light === 2) {
+        var tempS = cvibStts.substring(0, 2);
+        var tempE = cvibStts.substring(3);
+        //stts =(unprotected == 0) ? stts : 1;
+        cvibStts = tempS + stts + tempE;
+    } else if (light === 3) {
+
+        var tempS = cvibStts.substring(0, 4);
+        var tempE = cvibStts.substring(5);
+        cvibStts = tempS + stts + tempE;
+    } else if (light === 7) {
+        var temp = cvibStts.substring(0, 6);
+        cvibStts = temp + stts;
+    }
+    return cvibStts;
+
+}
+
+function dirCodeSort(a, b) {
+    return a.dirCode > b.dirCode ? 1 : a.dirCode === b.dirCode ? 0 : -1;
+}
+
+
+function transStatusCodeToWord(value, option) {
+    const wordArr = [['정상', '이상', '-'], ['OFF', 'ON', '-']];
+    if (value !== undefined && !isNaN(Number(value))) {
+        return wordArr[option][value];
+    }
+    return wordArr[option][2];
+}

+ 64 - 59
src/main/resources/static/js/map.js

@@ -35,6 +35,8 @@ var roadviewMarker = null;
 
 var _ToggleSiginfo = false;
 
+let _CoordOverlay = null;
+
 const stateMap = new Map();
 stateMap.set(0, '#FF0000');
 stateMap.set(1, '#00FF00');
@@ -231,9 +233,7 @@ function init() {
 let coordMarker;
 function mapRightClick(e) {
     if (!$('.road_on')[0]) {
-        if (coordMarker) {
-            coordMarker.close();
-        }
+        emptyCoordMarkers();
         const textArea = $('<textarea>');
         let xCoordinate = e.latLng.getLng().toString();
         let yCoordinate = e.latLng.getLat().toString();
@@ -245,24 +245,19 @@ function mapRightClick(e) {
         if (yCoordinate.length > 10) {
             yCoordinate = yCoordinate.substring(0, 10);
         }
-        const text = xCoordinate + "," + yCoordinate;
+        const text = yCoordinate + "," + xCoordinate;
         textArea.val(text);
         $('body').append(textArea);
         textArea[0].select();
         document.execCommand("copy");
         textArea.remove();
-        coordMarker = new kakao.maps.InfoWindow({
-            map: map,
-            position: e.latLng,
-            content: '<div style="width:150px; text-align: center;">' + text + '</div>',
-        });
+
+        createCoordMarkers(e.latLng, text);
     }
 }
 
 function mapClick(){
-    if (coordMarker) {
-        coordMarker.close();
-    }
+    emptyCoordMarkers();
 }
 
 function getKakaoPosition(y, x) {
@@ -384,7 +379,7 @@ function getRoadView() {
     var roadCheck = $("#road").attr("class");
 
     // 로드뷰 도로 오버레이가 보이게 합니다
-    if (roadCheck == "road_on") {
+    if (roadCheck === "road_on") {
         overlayOn = true,
             container = document.getElementById('container'), // 지도와 로드뷰를 감싸고 있는 div 입니다
             mapWrapper = document.getElementById('mapWrapper'), // 지도를 감싸고 있는 div 입니다
@@ -480,7 +475,8 @@ function getRoadView() {
 
         //지도를 감싸고 있는 div의 크기를 조정하는 함수입니다
         function toggleMapWrapper(active, position) {
-            var widthDiff = wideFlag ? '10' : '338';
+            console.log(wideFlag);
+            var widthDiff = wideFlag ? '10' : '290';
             if (active) {
                 // 지도를 감싸고 있는 div의 너비가 100%가 되도록 class를 변경합니다
                 //container.className = '';
@@ -560,7 +556,7 @@ function getRoadView() {
 
 //로드뷰에서 X버튼을 눌렀을 때 로드뷰를 지도 뒤로 숨기는 함수입니다
 function closeRoadview() {
-    const width = wideFlag ? 'calc(100% - 10px)':'calc(100% - 338px)';
+    const width = wideFlag ? 'calc(100% - 10px)':'calc(100% - 287px)';
     $('#mapWrapper').css('width', width);
     $('#rvWrapper').css('display','none');
 
@@ -568,12 +564,12 @@ function closeRoadview() {
     map.relayout();
 
     // 현재 마커가 놓인 자리의 좌표입니다
-    if(roadviewMarker != null)
-    {
-        var position = roadviewMarker.getPosition();
+    // if(roadviewMarker != null)
+    // {
+        // const position = roadviewMarker.getPosition();
         // 지도의 너비가 변경될 때 지도중심을 입력받은 위치(position)로 설정합니다
-        map.setCenter(position);
-    }
+        // map.setCenter(position);
+    // }
 }
 
 //로드뷰닫기
@@ -1258,28 +1254,28 @@ function drawingAreaStop() {
 //마커초기화
 function markerRemove(temp) {
 
-    var str = "";
-
+    let str = "";
+    const UserAgent = window.navigator.userAgent;
     //로드뷰아이콘
-    if (temp == "road") {
+    if (temp === "road") {
         str = roadMarker;
         roadMarker = [];
     }
 
-    if (temp == "search") {
+    if (temp === "search") {
 
         str = searchMarker;
         searchMarker = [];
     }
-    if (temp == "infowindow") {
+    if (temp === "infowindow") {
 
         str = searchInfoWindow;
         searchInfoWindow = [];
     }
-    if (temp == "ims") {
+    if (temp === "ims") {
         str = imsMarker;
         imsMarker = [];
-        var UserAgent = window.navigator.userAgent;
+        // let UserAgent = window.navigator.userAgent;
         if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
 
         } else {
@@ -1287,16 +1283,16 @@ function markerRemove(temp) {
         }
     }
 
-    if (temp == "imsLink") {
+    if (temp === "imsLink") {
         str = linkLineArr;
         linkLineArr = [];
     }
 
-    if (temp == "trouble") {
+    if (temp === "trouble") {
 
         str = troubleMarker;
         troubleMarker = [];
-        var UserAgent = window.navigator.userAgent;
+        // let UserAgent = window.navigator.userAgent;
         if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
 
         } else {
@@ -1458,47 +1454,56 @@ function toggleSiginfo() {
     } else _ToggleSiginfo = false;
     getSignalInfo();
 }
+function emptyCoordMarkers() {
+    if (coordMarker) {
+        coordMarker.close();
+        coordMarker = null;
+    }
+    if (_CoordOverlay) {
+        _CoordOverlay.setMap(null);
+    }
+}
 
+function createCoordMarkers(latLng, text) {
+
+    _CoordOverlay = new kakao.maps.Marker({
+        position: latLng
+    });
+
+    _CoordOverlay.setMap(map);
+
+    coordMarker = new kakao.maps.InfoWindow({
+        position: latLng,
+        content: '<div style="padding: 5px; font-weight: bold;">'+ text +'</div>'
+    })
+    coordMarker.open(map, _CoordOverlay);
+}
 
 /****************************************************************************
  * 좌표검색
  ****************************************************************************/
-function moveToPosition() {
-    const $lctnX = $('#lctn_x_coord');
-    const $lctnY = $('#lctn_y_coord');
-    var xCrdn = $lctnX.val();
-    var yCrdn = $lctnY.val();
-    if (!xCrdn) {
-        alert("X 좌표 값을 입력해주세요.");
-        return $lctnX.focus();
+function moveToPosition(e) {
+    if (e && e.keyCode !== 13) return;
+    emptyCoordMarkers();
+    const coordinates =  $('#lctn_coord');
+    if (!coordinates.val()) {
+        alert('좌표값을 입력해주세요.');
+        return coordinates.focus();
     }
-
-    if (!yCrdn) {
-        alert("Y 좌표 값을 입력해주세요.");
-        return $lctnY.focus();
+    const coordArr = coordinates.val().split(',');
+    if (coordArr.length !== 2 || isNaN(Number(coordArr[0].trim())) || isNaN(Number(coordArr[1].trim())))  {
+        alert('좌표값 형식에 맞게 입력해주세요.');
     }
 
-    if (isNaN(Number(xCrdn)) || xCrdn.length < 3) {
-        alert("X 좌표 : "+xCrdn+"\n부적절한 좌표값입니다. 다시 입력해주세요.");
-        return $lctnX.focus();
-    }
+    const lat  = Number(coordArr[0].trim());
+    const lng  = Number(coordArr[1].trim());
 
-    if (isNaN(Number(yCrdn)) || yCrdn.lenth < 2) {
-        alert("Y 좌표 : "+yCrdn+"\n부적절한 좌표값입니다. 다시 입력해주세요.");
-        return $lctnY.focus();
-    }
-
-    if (!xCrdn.includes('.')) {
-        xCrdn = xCrdn.substring(0, 3) + '.' + xCrdn.substring(3, xCrdn.length);
-    }
-
-    if (!yCrdn.includes('.')) {
-        yCrdn = yCrdn.substring(0, 2) + '.' + yCrdn.substring(2, yCrdn.length);
-    }
-    const position = new kakao.maps.LatLng(yCrdn, xCrdn);
+    const position = new kakao.maps.LatLng(lat, lng);
     map.setCenter(position);
     map.setLevel(3);
     mapZoomBound();
+    createCoordMarkers(position, lat + ','+ lng);
+
 }
 
 

+ 18 - 19
src/main/resources/static/js/signal.js

@@ -178,19 +178,19 @@ function getSignalInfoCallback(json) {
             if(el.nodeId == nodeId){
                 tmpHtml = '<tr id="'+nodeId+'">' +
                 '<td style="width:5%">' + addr1 + '</td>' +
-                '<td style="width:15%"><a style="margin: auto;" href="javascript:goPanToInt(\'' + nodeId + '\',' + lng + ',' + lat + ')">' + nodeId + '</a></td>' +
-                '<td style="width:15%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
+                '<td style="width:10%"><a style="margin: auto;" href="javascript:goPanToInt(\'' + nodeId + '\',' + lng + ',' + lat + ')">' + nodeId + '</a></td>' +
+                '<td style="width:20%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
                 /*'<td style="width:10%">'+el.date+'</td>'+*/
-                '<td style="width:3%" id="oprTrans">'+el.oprTrans+'</td>' +
-                '<td style="width:3%" id="oprInd">'+el.oprInd+'</td>' +
-                '<td style="width:3%" id="oprTurnoff">'+el.oprTurnoff+'</td>' +
-                '<td style="width:3%" id="oprBlink">'+el.oprBlink+'</td>' +
-                '<td style="width:3%" id="oprManual">'+el.oprManual+'</td>' +
+                '<td style="width:5%" id="oprTrans">'+el.oprTrans+'</td>' +
+                '<td style="width:5%" id="oprInd">'+el.oprInd+'</td>' +
+                '<td style="width:5%" id="oprTurnoff">'+el.oprTurnoff+'</td>' +
+                '<td style="width:5%" id="oprBlink">'+el.oprBlink+'</td>' +
+                '<td style="width:5%" id="oprManual">'+el.oprManual+'</td>' +
                 '<td style="width:7%" id="errScu">'+el.errScu+'</td>' +
                 '<td style="width:7%" id="errCenter">'+el.errCenter+'</td>' +
                 '<td style="width:7%" id="errCont">'+el.errCont+'</td>' +
                 '<td style="width:7%" id="counter">'+el.counter+'</td>'+
-                '<td style="width:10%" id="date">'+el.date+'</td>' +
+                '<td style="width:7%" id="date">'+el.date+'</td>' +
                 '</tr>';
             }
         });
@@ -199,18 +199,18 @@ function getSignalInfoCallback(json) {
             tmpHtml = '<tr id="'+nodeId+'">' +
             '<td style="width:5%">' + addr1 + '</td>' +
             '<td style="width:15%"><a style="margin: auto;" href="javascript:goPanToInt(\'' + nodeId + '\',' + lng + ',' + lat + ')">' + nodeId + '</a></td>' +
-            '<td style="width:15%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
+            '<td style="width:20%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
             /*'<td style="width:10%">'+el.date+'</td>'+*/
-            '<td style="width:3%" id="oprTrans">-</td>' +
-            '<td style="width:3%" id="oprInd">-</td>' +
-            '<td style="width:3%" id="oprTurnoff">-</td>' +
-            '<td style="width:3%" id="oprBlink">-</td>' +
-            '<td style="width:3%" id="oprManual">-</td>' +
+            '<td style="width:5%" id="oprTrans">-</td>' +
+            '<td style="width:5%" id="oprInd">-</td>' +
+            '<td style="width:5%" id="oprTurnoff">-</td>' +
+            '<td style="width:5%" id="oprBlink">-</td>' +
+            '<td style="width:5%" id="oprManual">-</td>' +
             '<td style="width:7%" id="errScu">-</td>' +
             '<td style="width:7%" id="errCenter">-</td>' +
             '<td style="width:7%" id="errCont">-</td>' +
             '<td style="width:7%" id="counter">-</td>'+
-            '<td style="width:10%" id="date">-</td>' +
+            '<td style="width:7%" id="date">-</td>' +
             '</tr>';
         }
 
@@ -301,7 +301,7 @@ function SignalLinkDraw(ALink, BLink) {
  */
 function Signal(nodeId, name, lat, lng, addr1, addr2, addr3) {
     this.initialize(nodeId, name, lat, lng, addr1, addr2, addr3);
-};
+}
 
 Signal.prototype = {
     Lat: 0,
@@ -366,12 +366,11 @@ Signal.prototype = {
                 customOverlay = null;
             }
         });
-
+        const mapCircle = this.mapCircle;
         kakao.maps.event.addListener(this.mapCircle, 'click', function () {
-            var url = 'cvibInfoDetail.do?nodeId=' + nodeId;
+            const url = 'cvibInfoDetail.do?nodeId=' + nodeId;
             var options = 'scrollbars=no,toolbar=no,location=no,resizable=no,status=no,menubar=no,width=1300px,height=590px,left=0,top=0';
             _CvibInfoDetailPop = window.open(url, 'cvibChild', options);
-
         });
 
 

+ 4 - 3
src/main/resources/static/js/signalInfo.js

@@ -3,12 +3,12 @@
 
  */
 
-function SignalInfo(nodeId, name, lat, lng, addr1, addr2, addr3) {
-    this.initialize(nodeId, name, lat, lng, addr1, addr2, addr3);
+function SignalInfo(nodeId, name, lat, lng, addr1, addr2, addr3, regionId) {
+    this.initialize(nodeId, name, lat, lng, addr1, addr2, addr3, regionId);
 };
 
 SignalInfo.prototype = {
-    initialize: function (nodeId, name, lat, lng, addr1, addr2, addr3) {
+    initialize: function (nodeId, name, lat, lng, addr1, addr2, addr3, regionId) {
         this.nodeId = nodeId;
         this.name = name;
         this.addr1 = addr1;
@@ -18,5 +18,6 @@ SignalInfo.prototype = {
         this.Lng = lng;
         this.status = 0;    // 0 데이터없음 1 정상 2 이상
         this.date = null;   //교차로시각
+        this.regionId = regionId;
     },
 };

+ 387 - 0
src/main/resources/static/js/signal_new.js

@@ -0,0 +1,387 @@
+/*
+ * 전체 신호제어기를 메모리에 저장해서 등록함(Bean 으로 등록하면 됨)
+ */
+var _mapSignal = new HashMap();
+var sigInfowindow = null;
+var cvibMarkerArr = [];
+var signalArr = [];
+var _Flag = null;
+var siginfoPhaseDetailWin = null;
+var _CvibObjArr = [];
+var _CvibCircleImgArr = [];
+var _CvibOutCircleImgArr = [];
+var _CvibInfoImgArr = [];
+var _CvibInfoCWImgArr = [];
+var _CvibInfoULImgArr = [];
+var _CvibInfoUtunImgArr = [];
+var _CvibTimeId = null;
+var overLayData;
+var _CustomOverlayArr = [];
+
+
+function mapZoomBound() {
+
+    _Level = map.getLevel();
+    getSignalInfo();
+    if (_Level === 0 || _Level === 1) {
+        // _CvibNodeArr.forEach(function (el) {
+        //     el.allDrawCircle();
+        //     el.allDrawCvib();
+        // });
+        
+        signalArr.forEach(function (el) {
+            if (el.mapCircle) {
+                el.mapCircle.setMap(null);
+            }
+        });
+
+    } else {
+        _CvibNodeArr.forEach(function (el) {
+            el.emptyCvibCircleImg();
+            el.emptyCvibInfoImg();
+        });
+        if (customOverlay != null) {
+            customOverlayFnc(overLayData.name, overLayData.lng, overLayData.lat);
+        }
+        //_CvibNodeArr = []
+    }
+
+    if (_Level === 1 || _Level === 2) {
+        if (customOverlay) {
+            customOverlay.setMap(null);
+        }
+    }
+
+    if (_EmergencyMap && _EmergencyMap.size) {
+        _EmergencyMap.forEach((obj)=>{
+            obj.road.setLineWidth();
+            if (obj.sig && obj.sig.size) {
+                obj.sig.forEach((sig)=>{
+                   sig.setLineWidth();
+                })
+            }
+        });
+    }
+
+    const history = $('#iframeTreeList').get(0).contentWindow._historyMap;
+    if (history && history.size) {
+        history.forEach((obj)=>{
+            if (obj.get('draw')) {
+                obj.get('draw').road.setLineWidth();
+                if (obj.get('draw').sig && obj.get('draw').sig.size) {
+                    obj.get('draw').sig.forEach((sig)=>{
+                        sig.setLineWidth();
+                    })
+                }
+            }
+        });
+    }
+}
+
+function mapMoveBound() {
+   // _CvibNodeArr = [];
+    getSignalInfo();
+    // _CvibNodeArr.forEach(function (el) {
+    //     el.emptyCvibCircleImg();
+    //     el.emptyCvibInfoImg();
+    // });
+    if(_Level == 0 || _Level == 1){
+        _CvibNodeArr.forEach(function (el) {
+            for (var ii = 0; ii < signalArr.length; ii++) {
+                if(el.nodeId == signalArr[ii].nodeId){
+                    el.emptyCvibCircleImg();
+                    el.emptyCvibInfoImg();
+                }
+            }
+        });
+        _CvibNodeArr.forEach(function (el) {
+            for (var ii = 0; ii < signalArr.length; ii++) {
+                if(el.nodeId == signalArr[ii].nodeId){
+                    
+                    el.setCircle();
+                    el.setCvibInfo();
+                }
+            }
+
+        });
+    } else {
+        _CvibNodeArr.forEach(function (el) {
+            for (var ii = 0; ii < signalArr.length; ii++) {
+                if(el.nodeId == signalArr[ii].nodeId){
+                    el.emptyCvibCircleImg();
+                    el.emptyCvibInfoImg();
+                }
+            }
+        });
+        if (customOverlay != null) {
+
+            customOverlayFnc(overLayData.name, overLayData.lng, overLayData.lat);
+        }
+        //_CvibNodeArr = []
+    }
+}
+
+function emptySignalArr(arr) {
+    if (arr != null) {
+        arr.forEach(function (el) {
+            el.setMap(null);
+        });
+        arr = 0;
+    }
+}
+
+
+function getSignalInfo() {
+    var url = 'getSignalInfo.do';
+    var param =
+        '&minX=' + map.getBounds().getSouthWest().getLng() +
+        '&minY=' + map.getBounds().getSouthWest().getLat() +
+        '&maxX=' + map.getBounds().getNorthEast().getLng() +
+        '&maxY=' + map.getBounds().getNorthEast().getLat();
+
+    if (_CustomOverlayArr.length > 0) {
+        _CustomOverlayArr.forEach((obj)=>{
+            obj.setMap(null);
+        })
+        _CustomOverlayArr = [];
+    }
+    requestService(url, param, getSignalInfoCallback);
+}
+
+function getSignalInfoCallback(json) {
+    signalArr = [];
+
+    var data = json;
+
+     cvibMarkerArr.forEach(function (el) {
+        if (el.getMap() != null) {
+            el.setMap(null);
+        }
+    });
+
+    var bottomCvibInfoList = "";
+
+    cvibMarkerArr = [];
+    //cvibMarkerArr =[];
+    for (var ii = 0; ii < data.length; ii++) {
+        //if(data[ii].addr1 == null||data[ii].addr1=='-')continue;
+        var nodeId = data[ii].nodeid;
+        var addr1 = data[ii].addr1;
+        var addr2 = data[ii].addr2;
+        var addr3 = data[ii].addr3;
+        var lat = data[ii].lat;
+        var lng = data[ii].lng;
+        var name = data[ii].name;
+        var tmpHtml="";
+
+        _CvibNodeArr.forEach(function(el) {
+            if(el.nodeId == nodeId){
+                tmpHtml = '<tr id="'+nodeId+'">' +
+                '<td style="width:10%">' + addr1 + '</td>' +
+                '<td style="width:10%"><a style="margin: auto;" href="javascript:goPanToInt(\'' + nodeId + '\',' + lng + ',' + lat + ')">' + nodeId + '</a></td>' +
+                '<td style="width:15%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
+                /*'<td style="width:10%">'+el.date+'</td>'+*/
+                '<td style="width:5%" id="oprTrans">'+el.oprTrans+'</td>' +
+                '<td style="width:5%" id="oprInd">'+el.oprInd+'</td>' +
+                '<td style="width:5%" id="oprTurnoff">'+el.oprTurnoff+'</td>' +
+                '<td style="width:5%" id="oprBlink">'+el.oprBlink+'</td>' +
+                '<td style="width:5%" id="oprManual">'+el.oprManual+'</td>' +
+                '<td style="width:7%" id="errScu">'+el.errScu+'</td>' +
+                '<td style="width:7%" id="errCenter">'+el.errCenter+'</td>' +
+                '<td style="width:7%" id="errCont">'+el.errCont+'</td>' +
+                '<td style="width:7%" id="counter">'+el.counter+'</td>'+
+                '<td style="width:7%" id="date">'+el.date+'</td>' +
+                '</tr>';
+            }
+        });
+        
+        if (tmpHtml == "") {
+            tmpHtml = '<tr id="'+nodeId+'">' +
+            '<td style="width:10%">' + addr1 + '</td>' +
+            '<td style="width:7%"><a style="margin: auto;" href="javascript:goPanToInt(\'' + nodeId + '\',' + lng + ',' + lat + ')">' + nodeId + '</a></td>' +
+            '<td style="width:15%"><a href="javascript:goPanToInt(\'' + name + '\',' + lng + ',' + lat + ')">' + name + '</a></td>' +
+            /*'<td style="width:10%">'+el.date+'</td>'+*/
+            '<td style="width:5%" id="oprTrans">-</td>' +
+            '<td style="width:5%" id="oprInd">-</td>' +
+            '<td style="width:5%" id="oprTurnoff">-</td>' +
+            '<td style="width:5%" id="oprBlink">-</td>' +
+            '<td style="width:5%" id="oprManual">-</td>' +
+            '<td style="width:7%" id="errScu">-</td>' +
+            '<td style="width:7%" id="errCenter">-</td>' +
+            '<td style="width:7%" id="errCont">-</td>' +
+            '<td style="width:7%" id="counter">-</td>'+
+            '<td style="width:15%" id="date">-</td>' +
+            '</tr>';
+        }
+
+        var signal = new Signal(nodeId, name, lat, lng, addr1, addr2, addr3);
+        signalArr.push(signal);
+
+        if (_Level === 1 || _Level === 2) {
+            let content = '<div class ="sigCustom"><span class="left"></span>';
+            let title = '<span class="center">' + name + '</span>';
+
+            if (customOverlay && overLayData.lat === lat && overLayData.lng === lng) {
+                title = '<span class="center" style="font-weight: bold; color: #3396ff;">' + name + '</span>'
+            }
+            content += title + '<span class="right"></span></div>';
+
+            // 커스텀 오버레이가 표시될 위치입니다
+            let position = new kakao.maps.LatLng(lat, lng);
+            // 커스텀 오버레이를 생성합니다
+            let overlay = new kakao.maps.CustomOverlay({
+                position: position,
+                content: content,
+                zIndex: 100
+            });
+
+            overlay.setMap(map);
+
+            // $('.sigCustom' + _Level + '').parent().css('width', '0px');
+            $('.sigCustom').parent().css('width', '0px');
+            _CustomOverlayArr.push(overlay);
+        }
+
+        bottomCvibInfoList += tmpHtml;
+    }
+
+    signalArr.forEach(function (el) {
+        if (el.mapCircle.getMap() == null) {
+            el.mapCircle.setMap(map);
+        }
+        //if (_Level < 2) el.mapCircle.setMap(null);
+    })
+
+    // 하단 리스트 클리어
+    // 하단 리스트 초기작업
+    $('#iframeBottomList').contents().find('#cvibBottomInfo').html(bottomCvibInfoList);
+}
+
+/*function drawSignal(signal) {
+	if(sigInfowindow != null) sigInfowindow.close();
+}*/
+
+function findSignal(Id) {
+    return _mapSignal.get(Id);
+};
+
+function addSignal(Id, Obj) {
+    _mapSignal.put(Id, Obj);
+    return _mapSignal.size();
+};
+
+
+//#define RadToDeg(x) (57.29577951 * x)
+//#define DegToRad(x) (0.017453293 * x)
+Number.prototype.toRadians = function () {
+    return this * 0.017453293;
+    //return this * Math.PI / 180;
+};
+Number.prototype.toDegrees = function () {
+    return this * 57.29577951;
+    //return this * 180 / Math.PI;
+};
+
+/*
+ * 좌표를 저장하는 Class
+ * Lat(Y) = 37.564974, Lng(X) = 126.978517;
+ * kakao.maps.LatLng(Lat, Lng)
+ */
+function SCoord(Lat, Lng) {
+    this.Lat = parseFloat(Lat);
+    this.Lng = parseFloat(Lng);
+};
+
+function SignalLinkDraw(ALink, BLink) {
+    this.initialize(ALink, BLink);
+}
+
+/*
+ * 신호제어기 정보
+ */
+function Signal(nodeId, name, lat, lng, addr1, addr2, addr3) {
+    this.initialize(nodeId, name, lat, lng, addr1, addr2, addr3);
+};
+
+Signal.prototype = {
+    Lat: 0,
+    Lng: 0,
+
+    drawSignal: null,	/* 현재 표출될 SignalDraw class */
+    listDrawSignal: null, 	/* 표출현시 목록을 저장할 list array: 최초 한번만 좌표를 계산하도록 하기 위함, 성능에 따라 map 사용 가능 */
+
+    mapCircle: null,
+    initialize: function (nodeId, name, lat, lng, addr1, addr2, addr3) {
+        this.nodeId = nodeId;
+        this.name = name;
+        this.addr1 = addr1;
+        this.addr2 = addr2;
+        this.addr3 = addr3;
+        this.Lat = lat;
+        this.Lng = lng;
+        this.listDrawSignal = [];
+        var markerSize = 0;
+        if (_Level >= 5) {
+            markerSize = 10;
+        } else if (_Level >= 3) {
+            markerSize = 20;
+        } else {
+            markerSize = 20;
+        }
+        let offset = markerSize / 2;
+        var icon = new kakao.maps.MarkerImage(
+            // 'images/TrafficLight25.png',
+            'images/sig6.png',
+            new kakao.maps.Size(markerSize * 3, markerSize),
+            {
+
+                alt: "신호현시",
+                offset: new kakao.maps.Point(offset * 3, offset)
+            },
+
+        );
+
+        this.mapCircle = new kakao.maps.Marker({
+            position: new kakao.maps.LatLng(lat, lng),
+            image: icon,
+            zIndex: 10
+        });
+
+        if (_Level < 7) {
+            this.mapCircle.setMap(map);
+        }
+
+        cvibMarkerArr.push(this.mapCircle);
+        kakao.maps.event.addListener(this.mapCircle, 'mouseover', function () {
+            //신호 이미지 마우스올렷을때 [이름 생성]
+            if (_Level !== 1 && _Level !== 2) {
+                customOverlayFnc(name, lng, lat);
+            }
+        });
+
+        kakao.maps.event.addListener(this.mapCircle, 'mouseout', function () {
+            // 신호 이미지 마우스아웃시 [이름 사라짐]
+            if (customOverlay && _Level !== 1 && _Level !== 2) {
+                customOverlay.setMap(null);
+                customOverlay = null;
+            }
+        });
+        const mapCircle = this.mapCircle;
+        kakao.maps.event.addListener(this.mapCircle, 'click', function () {
+            const url = 'cvibInfoDetail.do?nodeId=' + nodeId;
+            // const options = 'scrollbars=no,toolbar=no,location=no,resizable=no,status=no,menubar=no,width=1300px,height=590px,left=0,top=0';
+            // _CvibInfoDetailPop = window.open(url, 'cvibChild', options);
+            const modal = `<iframe src="${url}" id="iframeModal" class="iframeModal" title="상세 목록"></iframe>`;
+            const modalBox = $('.modal-box .modal-container');
+            $('.modal-title').text(name + '-' + nodeId);
+            modalBox.html(modal);
+            $('.modal-box').show();
+        });
+
+
+    },
+
+    empty: function (map) {
+        if (this.drawSignal.mapCircle != null) this.drawSignal.mapCircle.setMap(null);
+    }
+};

+ 19 - 22
src/main/webapp/WEB-INF/jsp/bottomListFrame.jsp

@@ -6,9 +6,9 @@
 <html lang="ko">
 <head>
 
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/common.css">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/main.css">
-    <link rel="stylesheet" href="${contextRoot }/css/themes/default/style.css">
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main.css">
+    <link rel="stylesheet" href="/css/themes/default/style.css">
 
     <%@ include file="/WEB-INF/jsp/common.jsp" %>
     <title>상세 정보</title>
@@ -19,22 +19,19 @@
         <table id="cvibBottomBodyTable">
             <thead>
                 <tr>
-                    <th style="width:5%">센터명</th>
-                    <th style="width:15%">제어기번호</th>
-                    <th style="width:15%">교차로명</th>
-                    <!-- <th style="width:10%">수집일시</th> -->
-                    <th style="width:3%">전이</th>
-                    <th style="width:3%">감응</th>
-                    <th style="width:3%">소등</th>
-                    <th style="width:3%">점멸</th>
-                    <th style="width:3%">수동</th>
-                    <!-- 						<th style="width:7%">BUS이상</th>
-                                            <th style="width:7%">DB이상</th> -->
-                    <th style="width:7%">SCU통신이상</th>
-                    <th style="width:7%">센터통신이상</th>
-                    <th style="width:7%">모순이상</th>
-                    <th style="width:7%">주기카운터</th>
-                    <th style="width:10%">교차로시각</th>
+                    <th style="width:10%;">센터명</th>
+                    <th style="width:15%;">제어기번호</th>
+                    <th style="width:15%;">교차로명</th>
+                    <th style="width:5%;">전이</th>
+                    <th style="width:5%;">감응</th>
+                    <th style="width:5%;">소등</th>
+                    <th style="width:5%;">점멸</th>
+                    <th style="width:5%;">수동</th>
+                    <th style="width:7%;">SCU통신이상</th>
+                    <th style="width:7%;">센터통신이상</th>
+                    <th style="width:7%;">모순이상</th>
+                    <th style="width:7%;">주기카운터</th>
+                    <th style="width:7%;">교차로시각</th>
                 </tr>
             </thead>
             <tbody id="cvibBottomInfo"></tbody>
@@ -57,9 +54,9 @@
         </table>
     </div>
 </div>
-<script src="${contextRoot }/js/common/hashmap.js"></script>
-<script src="${contextRoot }/js/common/common-all.js"></script>
-<script src="${contextRoot }/js/common/common.js"></script>
+<script src="/js/common/hashmap.js"></script>
+<script src="/js/common/common-all.js"></script>
+<script src="/js/common/common.js"></script>
 
 <script>
 

+ 118 - 0
src/main/webapp/WEB-INF/jsp/bottomListFrame_new.jsp

@@ -0,0 +1,118 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ page session="false" %>
+<!doctype html>
+<html lang="ko" style="background-color: #2c2c2c;">
+<head>
+
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main_new.css">
+    <link rel="stylesheet" href="/css/themes/default/style.css">
+
+    <%@ include file="/WEB-INF/jsp/common.jsp" %>
+    <title>상세 정보</title>
+</head>
+<body class="sang">
+<div class="bottomMenu" id="bottomMenu">
+    <div class="bottomBody" id="cvibBottomBody">
+        <table id="cvibBottomBodyTable">
+            <thead>
+                <tr>
+                    <th style="width:10%;">센터명</th>
+                    <th style="width:7%;">제어기번호</th>
+                    <th style="width:15%;">교차로명</th>
+                    <th style="width:5%;">전이</th>
+                    <th style="width:5%;">감응</th>
+                    <th style="width:5%;">소등</th>
+                    <th style="width:5%;">점멸</th>
+                    <th style="width:5%;">수동</th>
+                    <th style="width:7%;">SCU통신이상</th>
+                    <th style="width:7%;">센터통신이상</th>
+                    <th style="width:7%;">모순이상</th>
+                    <th style="width:7%;">주기카운터</th>
+                    <th style="width:15%;">교차로시각</th>
+                </tr>
+            </thead>
+            <tbody id="cvibBottomInfo"></tbody>
+        </table>
+        <table id="evpBottomBodyTable" style="display: none;">
+            <thead>
+                <tr>
+                    <th style="width:10%">수집시각</th>
+                    <th style="width:10%">지역센터명</th>
+                    <th style="width:15%">서비스 ID</th>
+                    <th style="width:15%">서비스명</th>
+                    <th style="width:10%">긴급차량번호</th>
+                    <th style="width:10%">X 좌표</th>
+                    <th style="width:10%">Y 좌표</th>
+                    <th style="width:10%">속도(km/h)</th>
+                    <th style="width:10%">남은거리(m)</th>
+                </tr>
+            </thead>
+            <tbody id="evpBottomInfo"></tbody>
+        </table>
+    </div>
+</div>
+<script src="/js/common/hashmap.js"></script>
+<script src="/js/common/common-all.js"></script>
+<script src="/js/common/common.js"></script>
+
+<script>
+
+    function goPanToInt(intNm, lng, lat) {
+        if (parent._Level > 3) parent.map.setLevel(1);
+        parent.map.panTo(new parent.kakao.maps.LatLng(lat, lng));
+
+        setTimeout(
+            function () {
+                parent.getSignalInfo();
+                parent.customOverlayFnc(intNm, lng, lat);
+            }, 100 * 2
+        );
+    }
+
+    /* function showOnlineList() {
+        $('#onlineToggle').attr('src','/images/online_on.png');
+        $('#offlineToggle').attr('src','/images/offline_off.png');
+        $('#cvibBottomHead').hide();
+        $('#cvibBottomBody').hide();
+        $('#bottomHead').show();
+        $('#bottomBody').show();
+    } */
+
+    function showOfflineList() {
+        $('#offlineToggle').attr('src', '/images/offline_on.png');
+        $('#onlineToggle').attr('src', '/images/online_off.png');
+        $('#bottomHead').hide();
+        $('#bottomBody').hide();
+        $('#cvibBottomHead').show();
+        $('#cvibBottomBody').show();
+    }
+
+    function moveLocation(serviceId, el) {
+        const treeFrame = $('#iframeTreeList', parent.document);
+        if (!treeFrame.contents().find('#play').hasClass('on')) {
+            return;
+        }
+
+
+        $('tr.on').removeClass('on');
+        $(el).addClass('on');
+        const map = treeFrame.get(0).contentWindow._historyMap.get(serviceId.toString());
+        const idx = $(el).index();
+        //$('#cvibBottomBody').scrollTop(32 * idx);
+        if (map) {
+           const obj  = map.get('obj');
+           const draw = map.get('draw');
+
+           const {cur_lat, cur_lng} = obj.event_list[idx];
+           let position = window.parent.getKakaoPosition(cur_lat, cur_lng);
+           draw.car.moveMarker(position);
+           treeFrame.get(0).contentWindow.setSigState(obj.phase_list[idx], draw);
+        }
+
+    }
+</script>
+</body>
+</html>

+ 1 - 1
src/main/webapp/WEB-INF/jsp/common.jsp

@@ -1 +1 @@
-<%@ page import="org.springframework.core.env.Environment" %>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>

<%--<%--%>
<%--    //Context Root--%>
<%--    String contextRoot = request.getContextPath().equals("/") ? "" : request.getContextPath();--%>
<%--%>--%>

<%--<!-- 기본변수 -->--%>
<%--<c:set var="contextRoot" value="<%=contextRoot%>"/>--%>

<script>
    var CONTEXTROOT = '${contextRoot }';
    var _CONTEXTROOT = '${contextRoot }'.replace('/', '');
    var BTN_IMAGE_PATH = CONTEXTROOT + "/images/";
    var _signalInfoArr = [];
</script>
<base href="/">
<!--  서울/원주 키 -->
<!--
-->
<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=${kakaokey}&libraries=services,clusterer,drawing"></script>
<!--  localhost 키

<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=00c3f7d32c1f1822dc98ad72e35e0223&libraries=services,clusterer,drawing"></script>
-->
+<%@ page import="org.springframework.core.env.Environment" %>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>

<script>
    var BTN_IMAGE_PATH = "/images/";
    var _signalInfoArr = [];
</script>
<base href="/">
<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=${kakaokey}&libraries=services,clusterer,drawing"></script>

+ 10 - 8
src/main/webapp/WEB-INF/jsp/cvibInfoDetail.jsp

@@ -11,16 +11,16 @@
 
     <%@ include file="/WEB-INF/jsp/common.jsp" %>
 
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/common.css">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/main.css">
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main.css">
     <link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
     <script src="js/common/jquery-1.9.1.js"></script>
 
     <title>${nodeId} 신호정보</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
-    <script src="${contextRoot }/js/cvib.js"></script>
-    <script src="${contextRoot }/js/common/cvibDetail.js"></script>
-<%--    <script src="${contextRoot }/js/common/oneTopicDetailSocket.js"></script>--%>
+    <script src="/js/cvib.js"></script>
+    <script src="/js/common/cvibDetail.js"></script>
+<%--    <script src="/js/common/oneTopicDetailSocket.js"></script>--%>
 </head>
 <body class="sang">
 <script>
@@ -259,6 +259,7 @@
             else if (_CvibNode.dirCode[ii] === 60) dirCodeTxt = '남동';
             else if (_CvibNode.dirCode[ii] === 70) dirCodeTxt = '남서';
             else if (_CvibNode.dirCode[ii] === 80) dirCodeTxt = '북서';
+            else if (_CvibNode.dirCode[ii] === 99) dirCodeTxt = '보조등';
             else dirCodeTxt = '미지정';
 
 
@@ -584,9 +585,10 @@
     </div>
 </div>
 
-<script src="${contextRoot }/js/common/common-all.js"></script>
-<script src="${contextRoot }/js/signal.js"></script>
-<script src="${contextRoot }/js/cvib.js"></script>
+<script src="/js/common/common-all.js"></script>
+<%--<script src="/js/signal.js"></script>--%>
+<script src="/js/signal_new.js"></script>
+<script src="/js/cvib.js"></script>
 
 
 <script>

+ 597 - 0
src/main/webapp/WEB-INF/jsp/cvibInfoDetail_new.jsp

@@ -0,0 +1,597 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ page session="false" %>
+<!doctype html>
+<html lang="ko">
+<head>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="Description" content="사이트설명">
+    <meta name="Keywords" content="키워드">
+
+    <%@ include file="/WEB-INF/jsp/common.jsp" %>
+
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main_new.css">
+    <link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
+    <script src="js/common/jquery-1.9.1.js"></script>
+
+    <title>${nodeId} 신호정보</title>
+    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
+    <script src="/js/cvib.js"></script>
+    <script src="/js/common/cvibDetail_new.js"></script>
+<%--    <script src="/js/common/oneTopicDetailSocket.js"></script>--%>
+</head>
+<body class="sang" style="overflow: auto;">
+<script>
+    var screenWidth = window.innerWidth;
+    var nodeName = "${node.name}";
+    var nodeLat = ${node.lat};
+    var nodeLng = ${node.lng};
+    var nodeAddr1 = "${node.addr1}";
+    var nodeAddr2 = "${node.addr2}";
+    var nodeAddr3 = "${node.addr3}";
+    var nodeId = ${nodeId};
+    var cvibPopTimeId = null;
+    var cvibDetailInfoTimeId = null;
+    var lunprotectedArr = [];
+    var uunprotectedArr = [];
+
+    function drawCvibInfo() {
+        if (cvibPopTimeId != null) clearTimeout(cvibPopTimeId);
+        var cvibMap = '';
+        var cwlightpos, sigFixedLeft, sigFixedTop, walkerFixedL, walkerFixedT,walkerPos;
+        walkerPos = [180,270,0,90];
+        if (screenWidth > 1000) {
+            cwlightpos = [180, 270, 0, 90, 45, 315, 225, 135];
+            sigFixedLeft = [13, 1, 19.5, 31, 0, 0, 0, 0];
+            sigFixedTop = [88, 36, 4, 56, 0, 0, 0, 0];
+
+            walkerFixedL = [8, 9, 28, 27.2, 0, 0, 0, 0];
+            walkerFixedT = [68, 13, 14, 70, 0, 0, 0, 0];
+        } else {
+            cwlightpos = [180, 270, 0, 90, 45, 315, 225, 135];
+            sigFixedLeft = [40, 25, 50, 65, 0, 0, 0, 0];
+            sigFixedTop = [90, 35, 3, 58, 0, 0, 0, 0];
+            walkerFixedL = [350, 360, 600, 600, 0, 0, 0, 0];
+            walkerFixedT = [340, 100, 100, 340, 0, 0, 0, 0];
+        }
+        var dataCount = _CvibNode.dataCount;
+        var leftUnprotect = false;
+
+        /*
+
+        */
+        for (var ii = 0; ii < _CvibNode.dataCount; ii++) {
+            if (_CvibNode.dirCode[ii] > 40) break;
+            if (_CvibNode.light[ii] == 2 && _CvibNode.unprotected[ii] == 1) {
+                var dirCodeIndex = (_CvibNode.dirCode[ii] / 10) - 1;
+                lunprotectedArr[dirCodeIndex] = _CvibNode.unprotected[ii];
+            }
+            if (_CvibNode.stts[ii] == 7 && _CvibNode.unprotected[ii] == 1) {
+                var dirCodeIndex = (_CvibNode.dirCode[ii] / 10) - 1;
+                uunprotectedArr[dirCodeIndex] = _CvibNode.unprotected[ii];
+            }
+        }
+
+        /*
+                  방향코드 50이상 북 동 남 서 로 방향 맞춰주기
+                  50[4] 반대 70[6] [넣을곳 방향코드 40[3] 있으면 30[2]] --
+                  60[5] 반대 80[7] [넣을곳 방향코드 10[0] 있으면 40[3]]
+                  70[6] 반대 50[4] [넣을곳 방향코드 20[1] 있으면 10[0]]
+                  80[7] 반대 60[5] [넣을곳 방향코드 30[2] 있으면 20[1]]
+       */
+        for (var ii = 4; ii < _CvibNode.lightPosArr.length; ii++) {
+            if (_CvibNode.lightStsArr[ii] == "9_9_9_9") continue;
+            var dirCodeIndex = ii + 1 //  0 -> 1
+            var opDirCode = (ii + 1) * 10;  //인덱스를 방향코드로 10 20 30 같이
+
+            for (var i = 0; i < dataCount; i++) {
+                if (_CvibNode.dirCode[i] > 40) {
+                    var index = (_CvibNode.dirCode[i] / 10) - 1;
+                    if (_CvibNode.light[i] == 2 || _CvibNode.unprotected[i] == 1) {
+                        lunprotectedArr[index] = _CvibNode.unprotected[i];
+                    }
+                    if (_CvibNode.light == 7 || _CvibNode.unprotected[i] == 1) {
+                        uunprotectedArr[index] = _CvibNode.unprotected[i];
+                    }
+                }
+            }
+            if (ii < 6) {
+                if (_CvibNode.lightStsArr[3] == "9_9_9_9") {
+                    _CvibNode.lightStsArr[3] = _CvibNode.lightStsArr[ii];
+
+                    lunprotectedArr[3] = lunprotectedArr[ii];
+                    uunprotectedArr[3] = uunprotectedArr[ii];
+                } else {
+                    if (ii < 5) {
+
+                        _CvibNode.lightStsArr[2] = _CvibNode.lightStsArr[ii];
+                        lunprotectedArr[2] = lunprotectedArr[ii];
+                        uunprotectedArr[2] = uunprotectedArr[ii];
+                    } else {
+                        _CvibNode.lightStsArr[0] = _CvibNode.lightStsArr[ii];
+                        lunprotectedArr[0] = lunprotectedArr[ii];
+                        uunprotectedArr[0] = uunprotectedArr[ii];
+                    }
+                }
+            } else {
+                if (_CvibNode.lightStsArr[1] == "9_9_9_9") {
+                    _CvibNode.lightStsArr[1] = _CvibNode.lightStsArr[ii];
+                    lunprotectedArr[1] = lunprotectedArr[ii];
+                    uunprotectedArr[1] = uunprotectedArr[ii];
+                } else {
+                    if (ii < 7) {
+                        _CvibNode.lightStsArr[0] = _CvibNode.lightStsArr[ii];
+                        lunprotectedArr[0] = lunprotectedArr[ii];
+                        uunprotectedArr[0] = uunprotectedArr[ii];
+                    } else {
+                        _CvibNode.lightStsArr[2] = _CvibNode.lightStsArr[ii];
+                        lunprotectedArr[2] = lunprotectedArr[ii];
+                        uunprotectedArr[2] = uunprotectedArr[ii];
+                    }
+                }
+            }
+            lunprotectedArr[ii] = null;
+            uunprotectedArr[ii] = null;
+            _CvibNode.lightStsArr[ii] = "9_9_9_9";
+
+        }
+
+
+        for (var ii = 0; ii < _CvibNode.lightPosArr.length; ii++) {
+
+            if (lunprotectedArr[ii] == null || lunprotectedArr[ii] == undefined || lunprotectedArr[ii] == 0) continue;
+            var dirCodeTemp = (ii + 1) * 10;
+            var dirLocation = getdirLocation(dirCodeTemp);
+            cvibMap += unprotectedLeft(dirCodeTemp, dirLocation);
+            leftUnprotect = true;
+        }
+
+
+        //     var dirLocation = getdirLocation(_CvibNode.dirCode[i]);
+        //     if (_CvibNode.light[i] == 7 && _CvibNode.stts[i] == 3) {
+        //         cvibMap += utunDraw(_CvibNode.dirCode[i], _CvibNode.unprotected[i], dirLocation);
+        //         leftUnprotect = false;
+        //     }
+        //     if (_CvibNode.light[i] == 2 && _CvibNode.stts[i] == 3 && _CvibNode.unprotected[i] == 1) {
+        //         cvibMap += unprotectedLeft(_CvibNode.dirCode[i], dirLocation);
+        //         leftUnprotect = true;
+        //     }
+        // }
+
+
+        for (var ii = 0; ii < _CvibNode.lightPosArr.length; ii++) {
+            var overlayId = _CvibNode.lightPosArr[ii];
+            var walkerOverlay = walkerPos[ii];
+            var overlaycwId = cwlightpos[ii];
+            var lightImg = _CvibNode.lightStsArr[ii].substring(0, 3);
+            //var cwLightCode = _CvibNode.lightStsArr[ii].substring(4);
+            //CrossWalkON OFF
+            var cwlightImg = '';
+            var tempcwlight = _CvibNode.lightStsArr[ii].substring(4, 5);
+
+            if (tempcwlight == '3' || tempcwlight == '6') {
+                cwlightImg = 'ON'
+            } else if (tempcwlight == '0') {
+                cwlightImg = '';
+            } else {
+                cwlightImg = 'OFF';
+            }
+
+            //신호등
+            if (lightImg != '9_9') {
+                if (leftUnprotect && lightImg == '3_3') {
+                    cvibMap += '<img src="images/cvib/CvibLight_3_9.gif" width="100px" heigtht="27px" style="transform:rotate(' + overlaycwId + 'deg); position: absolute; left:' + sigFixedLeft[ii] + '%; top:' + sigFixedTop[ii] + '%"  >'
+                } else {
+                    cvibMap += '<img src="images/cvib/CvibLight_' + lightImg + '.gif" width="100px" heigtht="27px" style="transform:rotate(' + overlaycwId + 'deg); position: absolute; left:' + sigFixedLeft[ii] + '%; top:' + sigFixedTop[ii] + '%"  >'
+                }
+            }
+            //보행자 신호등
+            if (tempcwlight != '9' && tempcwlight != '0') {
+                cvibMap += '<img class="cvibwalker" src="images/cvib/CrossWalk' + cwlightImg + '.gif" style="transform:rotate(' + walkerOverlay + 'deg); position: absolute; left:' + walkerFixedL[ii] + '%; top:' + walkerFixedT[ii] + '%;" width="50px" heigtht="27px;">';
+            } else if (cwlightImg == '') {
+                cvibMap += '<img class="cvibwalker" src="images/cvib/CrossWalk.png" style="transform:rotate(' + walkerOverlay + 'deg); position: absolute; left:' + walkerFixedL[ii] + '%; top:' + walkerFixedT[ii] + '%;" width="50px" heigtht="27px;">';
+            }
+        }
+        $('#cvibMap').empty().html(cvibMap);
+
+    }
+
+    function showCvibDetailInfo() {
+
+        //if (cvibDetailInfoTimeId != null) clearTimeout(cvibDetailInfoTimeId);
+
+        $('#cTab1 img').attr('src', 'images/cvibTab_on.png');
+        $('#cTab2 img').attr('src', 'images/tab05_off.png');
+
+        $('.bottomEvent').hide();
+        $('.bottomInfoTop').show();
+        $('.bottomInfoBottom').show();
+        $('.cvibLoading').hide();
+
+        $('#regionCd').text(_CvibNode.addr1);
+        $('#intNo').text(_CvibNode.nodeId);
+        $('#oprTrans').text(_CvibNode.oprTrans);
+        $('#oprInd').text(_CvibNode.oprInd);
+        $('#oprTurnoff').text(_CvibNode.oprTurnoff);
+        $('#oprBlink').text(_CvibNode.oprBlink);
+        $('#oprManual').text(_CvibNode.oprManual);
+        $('#errScu').text(_CvibNode.errScu);
+        $('#errCenter').text(_CvibNode.errCenter);
+        $('#errCont').text(_CvibNode.errCont);
+        $('#cycleCounter').text(_CvibNode.counter);
+        $('#dataCount').text(_CvibNode.dataCount);
+        $('#intTime').text(_CvibNode.date);
+
+
+        var bottom = '';
+        for (var ii = 0; ii < _CvibNode.dataCount; ii++) {
+            var lightTxt = '';
+            var outputTxt = '';
+            var dirCodeTxt = '';
+
+            if (_CvibNode.light[ii] === 1) outputTxt = '직진';
+            else if (_CvibNode.light[ii] === 2) outputTxt = '좌회전';
+            else if (_CvibNode.light[ii] === 3) outputTxt = '보행';
+            else if (_CvibNode.light[ii] === 4) outputTxt = '자전거';
+            else if (_CvibNode.light[ii] === 5) outputTxt = '우회전';
+            else if (_CvibNode.light[ii] === 6) outputTxt = '버스';
+            else if (_CvibNode.light[ii] === 7) outputTxt = '유턴';
+            else outputTxt = '';
+
+            outputTxt += '(' + _CvibNode.light[ii] + ')';
+
+            if (_CvibNode.stts[ii] === 1) lightTxt = '적색 점등(' + _CvibNode.stts[ii] + ')';
+            else if (_CvibNode.stts[ii] === 2) lightTxt = '황색 점등(' + _CvibNode.stts[ii] + ')';
+            else if (_CvibNode.stts[ii] === 3) lightTxt = '녹색 점등(' + _CvibNode.stts[ii] + ')';
+            else if (_CvibNode.stts[ii] === 4) lightTxt = '적색점멸 점등(' + _CvibNode.stts[ii] + ')';
+            else if (_CvibNode.stts[ii] === 5) lightTxt = '황색점멸 점등(' + _CvibNode.stts[ii] + ')';
+            else if (_CvibNode.stts[ii] === 6) lightTxt = '녹색점멸 점등(' + _CvibNode.stts[ii] + ')';
+            else lightTxt = '소등(' + _CvibNode.stts[ii] + ')';
+
+            if (_CvibNode.dirCode[ii] === 10) dirCodeTxt = '북';
+            else if (_CvibNode.dirCode[ii] === 20) dirCodeTxt = '동';
+            else if (_CvibNode.dirCode[ii] === 30) dirCodeTxt = '남';
+            else if (_CvibNode.dirCode[ii] === 40) dirCodeTxt = '서';
+            else if (_CvibNode.dirCode[ii] === 50) dirCodeTxt = '북동';
+            else if (_CvibNode.dirCode[ii] === 60) dirCodeTxt = '남동';
+            else if (_CvibNode.dirCode[ii] === 70) dirCodeTxt = '남서';
+            else if (_CvibNode.dirCode[ii] === 80) dirCodeTxt = '북서';
+            else if (_CvibNode.dirCode[ii] === 99) dirCodeTxt = '보조등';
+            else dirCodeTxt = '미지정';
+
+
+            bottom += '<tr>';
+            bottom += '<td>' + _CvibNode.walker[ii] + '</td>';
+            bottom += '<td>' + dirCodeTxt + '</td>';
+            bottom += '<td>' + _CvibNode.timeFlag[ii] + '</td>';
+            bottom += '<td>' + outputTxt + '</td>';
+            bottom += '<td class="cvibLightsBack_' + _CvibNode.stts[ii] + '">' + lightTxt + '</td>';
+            bottom += '<td>' + _CvibNode.unprotected[ii] + '</td>'
+            bottom += '<td>' + _CvibNode.remainTm[ii] + '</td>';
+            bottom += '<td>' + _CvibNode.dispTm[ii] + '</td>';
+            bottom += '</tr>';
+        }
+
+        $('#bottomInfoBottom').empty().html(bottom);
+
+    }
+
+    function getdirLocation(dircode) {
+        var dirlocation = 0;
+
+        switch (dircode) {
+            case 10:
+                dirlocation = 180;
+                break;
+            case 20:
+                dirlocation = 270;
+                break;
+            case 30:
+                dirlocation = 0;
+                break;
+            case 40:
+                dirlocation = 90;
+                break;
+            case 50:
+                dirlocation = 45;
+                break;
+            case 60:
+                dirlocation = 315;
+                break;
+            case 70:
+                dirlocation = 225;
+                break;
+            case 80:
+                dirlocation = 135;
+                break;
+        }
+
+        return dirlocation;
+    }
+
+    function utunDraw(dircode, unprotected, dirLocation) {
+
+        var unprotectX;
+        var unprotectY;
+
+        if (window.innerWidth > 1000) {
+            switch (dirCode) {
+                case 10:
+                    unprotectX = 17.5;
+                    unprotectY = 77;
+                    break;
+                case 20:
+                    unprotectX = 6.5;
+                    unprotectY = 41;
+                    break;
+                case 30:
+                    unprotectX = 20;
+                    unprotectY = 12;
+                    break;
+                case 40:
+                    unprotectX = 30;
+                    unprotectY = 49;
+                    break;
+            }
+        } else {
+            switch (dirCode) {
+                case 10:
+                    unprotectX = 47;
+                    unprotectY = 64;
+
+                    break;
+                case 20:
+                    unprotectX = 33;
+                    unprotectY = 37;
+                    break;
+                case 30:
+                    unprotectX = 50;
+                    unprotectY = 15;
+                    break;
+                case 40:
+                    unprotectX = 64;
+                    unprotectY = 43;
+
+                    break;
+            }
+        }
+        var imgUrl = '';
+        if (unprotected == 1) { //비보호유턴
+            imgUrl = 'images/cvib/unproUTUN120.png'
+        } else { // 유턴 *
+            imgUrl = 'images/cvib/UTUN.png'
+        }
+
+
+        return '<img src="' + imgUrl + '" width="45px" heigtht="45px" style="transform:rotate(' + dirLocation + 'deg); position: fixed; left:' + unprotectX + '%; top:' + unprotectY + '%">';
+    }
+
+    function unprotectedLeft(dirCode, dirLocation) {
+        var unprotectX;
+        var unprotectY;
+
+        if (window.innerWidth > 1000) {
+            switch (dirCode) {
+                case 10:
+                    unprotectX = 17.5;
+                    unprotectY = 77;
+                    break;
+                case 20:
+                    unprotectX = 6.5;
+                    unprotectY = 41;
+                    break;
+                case 30:
+                    unprotectX = 20;
+                    unprotectY = 12;
+                    break;
+                case 40:
+                    unprotectX = 30;
+                    unprotectY = 49;
+                    break;
+            }
+        } else {
+            switch (dirCode) {
+                case 10:
+                    unprotectX = 46.5;
+                    unprotectY = 79;
+
+                    break;
+                case 20:
+                    unprotectX = 32;
+                    unprotectY = 41;
+                    break;
+                case 30:
+                    unprotectX = 50;
+                    unprotectY = 11;
+                    break;
+                case 40:
+                    unprotectX = 64;
+                    unprotectY = 49;
+                    break;
+            }
+        }
+
+
+        // switch (dirCode) {
+        //     case 10:
+        //         unprotectX = 230;
+        //         unprotectY = 380;
+        //         break;
+        //     case 20:
+        //         unprotectX = 100;
+        //         unprotectY = 225;
+        //         break;
+        //     case 30:
+        //         unprotectX = 260;
+        //         unprotectY = 105;
+        //         break;
+        //     case 40:
+        //         unprotectX = 390;
+        //         unprotectY = 260;
+        //         break;
+        // }
+
+        return '<img src="images/cvib/unprotected.png" width="40px" heigtht="40px" style="transform:rotate(' + dirLocation + 'deg); position: absolute; left:' + unprotectX + '%; top:' + unprotectY + '%">';
+    }
+
+
+</script>
+<div class="popContainer">
+    <div class="cvibInfoTop">
+        <ul class="tabs">
+            <li><a href="javascript:showCvibDetailInfo()" class="tab" id="cTab1"></a></li>
+            <li><a href="javascript:showCvibEventHistory()" class="tab" id="cTab2"></a></li>
+            <li class="regionIntNm"><strong id="regionIntNm"></strong></li>
+        </ul>
+    </div>
+    <div class="cvibMapAndInfo">
+        <!--  픽셀  북 : 450 80  -->
+        <div class="crossLoadMap">
+            <c:choose>
+                <c:when test="${node.nodetype eq 4}">
+                    <div style="float:left;background-image: url(images/cvib/CrossLoadBG.png); width:100%; height: 100%; background-repeat: no-repeat;background-position: center;background-size: 500px;"></div>
+                    <div id='cvibMap'></div>
+                </c:when>
+                <c:otherwise>
+                    <div id="map" class="detailMap"></div>
+                </c:otherwise>
+            </c:choose>
+
+        </div>
+
+        <div class="crossLoadInfo">
+
+            <div class="bottomInfo">
+                <div class="bottomInfoBottom">
+                    <div class="bottomInfoBody">
+                        <table>
+                            <colgroup>
+                                <col width="7%">
+                                <col width="8%">
+                                <col width="9%">
+                                <col width="9%">
+                                <col width="10%">
+                                <col width="6%">
+                                <col width="10%">
+                                <col width="10%">
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <td colspan="2">방향정보</td>
+                                <td colspan="4">출력정보</td>
+                                <td colspan="2">기타</td>
+                            </tr>
+                            <tr>
+                                <td>보행자</td>
+                                <td>방향코드</td>
+                                <td>시간신뢰성</td>
+                                <td>출력형태</td>
+                                <td>신호등상태</td>
+                                <td>비보호</td>
+                                <td>잔여시간</td>
+                                <td>표출시간</td>
+                            </tr>
+                            </thead>
+                            <tbody id="bottomInfoBottom"></tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="bottomInfo">
+        <div class="bottomInfoTop">
+            <table>
+                <colgroup>
+                    <col width="9%">
+                    <col width="9%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="6%">
+                    <col width="8%">
+                    <col width="8%">
+                    <col width="18%">
+                </colgroup>
+                <thead>
+                    <tr>
+                        <th colspan="2">교차로정보</th>
+                        <th colspan="5">운영정보</th>
+                        <th colspan="3">고장정보</th>
+                        <th colspan="3">기타</th>
+                    </tr>
+                    <tr>
+                        <th>지역센터</th>
+                        <th>번호</th>
+                        <th>전이</th>
+                        <th>감응</th>
+                        <th>소등</th>
+                        <th>점멸</th>
+                        <th>수동</th>
+                        <th>SCU</th>
+                        <th>센터</th>
+                        <th>모순</th>
+                        <th>신호주기</th>
+                        <th>정보개수</th>
+                        <th>교차로시각</th>
+                    </tr>
+                </thead>
+                <tbody id="bottomInfoTop">
+                <tr>
+                    <td id="regionCd"></td>
+                    <td id="intNo"></td>
+                    <td id="oprTrans"></td>
+                    <td id="oprInd"></td>
+                    <td id="oprTurnoff"></td>
+                    <td id="oprBlink"></td>
+                    <td id="oprManual"></td>
+                    <td id="errScu"></td>
+                    <td id="errCenter"></td>
+                    <td id="errCont"></td>
+                    <td id="cycleCounter"></td>
+                    <td id="dataCount"></td>
+                    <td id="intTime"></td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+
+    </div>
+</div>
+
+<script src="/js/common/common-all.js"></script>
+<%--<script src="/js/signal.js"></script>--%>
+<script src="/js/signal_new.js"></script>
+<script src="/js/cvib.js"></script>
+
+
+<script>
+    _Level = 1;
+    $('#regionIntNm').html('[' + nodeAddr1 + ']' + nodeName);
+    var map;
+    var mapContainer;
+    <c:if test="${node.nodetype ne 4}">
+    mapContainer = document.getElementById('map'); // 지도를 표시할 div
+    var mapOption = {
+        draggable: false,
+        center: new kakao.maps.LatLng(nodeLat, nodeLng),
+        level: _Level
+    };
+    map = new kakao.maps.Map(mapContainer, mapOption); // 지도생성
+    </c:if>
+    /**
+     Map 매칭 방안에 따른 방향 코드-신호등정보(미지정(0), 북(1), 동(2), 남(3), 서(4), 북동(5), 남동(6), 남서(7), 북서(8))'
+     출력형태 : 미지정(0), 직진(1), 좌회전(2), 보행(3), 자전거(4), 우회전(5), 버스(6), 유턴(7)'
+     신호등상태: 소등(0), 적색점등(1), 황색점등(2), 녹색점등(3), 적색점멸(4), 황색점멸(5), 녹색점멸(6)'
+     **/
+
+</script>
+
+</body>
+</html>

+ 3 - 1
src/main/webapp/WEB-INF/jsp/denied.jsp

@@ -13,12 +13,14 @@
     const useYN = "${useYN}"
     if (useYN == "N") {
         alert("휴면계정입니다 관리자에게 문의하십시요.")
-    } else if (userId) {
+    }
+    else if (userId) {
         alert("비밀번호 5회 실패시에 휴면계정으로 전환됩니다. \n 실패횟수 : " + failCnt + "/5회")
     } else {
         alert("존재하지 않는 계정입니다.")
     }
     location.href=('/showlogin.do')
+    //
 </script>
 </body>
 </html>

+ 4 - 4
src/main/webapp/WEB-INF/jsp/login.jsp

@@ -12,9 +12,9 @@
     <meta name="Description" content="사이트설명">
     <meta name="Keywords" content="키워드">
 
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/common.css">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/reset.css">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/login.css">            <!-- 해당 페이지 전용 -->
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/reset.css">
+    <link rel="stylesheet" type="text/css" href="/css/login.css">            <!-- 해당 페이지 전용 -->
 
     <title>SIGINFO_MAP</title>
 </head>
@@ -46,7 +46,7 @@
     </div>
 </div>
 
-<script src="${contextRoot }/js/common/common-all.js"></script><!-- 공통 -->
+<script src="/js/common/common-all.js"></script><!-- 공통 -->
 <script>
 
     <c:if test="${param.error == 1}">

+ 51 - 33
src/main/webapp/WEB-INF/jsp/main.jsp

@@ -25,22 +25,23 @@
     <meta name="Description" content="신호관제">
     <meta name="Keywords" content="신호관제">
     <link rel="icon" href="data:;base64,iVBORw0KGgo=">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/common.css?v=1">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/main.css?v=1">
-    <link rel="stylesheet" type=iframe"text/css" href="${contextRoot }/css/themes/default/style.css">
+    <link rel="stylesheet" type="text/css" href="/css/common.css?v=1">
+    <link rel="stylesheet" type="text/css" href="/css/main.css?v=1">
+    <link rel="stylesheet" type=iframe"text/css" href="/css/themes/default/style.css">
     <link rel="icon" type="text/css" href="data:;base64,iVBORw0KGgo=">
 
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
-    <script src="${contextRoot }/js/common/moment.js"></script>
-    <script src="${contextRoot }/js/common/hashmap.js"></script>
-    <script src="${contextRoot }/js/common/common-all.js"></script>
-    <script src="${contextRoot }/js/common/common.js"></script>
-    <script src="${contextRoot }/js/common/jstree.min.js"></script>
+    <script src="/js/common/moment.js"></script>
+    <script src="/js/common/hashmap.js"></script>
+    <script src="/js/common/common-all.js"></script>
+    <script src="/js/common/common.js"></script>
+    <script src="/js/common/jstree.min.js"></script>
 
-    <script src="${contextRoot }/js/map.js"></script>
-    <script src="${contextRoot }/js/signal.js"></script>
+    <script src="/js/map.js"></script>
+<%--    <script src="/js/signal.js"></script>--%>
+    <script src="/js/signal_new.js"></script>
 
-    <script src="${contextRoot }/js/cvib.js"></script>
+    <script src="/js/cvib.js"></script>
 
     <title>CVIM_MAP</title>
 </head>
@@ -80,8 +81,12 @@
     <iframe src="/getTreeListFrame.do" id="iframeTreeList" class="iframeTreeList" title="리스트 목록"></iframe>
     <div id="mapWrapper">
         <div id="map" class="map">
-            <div class="wideMap"><img alt="지도확대축소" src="images/drop_down.png" width="45" height="25"></div>
-            <div class="upDownMap"><img alt="지도확대축소" src="images/drop_down.png" width="45" height="25"></div>
+            <div class="wideMap">
+<%--                <img alt="지도확대축소" src="images/drop_down.png" width="30" height="17">--%>
+            </div>
+            <div class="upDownMap">
+<%--                <img alt="지도확대축소" src="images/drop_down.png" width="30" height="17">--%>
+            </div>
             <!-- 검색값-->
             <div class="bg_white" id="menu_wrap">
                 <div class="option">
@@ -101,49 +106,49 @@
                 <ul style="width:100%; display:block;">
                     <li class="toggleBtn" style="float:left;"><a href="javascript:void(0)" class="place"><img id="place"
                                                                                                               class="road_off"
-                                                                                                              src="${contextRoot}/images/find_location_off.png"
+                                                                                                              src="/images/find_location_off.png"
                                                                                                               alt="위치찾기"></a>
                     </li>
                     <li class="toggleBtn" style="margin-left: 5px;float:left;"><a href="javascript:findLocation()" class="coord" id="coordControl"><img id="coord"
                                                                                                               class="coord_off"
-                                                                                                              src="${contextRoot}/images/find_coord_off.png"
+                                                                                                              src="/images/find_coord_off.png"
                                                                                                               alt="좌표검색"></a>
                     </li>
                     <li class="toggleBtn" style="margin-left:5px;float:left;"><a href="javascript:getRoadView();"
                                                                                  class="road" id="roadviewControl"><img
-                            id="road" class="road_off" src="${contextRoot}/images/road_view.gif" alt="로드뷰오프"></a></li>
+                            id="road" class="road_off" src="/images/road_view.gif" alt="로드뷰오프"></a></li>
                     <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:getDrawRoad();"
                                                                                   class="draw"><img id="draw"
                                                                                                     class="road_off"
-                                                                                                    src="${contextRoot}/images/distance.png"
+                                                                                                    src="/images/distance.png"
                                                                                                     alt="거리재기"></a>
                     </li>
                     <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:getDrawArea();"
                                                                                   class="area"><img id="area"
                                                                                                     class="road_off"
-                                                                                                    src="${contextRoot}/images/area.png"
+                                                                                                    src="/images/area.png"
                                                                                                     alt="면적재기"></a>
                     </li>
                     <%-- <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:toggleSiginfo();"
                                                                                    class="toggle_siginfo"><img
-                             id="toggle_siginfo" class="road_off" src="${contextRoot}/images/toggle_siginfo_off.png"
+                             id="toggle_siginfo" class="road_off" src="/images/toggle_siginfo_off.png"
                              alt="신호정보토글"/></a></li>--%>
                     <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:getMyLocation();"
                                                                                   class="mylocation"><img
                                                                                                           class="mylocation"
-                                                                                                          src="${contextRoot}/images/position.png"
+                                                                                                          src="/images/position.png"
                                                                                                           alt="현재위치"></a>
                     </li>
                     <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:refreshNodeList();"
                                                                                   class="refresh"><img
                             class="refresh"
-                            src="${contextRoot}/images/refresh.png"
+                            src="/images/refresh.png"
                             alt="새로고침"></a>
                     </li>
 <%--                    <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:emergencyHistory();"--%>
 <%--                                                                                  class="refresh"><img--%>
 <%--                            class="refresh"--%>
-<%--                            src="${contextRoot}/images/refresh.png"--%>
+<%--                            src="/images/refresh.png"--%>
 <%--                            alt="테스트"/></a>--%>
 <%--                    </li>--%>
                 </ul>
@@ -176,16 +181,24 @@
     <!-- 모바일인경우 현재위치버튼 변경 -->
     <div class="mylocationMob" style="display:none;position:absolute;bottom:calc(25% + 35px);left:335px;z-index:10;">
         <a href="javascript:getMyLocation();" class="mylocationMobA">
-            <img id="position" class="road_off" src="${contextRoot}/images/position_m.png" alt="현재위치" style="width:75px;height:75px">
+            <img id="position" class="road_off" src="/images/position_m.png" alt="현재위치" style="width:75px;height:75px">
         </a>
     </div>
 </div>
 
-<%--<script src="${contextRoot }/js/common/webSocket.js"></script>--%>
-<script src="${contextRoot }/js/common/cvibStatus.js"></script>
+<%--<script src="/js/common/webSocket.js"></script>--%>
+<script src="/js/common/cvibStatus.js"></script>
 <script>
+    function modalClose() {
+        $('.modal-box').hide();
+        $('#iframeModal').remove();
 
+    }
 
+    $('.modal-box').draggable({
+        containment : "body",
+        handle : ".modal-title-box",
+    });
     var centerPopId = null;
     var eventPopId = null;
     var signalPopId = null;
@@ -372,7 +385,8 @@
         const isRoadView = $('.road_on')[0];
         if (wideFlag === false) {
             // $('.wideMap img').attr('src', '/images/arrow_right.png');
-            $('.wideMap img').attr('src', '/images/drop_up.png');
+            //$('.wideMap img').attr('src', '/images/drop_up.png');
+            $('.wideMap').addClass('on');
             $('.iframeTreeList').hide();
             const widthVal = isRoadView ? 'calc(60% - 10px)' : 'calc(100% - 10px)';
             $('#mapWrapper').css({
@@ -388,21 +402,23 @@
                 $('.mylocationMob').css('left', '25px');
             wideFlag = true;
         } else if (wideFlag === true) {
+
             // $('.wideMap img').attr('src', '/images/arrow_left.png');
-            $('.wideMap img').attr('src', '/images/drop_down.png');
+            // $('.wideMap img').attr('src', '/images/drop_down.png');
+            $('.wideMap').removeClass('on');
             $('.iframeTreeList').show();
-            const widthVal = isRoadView ? 'calc(60% - 338px)' : 'calc(100% - 338px)';
+            const widthVal = isRoadView ? 'calc(60% - 290px)' : 'calc(100% - 290px)';
             $('#mapWrapper').css({
                 width: widthVal,
-                margin: '5px 0 0 332px'
+                margin: '5px 0 0 287px'
             });
             $('.iframeBottomList').css({
-                width: 'calc(100% - 338px)',
+                width: 'calc(100% - 287px)',
                 margin: '0 0 0 2px'
             });
 
             if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
-                $('.mylocationMob').css('left', '335px');
+                $('.mylocationMob').css('left', '287px');
             wideFlag = false;
         }
         map.relayout();
@@ -412,7 +428,8 @@
         if (upDownFlag == false) {
             //확대
             // $('.upDownMap img').attr('src', 'images/arrow_up.png');
-            $('.upDownMap img').attr('src', 'images/drop_up.png');
+            // $('.upDownMap img').attr('src', 'images/drop_up.png');
+            $('.upDownMap').addClass('on');
             $('.iframeBottomList').hide();
             $('#mapWrapper').css('height', 'calc(100% - 12px)');
             $('.evp_legend').css('bottom', '10px');
@@ -422,7 +439,8 @@
         } else if (upDownFlag == true) {
             //축소
             // $('.upDownMap img').attr('src', 'images/arrow_down.png').slide;
-            $('.upDownMap img').attr('src', 'images/drop_down.png').slide;
+            // $('.upDownMap img').attr('src', 'images/drop_down.png').slide;
+            $('.upDownMap').removeClass('on');
             $('.iframeBottomList').show();
             $('#mapWrapper').css('height', 'calc(100% - 230px)');
             if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)

+ 666 - 0
src/main/webapp/WEB-INF/jsp/main_new.jsp

@@ -0,0 +1,666 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
+<!doctype html>
+<html lang="ko">
+<head>
+    <%@ include file="/WEB-INF/jsp/common.jsp" %>
+    <sec:authorize access="hasAuthority('B')">
+        <script>
+            alert("기반정보 관리자 계정입니다. 다른 계정으로 로그인하십시요")
+            location.href=('/logout.do')
+        </script>
+    </sec:authorize>
+    <sec:authorize access="!isAuthenticated()">
+        <script>
+            alert("로그인 정보가 없습니다. 로그인해주세요")
+            location.href=('/logout.do')
+        </script>
+    </sec:authorize>
+    <meta name="_csrf" content="${_csrf.token}">
+    <meta name="_csrf_header" content="${_csrf.headerName}">
+    <meta http-equiv="X-UA-Compatible" content="IE=10; IE=9; IE=8; IE=7; IE=EDGE">
+    <meta name="Description" content="신호관제">
+    <meta name="Keywords" content="신호관제">
+    <link rel="icon" href="data:;base64,iVBORw0KGgo=">
+    <link rel="stylesheet" type="text/css" href="/css/common.css?v=2">
+    <link rel="stylesheet" type="text/css" href="/css/main_new.css?v=2">
+    <link rel="stylesheet" type=iframe"text/css" href="/css/themes/default/style.css?v=2">
+    <link rel="icon" type="text/css" href="data:;base64,iVBORw0KGgo=">
+
+    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js?v=2"></script>
+    <script src="/js/common/moment.js?v=2"></script>
+    <script src="/js/common/hashmap.js?v=2"></script>
+    <script src="/js/common/common-all.js?v=2"></script>
+    <script src="/js/common/common.js?v=2"></script>
+    <script src="/js/common/jstree.min.js?v=2"></script>
+
+    <script src="/js/map.js?v=2"></script>
+    <script src="/js/signal_new.js?v=2"></script>
+
+    <script src="/js/cvib.js?v=2"></script>
+
+    <title>CVIM_MAP</title>
+</head>
+<body class="sang">
+<input type="hidden" name="min_x" id="min_x" value="">
+<input type="hidden" name="min_y" id="min_y" value="">
+<input type="hidden" name="max_x" id="max_x" value="">
+<input type="hidden" name="max_y" id="max_y" value="">
+<input type="hidden" name="regionCd" id="regionCd" value="">
+<input type="hidden" name="regionId" id="regionId" value="">
+<input type="hidden" name="intNo" id="intNo" value="">
+<input type="hidden" name="intNm" id="intNm" value="">
+<input type="hidden" name="intLcType" id="intLcType" value="">
+<input type="hidden" name="intLampType" id="intLampType" value="">
+<input type="hidden" name="groupNo" id="groupNo" value="">
+<input type="hidden" name="mainIntNo" id="mainIntNo" value="">
+<input type="hidden" name="treeXCoord" id="treeXCoord" value="">
+<input type="hidden" name="treeYCoord" id="treeYCoord" value="">
+<input type="hidden" name="XCoord" id="XCoord" value="126.978087">
+<input type="hidden" name="YCoord" id="YCoord" value="37.560684">
+<input type="hidden" name="sigunguCd" id="sigunguCd" value="">
+<input type="hidden" name="intMainphase" id="intMainphase" value="">
+<input type="hidden" name="networkType" id="networkType" value="">
+<input type="hidden" name="modemIp" id="modemIp" value="">
+
+<div class="headMenu">
+    <h1><a href="main.do" title="로고"><img src="images/logo3.png" alt="로고" style="width: 310px;margin: 3px;"></a></h1>
+    <h2 style="width: calc(100% - 340px); display: flex; justify-content: flex-end; height: 100%; align-items: center;">
+        <a href="logout.do" title="로그아웃"><img src="images/logout.png" alt="로그아웃"></a>
+    </h2>
+
+    <ul>
+
+    </ul>
+</div>
+<div id="container" class="container">
+    <iframe src="/getTreeListFrame.do" id="iframeTreeList" class="iframeTreeList" title="리스트 목록"></iframe>
+    <div id="mapWrapper">
+        <div id="map" class="map">
+            <div class="wideMap">
+<%--                <img alt="지도확대축소" src="images/drop_down.png" width="30" height="17">--%>
+            </div>
+            <div class="upDownMap">
+<%--                <img alt="지도확대축소" src="images/drop_down.png" width="30" height="17">--%>
+            </div>
+<%--            <div class="evp_legend"><img alt="긴급차량우선신호 범례" src="/images/evp_legend.png" width="178" height="50"></div>--%>
+            <!-- 검색값-->
+            <div class="bg_white" id="menu_wrap">
+                <div class="option">
+                    <p></p>
+                    키워드 :
+                    <input id="keyword" type="text" size="15" maxlength="50" value="">
+                    <label style="display: none;" for="keyword"></label>
+                    <button type="submit" class="searchBtn" onclick="searchPlaces();" autocomplete="false">검색하기</button>
+                    <p></p>
+                </div>
+                <hr>
+                <ul id="placesList"></ul>
+            </div>
+            <!-- 검색값끝-->
+            <!-- 로드뷰 -->
+            <div class="mapToggle">
+                <ul style="width:100%; display:block;">
+                    <li class="toggleBtn" style="float:left;">
+                        <a href="javascript:void(0)" class="place" title="위치찾기">
+                            <img id="place" class="road_off" src="/images/find_location_off.png" alt="위치찾기">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left: 5px;float:left;" title="좌표검색">
+                        <a href="javascript:void(0)" class="coord" id="coordControl">
+                            <img id="coord" class="road_off" src="/images/find_coord_off.png" alt="좌표검색">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left:5px;float:left;" title="로드뷰 보기">
+                        <a href="javascript:void(0);" class="road" id="roadviewControl">
+                            <img id="road" class="road_off" src="/images/road_view.gif" alt="로드뷰 보기">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left:5px; float:left;" title="거리재기">
+                        <a href="javascript:void(0);" class="draw">
+                            <img id="draw" class="road_off" src="/images/distance.png" alt="거리재기">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left:5px; float:left;" title="면적재기">
+                        <a href="javascript:void(0);" class="area">
+                            <img id="area" class="road_off" src="/images/area.png" alt="면적재기">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left:5px; float:left;" title="현재위치">
+                        <a href="javascript:getMyLocation();" class="mylocation">
+                            <img class="mylocation" src="/images/position.png" alt="현재위치">
+                        </a>
+                    </li>
+                    <li class="toggleBtn" style="margin-left:5px; float:left;" title="리스트 새로고침">
+                        <a href="javascript:refreshNodeList();" class="refresh">
+                            <img class="refresh" src="/images/refresh.png" alt="새로고침">
+                        </a>
+                    </li>
+                </ul>
+            </div>
+            <div class="coordBox">
+                <div class="coord-unit">
+                    <div>좌표 : </div>
+                    <input type="text" id="lctn_coord" name="coord" autocomplete="false" onkeyup="moveToPosition(event)">
+                    <label style="display: none;" for="lctn_coord"></label>
+                </div>
+                <div class="coord-btn" onclick="moveToPosition()">이동</div>
+            </div>
+        </div>
+    </div>
+    <!-- 로드뷰 -->
+    <div id="rvWrapper">
+        <div id="roadview" class="roadview">
+            <div id="roadviewClose" title="로드뷰닫기" onclick="closeRoadview()"><span class="img"></span></div>
+        </div>
+    </div>
+    <!-- //로드뷰-->
+    <iframe src="/getBottomListFrame.do" id="iframeBottomList" class="iframeBottomList" onload="getSignalInfo()" title="상세 목록"></iframe>
+    <!-- 모바일인경우 현재위치버튼 변경 -->
+    <div class="mylocationMob" style="display:none;position:absolute;bottom:calc(25% + 35px);left:335px;z-index:10;">
+        <a href="javascript:getMyLocation();" class="mylocationMobA">
+            <img id="position" class="road_off" src="/images/position_m.png" alt="현재위치" style="width:75px;height:75px">
+        </a>
+    </div>
+    <div class="modal-box">
+        <div class="modal-title-box">
+            <div>신호정보(<span class="modal-title"></span>)</div>
+            <div class="modal-close" onclick="modalClose()">x</div>
+        </div>
+        <div class="modal-container"></div>
+    </div>
+    <div class="error-box">
+        <div class="error-content">
+            <div class="modal-title-box">
+                <div>장애이력 조회</div>
+                <div class="modal-close" onclick="errorClose()">x</div>
+            </div>
+            <div class="search-box">
+                <div>지역 </div>
+                <select id="region_cd">
+                    <option>전체</option>
+                    <c:forEach items="${region_list}" var="obj">
+                        <option value="${obj.regionId}">${obj.regionName}</option>
+                    </c:forEach>
+                </select>
+                &nbsp;
+                <div>교차로 검색</div>
+                <input type="text" id="int_no">
+                </input>
+                &nbsp;
+                <div>검색기간</div>
+                <input type="date" id="fromDt">
+                <div>~</div>
+                <input type="date" id="toDt">
+                &nbsp;
+                <input type="radio" name="error-type" id="error-stat" checked onchange="toggleTable('stat')"><label for="error-stat">장애 이력</label>
+                <input type="radio" name="error-type" id="error-cnt" onchange="toggleTable('cnt')"><label for="error-cnt" >장애 발생 건수</label>
+                <div class="button" onclick="searchErrorStatistics()"><i class="fa fa-search"></i>조회</div>
+            </div>
+            <div class="modal-title-box">
+                <div>검색 결과</div>
+            </div>
+            <div class="table-box">
+                <div class="table-content">
+                    <table class="error-stat-table on">
+                        <colgroup>
+                            <col width="20%">
+                            <col width="40%">
+                            <col width="20%">
+                            <col width="10%">
+                            <col width="10%">
+                        </colgroup>
+                        <thead>
+                        <tr>
+                            <th>지역명</th>
+                            <th>교차로명</th>
+                            <th>이벤트 발생시각</th>
+                            <th>상태</th>
+                            <th>서버 ID</th>
+                        </tr>
+                        </thead>
+                        <tbody></tbody>
+                    </table>
+                    <table class="error-cnt-table">
+                        <thead>
+                        <tr>
+                            <th>지역명</th>
+                            <th>교차로명</th>
+                            <th>이벤트 발생 건수</th>
+                            <th>상태</th>
+                        </tr>
+                        </thead>
+                        <tbody></tbody>
+                    </table>
+                </div>
+                <div class="error-stat pagination on">
+                    <button class="prev unactive">«</button>
+                    <button class="active">1</button>
+                    <button class="next unactive">»</button>
+                </div>
+                <div class="error-cnt pagination">
+                    <button class="prev unactive">«</button>
+                    <button class="active">1</button>
+                    <button class="next unactive">»</button>
+                </div>
+            </div>
+            <div class="showLoading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
+        </div>
+    </div>
+</div>
+
+<%--<script src="/js/common/webSocket.js"></script>--%>
+<script src="/js/common/cvibStatus.js?v=2"></script>
+<script>
+    const yesterday = new Date(new Date().setDate(new Date().getDate() - 1));
+    const yesterdayVal = yesterday.getFullYear() + '-' +
+        (yesterday.getMonth() + 1).toString().padStart(2, '0') + '-' +
+        yesterday.getDate().toString().padStart(2, '0');
+    let _errorStatMap = new Map();
+    let _errorCntMap = new Map();
+    $('#fromDt').val(yesterdayVal);
+    $('#toDt').val(yesterdayVal);
+
+
+    function toggleTable(type) {
+        $('.table-box table.on').removeClass('on');
+        $('.pagination.on').removeClass('on');
+        $('.error-' + type + '-table').addClass('on');
+        $('.error-' + type).addClass('on');
+    }
+
+    function errorClose() {
+        $('.error-box').hide();
+        $('#errorStatistics').removeClass('on');
+    }
+
+    function modalClose() {
+        let iframe = document.querySelector('#iframeModal');
+        iframe.src = 'about:blank';
+        iframe.remove();
+        $('.modal-box').hide();
+    }
+
+
+    function searchErrorStatistics() {
+        const isStat = $('#error-stat').is(':checked');
+        const url = isStat ? 'getNodeStatistics.do' : 'getNodeStatusCntStat.do';
+        const fromDt = $('#fromDt').val();
+        const toDt = $('#toDt').val();
+        let regionId = $('#region_cd').val()
+        let nodeId = $('#int_no').val();
+
+        if (!fromDt || !toDt) {
+            return alert('검색 기간을 선택 해주세요.');
+        }
+
+        if (fromDt > toDt) {
+            return alert('검색 시작일이 종료일보다 클 수 없습니다.');
+        }
+
+        const loading = $('.error-box .showLoading');
+        const param = { fromDt, toDt, regionId, nodeId };
+        loading.css('display', 'flex');
+        requestService(url, param, (res)=>{
+            isStat ? errorStatPaging(res) : errorCntPaging(res);
+            loading.css('display', 'none');
+        }, true, ()=> loading.css('display', 'none'));
+    }
+
+
+    function errorStatPaging(res) {
+        _errorStatMap.clear();
+        const rowsPerPage = 20;
+
+        if (res && res.length) {
+            let str = '';
+            res.forEach((obj, idx)=>{
+                if (idx % rowsPerPage === 0 && idx > 0) {
+                    let selPage = idx / rowsPerPage;
+                    _errorStatMap.set(selPage, str);
+                    str = '';
+                }
+
+                str += `<tr>
+                            <td>\${obj.regionNm}</td>
+                            <td>[\${obj.nodeid}] \${obj.name}</td>
+                            <td>\${obj.eventDt}</td>
+                            <td>\${obj.status}</td>
+                            <td>\${obj.serverid}</td>
+                        </tr>`;
+            });
+            if (str) {
+                _errorStatMap.set(Number(_errorStatMap.size) + 1, str);
+            }
+
+            let pageSize = _errorStatMap.size;
+
+            const isNext = pageSize > 10;
+            if (isNext) {
+                pageSize = 10;
+            }
+
+            makePageStr(false, isNext, 1, pageSize, 1, 'stat');
+        }
+        else {
+            makePageStr(false, false, 1, 1, 1, 'stat');
+        }
+    }
+
+
+    function beforeErrorStat(el, type) {
+        if ($(el).hasClass('unactive')) return;
+        const firstPage = Number($(el).next().text());
+        if (!isNaN(firstPage)) {
+            const startPage = firstPage - 10;
+            const endPage = firstPage - 1;
+            makePageStr(startPage !== 1, true, startPage, endPage, endPage, type);
+        }
+    }
+
+    function makePageStr(isBefore, isNext, start, end, active, type) {
+        const map = type === 'stat' ? _errorStatMap : _errorCntMap;
+        let str = `<button class="prev \${isBefore ? '' : 'unactive'}" onclick="beforeErrorStat(this, '\${type}')">«</button>`;
+        for (let ii = start; ii <= end; ii++) {
+            str += `<button class="\${active === ii ? 'active' : ''}" onclick="showErrorStatPage(\${ii}, this, '\${type}')">\${ii}</button>`;
+        }
+        str += `<button class="next \${isNext ? '' : 'unactive'}" onclick="nextErroStat(this, '\${type}')">»</button>`;
+        let statistics = map.get(active);
+        if (!statistics) {
+            statistics = '<tr class="empty-box"><td colspan="'+$('.error-box table.on thead th').length+'">표출할 정보가 없습니다.</td></tr>'
+        }
+        $('.error-box table.on tbody').html(statistics);
+        $('.error-box .pagination.on').html(str);
+    }
+
+    function nextErroStat(el, type) {
+        if ($(el).hasClass('unactive')) return;
+        const beforePage = $(el).prev().text();
+        let lastPage = Number(beforePage) + 10;
+        const map = type === 'stat' ? _errorStatMap : _errorCntMap;
+        let isNext = map.size > lastPage;
+        if (!isNext) {
+            lastPage = map.size;
+        }
+        const firstPage = Number(beforePage) + 1;
+        makePageStr(true, isNext, firstPage, lastPage, firstPage, type);
+    }
+
+    function showErrorStatPage(page, el, type) {
+        const beforePage = $('.error-box .pagination.on .active');
+        if (page === Number(beforePage.text())) return;
+        beforePage.removeClass('active');
+        $(el).addClass('active');
+        const map = type === 'stat' ? _errorStatMap : _errorCntMap;
+        $('.error-box table.on tbody').html(map.get(page));
+    }
+
+
+    function errorCntPaging(res) {
+        _errorCntMap.clear();
+        const rowsPerPage = 20;
+
+        if (res && res.length) {
+            let str = '';
+            res.forEach((obj, idx)=>{
+                if (idx % rowsPerPage === 0 && idx > 0) {
+                    let selPage = idx / rowsPerPage;
+                    _errorCntMap.set(selPage, str);
+                    str = '';
+                }
+
+                let dataCnt = Number(obj.dataCnt).toLocaleString();
+                str += `<tr>
+                            <td>\${obj.regionNm}</td>
+                            <td>[\${obj.nodeid}] \${obj.name}</td>
+                            <td>\${dataCnt}</td>
+                            <td>\${obj.status}</td>
+                        </tr>`
+
+            });
+            if (str) {
+                _errorCntMap.set(Number(_errorCntMap.size) + 1, str);
+            }
+
+            let pageSize = _errorCntMap.size;
+
+            const isNext = pageSize > 10;
+            if (isNext) {
+                pageSize = 10;
+            }
+
+            makePageStr(false, isNext, 1, pageSize, 1, 'cnt');
+        }
+        else {
+            makePageStr(false, false, 1, 1, 1, 'cnt');
+        }
+    }
+
+    var centerPopId = null;
+    var eventPopId = null;
+    var signalPopId = null;
+
+    /**
+     * 좌표 검색
+     */
+    function findLocation() {
+
+        // var coordImage = "find_coord_";
+        // var onOff = 'off';
+        // if ($coordBox.hasClass('on')) {
+        //     onOff = 'on';
+        // }
+        // coordImage += onOff;
+        // coordImage += ".png";
+
+        // $('#coord').attr('src', BTN_IMAGE_PATH + coordImage);
+    }
+
+    /********************************************************************************
+     * 위치검색
+     *********************************************************************************/
+
+    $(".toggleBtn > a").click(function () {
+        drawingAreaStop();
+        drawingStop();
+        roadViewClose();
+        markerRemove('search');
+        markerRemove('infowindow');
+        const menuWrap  = $("#menu_wrap");
+        const keyword   = $("#keyword");
+        const coordinates = $('#lctn_coord');
+        const $coordBox = $('.coordBox');
+        $coordBox.removeClass('on');
+        keyword.val("");
+        coordinates.val("");
+        menuWrap.css("display", "none");
+
+        const objClass = $(this).attr("class");
+        const obj = $("#" + objClass);
+        const iconClass = obj.attr("class");
+        $('.road_on:not(#'+ objClass + ')').attr('class', 'road_off');
+
+        obj.toggleClass('road_on road_off');
+        if (iconClass === 'road_off'){
+            switch (objClass) {
+                case "place" :
+                    menuWrap.css("display", "block");
+                    keyword.focus();
+                    break;
+                case "coord" :
+                    $coordBox.toggleClass('on');
+                    coordinates.focus();
+                    break;
+                case "road" :
+                    getRoadView();
+                    break;
+                case "draw" :
+                    getDrawRoad();
+                    break;
+                case "area" :
+                    getDrawArea();
+                    break;
+            }
+        }
+    });
+
+    $("#keyword").keydown(function (key) {
+        if (key.keyCode == 13) {
+            searchPlaces();
+        }
+    });
+
+    //메뉴창 숨기기
+
+    var UserAgent = window.navigator.userAgent;
+    var wideFlag = false;
+    var upDownFlag = false;
+
+    $('.wideMap').click(function () {
+        const isRoadView = $('.road_on')[0];
+        if (wideFlag === false) {
+            // $('.wideMap img').attr('src', '/images/arrow_right.png');
+            //$('.wideMap img').attr('src', '/images/drop_up.png');
+            $('.wideMap').addClass('on');
+            $('.iframeTreeList').hide();
+            const widthVal = isRoadView ? 'calc(60% - 10px)' : 'calc(100% - 10px)';
+            $('#mapWrapper').css({
+                width: widthVal,
+                margin: '5px 0 0 5px'
+            });
+            $('.iframeBottomList').css({
+                width: 'calc(100% - 10px)',
+                margin: '0 0 0 5px'
+            });
+
+            if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
+                $('.mylocationMob').css('left', '25px');
+            wideFlag = true;
+        } else if (wideFlag === true) {
+
+            // $('.wideMap img').attr('src', '/images/arrow_left.png');
+            // $('.wideMap img').attr('src', '/images/drop_down.png');
+            $('.wideMap').removeClass('on');
+            $('.iframeTreeList').show();
+            const widthVal = isRoadView ? 'calc(60% - 290px)' : 'calc(100% - 290px)';
+            $('#mapWrapper').css({
+                width: widthVal,
+                margin: '5px 0 0 287px'
+            });
+            $('.iframeBottomList').css({
+                width: 'calc(100% - 287px)',
+                margin: '0 0 0 2px'
+            });
+
+            if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
+                $('.mylocationMob').css('left', '287px');
+            wideFlag = false;
+        }
+        map.relayout();
+    });
+
+    $('.upDownMap').click(function () {
+        if (upDownFlag == false) {
+            //확대
+            // $('.upDownMap img').attr('src', 'images/arrow_up.png');
+            // $('.upDownMap img').attr('src', 'images/drop_up.png');
+            $('.upDownMap').addClass('on');
+            $('.iframeBottomList').hide();
+            $('#mapWrapper').css('height', 'calc(100% - 8px)');
+            $('.evp_legend').css('bottom', '6px');
+            if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
+            $('.mylocationMob').css('bottom', '35px');
+            upDownFlag = true;
+        } else if (upDownFlag == true) {
+            //축소
+            // $('.upDownMap img').attr('src', 'images/arrow_down.png').slide;
+            // $('.upDownMap img').attr('src', 'images/drop_down.png').slide;
+            $('.upDownMap').removeClass('on');
+            $('.iframeBottomList').show();
+            $('#mapWrapper').css('height', 'calc(100% - 230px)');
+            if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
+            $('.mylocationMob').css('bottom', 'calc(25% + 35px)');
+            $('.evp_legend').css('bottom', '228px');
+            upDownFlag = false;
+        }
+        map.relayout();
+    });
+
+    /********************************************************************************
+     * header display
+     *********************************************************************************/
+
+    var headFlag = false;
+    $("#hline").click(function () {
+        if (headFlag == false) {
+            $('.headMenu').slideUp();
+            $('.container').css('height', '100%');
+            headFlag = true;
+        } else {
+            $('.headMenu').slideDown();
+            $('.container').css('height', 'calc(100% - 66px)');
+            headFlag = false;
+        }
+
+    });
+
+    function closeTopMenu(obj) {
+        mapZoomBound();
+        $('#' + obj).attr("class", "road_off");
+        if (obj == "center") {
+            $("#center").attr("src", BTN_IMAGE_PATH + "btn_02.png");
+            setTimeout(function () {
+                location.reload(true);
+            }, 100 * 1);
+        }
+        if (obj == "event") $("#event").attr("src", BTN_IMAGE_PATH + "btn_03.png");
+        if (obj == "addSignal") {
+            $("#addSignal").attr("src", BTN_IMAGE_PATH + "btn_05.png");
+            setTimeout(function () {
+                location.reload(true);
+            }, 100 * 1);
+        }
+    }
+
+    //모바일 인경우 현재위치버튼 변경
+    if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
+        $('.mylocation').hide();
+        $('.mylocationMob').show();
+    }
+
+    $(window).bind("beforeunload", function (e) {
+        if (siginfoPhaseDetailWin != null) siginfoPhaseDetailWin.close();
+        if (signalPopId != null) signalPopId.close();
+    });
+
+    function refreshNodeList() {
+        requestService('refreshNodeList.do', null, (res)=>{
+            console.log(res);
+            if (res) {
+                if (res.success) {
+                    alert('리스트 목록이 새로고침 되었습니다.');
+                    location.reload(true);
+                }
+                else if(res.errorMsg) {
+                    alert('리스트 목록 새로고침 중 오류가 발생하였습니다.<br>' + res.errorMsg);
+                }
+            }
+        }, true)
+    }
+
+
+    function errorHistory() {
+        $('#fromDt').val(yesterdayVal);
+        $('#toDt').val(yesterdayVal);
+        $('#region_cd').val('전체').trigger('change');
+        $('.error-box .error-cnt-table tbody').empty();
+        $('.error-box .error-stat-table tbody').empty();
+        $('#error-stat').click();
+        $('.error-box').css('display', 'flex');
+        $('#errorStatistics').addClass('on');
+    }
+</script>
+</body>
+</html>

+ 7 - 7
src/main/webapp/WEB-INF/jsp/treeListFrame.jsp

@@ -8,9 +8,9 @@
     <%@ include file="/WEB-INF/jsp/common.jsp" %>
     <meta name="_csrf" content="${_csrf.token}">
     <meta name="_csrf_header" content="${_csrf.headerName}">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/common.css">
-    <link rel="stylesheet" type="text/css" href="${contextRoot }/css/main.css">
-    <link rel="stylesheet" href="${contextRoot }/css/themes/default/style.css">
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main.css">
+    <link rel="stylesheet" href="/css/themes/default/style.css">
     <title>교차로 목록</title>
 </head>
 <body class="sang" style="min-width:305px !important;overflow:hidden">
@@ -101,10 +101,10 @@
     </div>
     <div class="treeLoading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
 </div>
-<script src="${contextRoot }/js/common/moment.js"></script>
-<script src="${contextRoot }/js/common/common-all.js"></script>
-<script src="${contextRoot }/js/signalInfo.js"></script>
-<script src="${contextRoot }/js/common/jstree.min.js"></script>
+<script src="/js/common/moment.js"></script>
+<script src="/js/common/common-all.js"></script>
+<script src="/js/signalInfo.js"></script>
+<script src="/js/common/jstree.min.js"></script>
 
 <script>
 

+ 1044 - 0
src/main/webapp/WEB-INF/jsp/treeListFrame_Tree.jsp

@@ -0,0 +1,1044 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ page session="false" %>
+<!doctype html>
+<%--<html lang="ko" style="min-width:300px !important;overflow:hidden">--%>
+<html lang="ko">
+<head>
+    <%@ include file="/WEB-INF/jsp/common.jsp" %>
+    <meta name="_csrf" content="${_csrf.token}">
+    <meta name="_csrf_header" content="${_csrf.headerName}">
+    <link rel="stylesheet" type="text/css" href="/css/common.css?v=2">
+    <link rel="stylesheet" type="text/css" href="/css/main_new.css?v=2">
+    <link rel="stylesheet" href="/css/themes/default/style.css?v=2">
+    <link rel="stylesheet" href="/css/tree.css?v=2">
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
+    <title>교차로 목록</title>
+</head>
+<%--<body class="sang" style="min-width:305px !important;overflow:hidden">--%>
+<body class="sang">
+<div class="leftMenu">
+    <ul class="tabs">
+        <li class="on" onclick="goIntMenu()"><div id="tab1"><img src="/images/int_icon.png" width="17px" alt="교차로 아이콘">교차로</div></li>
+        <li onclick="goEvpMenu()"><div id="tab2"><img src="/images/evp_icon.png" width="17px" alt="긴급차량우선신호 아이콘">긴급차량우선신호</div></li>
+    </ul>
+    <div id="onOffTree"></div>
+    <div class="tree-search">
+        <input type="text" id="searchText" onkeyup="searchTree(event)" placeholder="검색어를 입력해주세요" autocomplete="off">
+        <div class="searchBtn" onclick="searchTree()"><i class="fa fa-search" aria-hidden="true" style="font-size: 14px;"></i>검 색</div>
+    </div>
+    <div class="evp-cur">
+        <div class="title">긴급차량우선신호 서비스 현황</div>
+        <div id="evp-cur-list">
+            <table>
+                <thead>
+                    <tr>
+                        <th width="23%">발생시각</th>
+                        <th width="40%">서비스 ID</th>
+                        <th width="37%">서비스명</th>
+                    </tr>
+                </thead>
+                <tbody class="evp-table">
+                    <tr>
+                        <td colspan="3" class="empty-evp">조회된 내용이 없습니다.</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <div class="historyBox">
+        <div class="title">긴급차량우선신호 서비스 이력</div>
+        <div>
+            <div>
+                <div>센터</div>
+                <label style="display: none;" for="region"></label>
+                <select id="region">
+                    <option value="ALL" selected>전체</option>
+                    <c:if test="${fn:length(center) != 0}">
+                        <c:forEach var="item" items="${center}">
+                            <option value="${item.centerId}">${item.centerNm}</option>
+                        </c:forEach>
+                    </c:if>
+                </select>
+            </div>
+            <div>
+                <div>시작</div>
+                <input type="date" id="start" autocomplete="false"><input type="time" id="start_time">
+                <label style="display: none;" for="start"></label>
+                <label style="display: none;" for="start_time"></label>
+            </div>
+            <div>
+                <div>종료</div>
+                <input type="date" id="end" autocomplete="false" ><input type="time" id="end_time">
+                <label style="display: none;" for="end"></label>
+                <label style="display: none;" for="end_time"></label>
+            </div>
+        </div>
+        <div class="history-btn" onclick="searchHistoryData()">검색</div>
+    </div>
+    <div id="intTree"></div>
+    <div id="historyList">
+        <table>
+            <thead>
+                <tr>
+                    <th width="23%">발생시각</th>
+                    <th width="40%">서비스 ID</th>
+                    <th width="37%">서비스명</th>
+                </tr>
+            </thead>
+            <tbody class="history-table"></tbody>
+        </table>
+        <div class="empty-history">조회된 내용이 없습니다.</div>
+        <div class="history-loading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
+    </div>
+    <div class="play-box">
+        <div>
+            <div>시뮬레이션 재생/멈춤</div>
+            <img id="play" onclick="playSimulation()" src="/images/play.png" alt="시뮬레이션 재생버튼" width="24px">
+            <img id="pause" onclick="pauseSimulation()" src="/images/pause.png" alt="시뮬레이션 멈춤버튼" width="24px">
+            <img id="stop" onclick="stopSimulation()" src="/images/stop.png" alt="시뮬레이션 초기화버튼" width="24px">
+        </div>
+    </div>
+    <div class="treeLoading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
+</div>
+<script src="/js/common/moment.js"></script>
+<script src="/js/common/common-all.js"></script>
+<script src="/js/signalInfo.js"></script>
+<script src="/js/common/jstree.min.js"></script>
+<script>
+
+    var treeJson = null;
+    var groupTreeJson = null;
+    var treeCtrl;
+    var onlineCenterStatusCheckId;
+    var offlineCenterStatusCheckId;
+    var onlineIntStatusCheckId;
+    var offlineIntStatusCheckId;
+    var _RegionCdArr = [];
+    var _GroupNoArr = [];
+    var intGroupLineArr = [];
+    var intGroupCircleArr = [];
+    var _groupTreeListCallBackData;
+
+    var onlineCenterStatusData;
+    var offlineCenterStatusData;
+    var onlineIntStatusData;
+    var offlineIntStatusData;
+    let _regionMap = new Map();
+    var _searchText = "";
+    let timeout = null;
+    const $start     = $('#start');
+    const $startTime = $('#start_time');
+    const $end       = $('#end');
+    const $endTime   = $('#end_time');
+    var _historyMap = new Map();
+    let _TreeList;
+    var interval = null;
+    var cnt = 0;
+
+    $start.val(getDateToStr(new Date()));
+    $end.val(getDateToStr(new Date()));
+
+    $startTime.val('00:00');
+    $endTime.val('23:59');
+
+    let _timeIdx;
+    function nodeClick(nodeId, regionId, name, lat, lng){
+        parent._RegionCd = regionId;
+        $('.node-li').removeClass('on');
+        $('#node-li-'+ nodeId).addClass('on');
+        if (parent._Level > 3) parent.map.setLevel(1);
+        parent.map.panTo(new parent.kakao.maps.LatLng(lat, lng));
+        if (_timeIdx) {
+            clearTimeout(_timeIdx);
+            _timeIdx = "";
+        }
+        _timeIdx = setTimeout(() => {
+            parent.getSignalInfo();
+            parent.customOverlayFnc(name,lng,lat);
+        }, 100 * 2);
+    }
+
+    function toggleCheckbox(event, nodeId) {
+        event.stopPropagation(); // 이벤트 버블링 방지
+        document.getElementById(`state_\${nodeId}`).click();
+    }
+
+
+    function getDateToStr(date){
+        const year = date.getFullYear();
+        let month = date.getMonth() + 1;
+        let day = date.getDate();
+        if (month < 10){
+            month = '0' + month;
+        }
+        if (day < 10) {
+            day = '0' + day;
+        }
+        return year.toString() + '-' + month.toString() + '-' + day.toString();
+    }
+
+    function getDateTimeToStr(date) {
+        let str = getDateToStr(date);
+        let hour = date.getHours();
+        let minute = date.getMinutes();
+        let seconds = date.getSeconds();
+        if (hour < 10){
+            hour = '0' + hour;
+        }
+        if (minute < 10){
+            minute = '0' + minute;
+        }
+        if (seconds < 10){
+            seconds = '0' + seconds;
+        }
+        str += ' ';
+        str += hour + ':' + minute + ':' + seconds;
+        return str;
+    }
+
+    function setAddrMap(data) {
+
+        _regionMap.clear();
+        for (let ii = 0; ii < data.length; ii++) {
+            if (data[ii].addr1 == null || data[ii].addr1 == '-') {
+                continue;
+            }
+            _regionMap.set(data[ii].addr1, data[ii].regionid);
+        }
+    }
+
+    function getAddrSi() {
+        return Array.from(_regionMap.keys());
+    }
+    function getEngAddr(addr){
+        let region_id = _regionMap.get(addr);
+        if (region_id === null || region_id === undefined) {
+            return "U99"
+        }
+        return region_id;
+    }
+
+    // setTreeList();
+    getIntTreeList();
+    controllerOnOffFunc();
+    /*
+    * 신호 제어기 상태 정보
+    * 1 : 정상, 2: 교차로 시각 오류 0: 마지막통신 시간 3초이상 지연시
+     */
+
+    function controllerOnOffFunc() {
+        if (timeout) clearTimeout(timeout);
+        const start = new Date();
+
+        let now = new Date();
+        const countMap = new Map();
+        window.parent._signalInfoArr.forEach(function (el, idx) {
+            if (!countMap.get(el.regionId)) {
+                countMap.set(el.regionId, {tot : 0, error : 0});
+            }
+            const regionCounter = countMap.get(el.regionId);
+
+            regionCounter.tot++;
+            if (!el.commStatus) {
+                regionCounter.error++;
+                el.status = 0;  // 데이터 없음(현재 통신하고 있지 않음)
+            }
+            else {
+                el.status = 1;
+            }
+            $('#int-status-'+ el.nodeId).attr('class', 'comm'+ el.status);
+        });
+
+        countMap.forEach((obj, key)=>{
+            $('#intErrCnt_'+ key).text(obj.error);
+            $('#intTotal_'+ key).text(obj.tot);
+        });
+        const end = new Date();
+        let diff = end.getTime() - start.getTime();
+        if (diff >= 1000) {
+            diff = 1000;
+        }
+
+        timeout = setTimeout(()=>controllerOnOffFunc(), 1000 - diff);
+    }
+
+
+    function getIntTreeList() {
+        $('.treeLoading').show();
+        const url = 'getIntTreeList.do';
+        // requestService(url, '', getIntTreeListCallback);
+        requestService(url, '', drawIntTree);
+    }
+
+    function drawIntTree(data) {
+        treeJson = [];
+        if (data && data.length) {
+            data.sort((a, b)=> a.addr1.localeCompare(b.addr1) || a.name.localeCompare(b.name));
+            treeJson = data;
+        }
+        const treeData = getIntTreeData(data);
+        const intTree = $('#intTree');
+        intTree.jstree({
+            "themes": {"stripes": false},
+            "plugins": ["wholerow"],
+            'core' : { 'data' : treeData }
+        });
+
+        // intTree.on('loaded.jstree', function (e, data) {
+        //     if (data.instance.get_json('#', { flat: true }).length === 0) {
+        //         intTree.html('<div class="empty-box">표시할 항목이 없습니다.</div>');
+        //     }
+        // })
+
+        intTree.on('select_node.jstree', function (e, data) {
+            const { lat, lng, type, id, name, regionid } = data.node.data;
+            if (type === 'int') nodeClick(id, regionid, name, lat, lng);
+            // moveCenter(lat, lng);
+        });
+
+        $('.treeLoading').hide();
+    }
+
+    function getStatus(stts){
+        if ( stts == 1 ) {
+            return "On"; //정상
+        }
+        else if( stts == 2 ){
+            return "Off"; //교차로시각이상
+        }
+        else{
+            return "Null"; // 통신시간 3초이상
+        }
+    }
+
+    function isNull(str) {
+        return (str === "" || str === null || str === undefined);
+    }
+
+    //유선 센터상테 체크..
+    function onlineCenterStatusCheck() {
+
+        if (onlineCenterStatusCheckId != null) clearTimeout(onlineCenterStatusCheckId);
+        onlineCenterStatusCheckId = setTimeout('onlineCenterStatusCheck()', 5 * 1000);
+
+        var url = 'getOnlineCenterStatus.do';
+        requestService(url, '', onlineCenterStatusCheckCallback);
+    }
+
+    function onlineCenterStatusCheckCallback(data) {
+        onlineCenterStatusData = data;
+        drawCenterStatus(0, data);
+    }
+
+    //무선 센터상테 체크..
+    function offlineCenterStatusCheck() {
+
+        if (offlineCenterStatusCheckId != null) clearTimeout(offlineCenterStatusCheckId);
+        offlineCenterStatusCheckId = setTimeout('offlineCenterStatusCheck()', 5 * 1000);
+
+        var url = 'getOfflineCenterStatus.do';
+        requestService(url, '', offlineCenterStatusCheckCallback);
+    }
+
+    function offlineCenterStatusCheckCallback(data) {
+        offlineCenterStatusData = data;
+        drawCenterStatus(1, data);
+    }
+
+    function drawCenterStatus(network, data) {
+        let regionCd;
+        let totCnt;
+        let errCnt;
+        let commState;
+        let commImg = '';
+
+        for (let ii = 0; ii < data.length; ii++) {
+            regionCd = data[ii].REGION_CD;
+            totCnt = data[ii].TOT;
+            errCnt = data[ii].ERR_CNT;
+            commState = data[ii].COMM_STATE;
+
+            if (commState == 1) {
+                commImg = 'images/commOff.png';
+                $('.intErrCnt_' + network + '_' + regionCd).text(totCnt);
+            } else {
+                commImg = 'images/commOn.png';
+                $('.intErrCnt_' + network + '_' + regionCd).text(errCnt);
+            }
+            if (network == 0) $('.regionComm_' + network + '_' + regionCd).attr('src', commImg);
+
+            $('.intTotal_' + network + '_' + regionCd).text(totCnt);
+        }
+    }
+
+    //유선 제어기 상태 체크..
+    function onlineIntStatusCheck() {
+        if (_RegionCdArr.length > 0) {
+            if (onlineIntStatusCheckId != null) clearTimeout(onlineIntStatusCheckId);
+            onlineIntStatusCheckId = setTimeout('onlineIntStatusCheck()', 5 * 1000);
+
+            var url = 'getOnlineIntStatus.do';
+            var param = 'regionCd=' + _RegionCdArr;
+            requestService(url, param, onlineIntStatusCheckCallback);
+        }
+    }
+
+    function onlineIntStatusCheckCallback(data) {
+        onlineIntStatusData = data;
+        drawIntStatus(0, data);
+    }
+
+    //무선 제어기 상태 체크..
+    function offlineIntStatusCheck() {
+        if (_RegionCdArr.length > 0) {
+            if (offlineIntStatusCheckId != null) clearTimeout(offlineIntStatusCheckId);
+            offlineIntStatusCheckId = setTimeout('offlineIntStatusCheck()', 5 * 1000);
+
+            var url = 'getOfflineIntStatus.do';
+            var param = 'regionCd=' + _RegionCdArr;
+            requestService(url, param, offlineIntStatusCheckCallback);
+        }
+    }
+
+    function offlineIntStatusCheckCallback(data) {
+        offlineIntStatusData = data;
+        drawIntStatus(1, data);
+    }
+
+    function drawIntStatus(network, data) {
+        var commState;
+        if (network == 0) {
+            for (var i = 0; i < onlineCenterStatusData.length; i++) {
+                if (onlineCenterStatusData[i].REGION_CD == data[0].REGION_CD) {
+                    commState = onlineCenterStatusData[i].COMM_STATE;
+                    break;
+                }
+            }
+        } else {
+            commState = 0;
+        }
+
+        var regionCd;
+        var siguguCd;
+        var intNoArr;
+        var commOnOffFlagArr;
+        var commOnOffImg;
+
+        for (var ii = 0; ii < data.length; ii++) {
+            regionCd = data[ii].REGION_CD;
+            sigunguCd = data[ii].SIGUNGU_CD;
+            intNoArr = data[ii].INT_NO.split('|');
+            commOnOffFlagArr = data[ii].COMM_ON_OFF_FLAG.split('|');
+
+            $('.guIntTotal_' + network + '_' + sigunguCd).text(intNoArr.length);
+
+            //sigunguCd = 41190 부천시 , 41430 의왕시, 41410 군포시
+
+            //commState = 1;
+
+            //시군구 코드가 없으면 상태정보이미지가 없데이트가 안된다...
+            if (commState == 0) {
+                var guIntErrCnt = 0;
+                for (var jj = 0; jj < intNoArr.length; jj++) {
+                    if (commOnOffFlagArr[jj] == 1) {
+                        guIntErrCnt++;
+                        commOnOffImg = 'background-image:url("images/intStatusOff.png");background-position:center center';
+                    } else {
+                        commOnOffImg = 'background-image:url("images/intStatusOn.png");background-position:center center';
+                    }
+                    $('#' + regionCd + '_' + network + '_' + intNoArr[jj] + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                }
+                $('.guIntErrCnt_' + network + '_' + sigunguCd).text(guIntErrCnt);
+            } else {
+                for (var j = 0; j < intNoArr.length; j++) {
+                    commOnOffImg = 'background-image:url("images/intStatusOff.png");background-position:center center';
+                    $('#' + regionCd + '_' + network + '_' + intNoArr[j] + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                }
+                $('.guIntErrCnt_' + network + '_' + sigunguCd).text(intNoArr.length);
+            }
+
+        }
+    }
+
+
+    function goIntMenu() {
+        $('.tabs > li').eq(0).addClass('on');
+        $('.tabs > li').eq(1).removeClass('on');
+        // $('.treeLoading').hide();
+        $('#historyList').hide();
+        $('#intTree').show();
+        $('.tree-search').css('display', 'flex');
+        $('.historyBox').css('display', 'none');
+        $('.leftMenu .evp-cur').hide();
+        $('.evp_legend', parent.document).hide();
+        unsetEvpCurr();
+
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        bottomFrame.find('#cvibBottomBodyTable').show();
+        bottomFrame.find('#evpBottomBodyTable').hide();
+
+        if (_historyMap && _historyMap.size) {
+            if(interval) clearInterval(interval);
+            _historyMap.forEach((obj)=> {
+                if (obj.get('draw')) {
+                    obj.get('draw').clear();
+                    obj.set('draw', null);
+                }
+            });
+            $('.history-table tr.on').removeClass('on');
+            bottomFrame.find('#evpBottomInfo').empty();
+            imageOnOff('play', false);
+            imageOnOff('stop', false);
+            imageOnOff('pause', false);
+        }
+        //.show();
+    }
+
+    function unsetEvpCurr() {
+        if (window.parent._EmergencyMap && window.parent._EmergencyMap.size) {
+            window.parent._EmergencyMap.forEach((obj)=> obj.clear());
+            window.parent._EmergencyMap.clear();
+            $('.evp-table tr.on').removeClass('on');
+            window.parent._isClick = null;
+        }
+    }
+
+    function goEvpMenu() {
+        $('.tabs > li').eq(1).addClass('on');
+        $('.tabs > li').eq(0).removeClass('on');
+        $('.evp_legend', parent.document).show();
+        // $('.treeLoading').show();
+        $('#historyList').show();
+        $('#intTree').hide();
+        $('.tree-search').css('display', 'none');
+        $('.historyBox').css('display', 'flex');
+        $('.leftMenu .evp-cur').show();
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        bottomFrame.find('#cvibBottomBodyTable').hide();
+        bottomFrame.find('#evpBottomBodyTable').show();
+    }
+
+    function searchHistoryData() {
+        const $emptyBox = $('.empty-history');
+        const $historyLoading = $('.history-loading');
+        const $region       = $('#region');
+        const regionVal    = $region.val();
+        const startVal     = $start.val();
+        const endVal       = $end.val();
+        const startTimeVal = $startTime.val();
+        const endTimeVal   = $endTime.val();
+        if (!startVal) {
+            alert('검색시작 년월일을 입력해주세요');
+            return $start.focus();
+        }
+        if (!startTimeVal) {
+            alert('검색시작 시간을 입력해주세요');
+            return $startTime.focus();
+        }
+        if (!endVal) {
+            alert('검색종료 년월일을 입력해주세요');
+            return $end.focus();
+        }
+
+        if (!endTimeVal) {
+            alert('검색종료 시간을 입력해주세요');
+            return $endTime.focus();
+        }
+
+        const param = {
+            start : startVal + ' ' + startTimeVal + ':00',
+            end : endVal + ' ' + endTimeVal + ':59',
+            region : regionVal
+        }
+        if (param.start > param.end) {
+            alert('검색시작 일시가 종료 일시 보다 큽니다.');
+            return $end.focus();
+        }
+
+        const $historyTable = $('.history-table');
+        $historyTable.empty();
+        $emptyBox.css('display', 'none');
+        $historyLoading.css('display', 'flex');
+        imageOnOff('play', false);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+
+        if (_historyMap.size) {
+            _historyMap.forEach((obj)=>{
+                if (obj.get('draw')) obj.get('draw').clear();
+            });
+            _historyMap.clear();
+            if (interval) {
+                clearInterval(interval);
+                interval = null;
+                cnt = 0;
+            }
+            const bottomFrame = $('#iframeBottomList', parent.document).contents();
+            bottomFrame.find('#evpBottomInfo').empty();
+        }
+
+        window.parent.requestService('getEvpHistory.do', param, (rec)=>{
+            let str = ''
+            if (rec && rec.length) {
+                for (let history of rec) {
+                     _historyMap.set(history.service_id, new Map());
+                    const {event_list, phase_list, node_list} = history;
+
+                    const dateMap = new Map();
+                    if (event_list.length && phase_list.length) {
+                        event_list.forEach((eventObj)=>{
+                            dateMap.set(eventObj.clct_dt, new Map());
+                            dateMap.get(eventObj.clct_dt).set('event', eventObj);
+                            dateMap.get(eventObj.clct_dt).set('sig', []);
+                        });
+
+                        phase_list.forEach((obj)=>{
+                            if (!dateMap.get(obj.clct_dt)) {
+                                dateMap.set(obj.clct_dt, new Map());
+                                dateMap.get(obj.clct_dt).set('event', null);
+                                dateMap.get(obj.clct_dt).set('sig', []);
+
+                            }
+                            dateMap.get(obj.clct_dt).get('sig').push(obj);
+                        });
+
+                        if (dateMap.size) {
+                            history.event_list = [];
+                            history.phase_list = [];
+                            const dateArr = Array.from(dateMap.keys()).sort();
+                            let beforeSig;
+                            let beforeEvent;
+
+                            for (let date of dateArr) {
+                                const objMap = dateMap.get(date);
+                                if (objMap.get('event') === null && beforeEvent === undefined) {
+                                    beforeEvent = event_list[0];
+                                }
+                                else if (objMap.get('event')){
+                                    beforeEvent = objMap.get('event');
+                                }
+                                const eventData = {...beforeEvent};
+
+                                eventData.clct_dt = date;
+
+                                if (objMap.get('sig').length === 0 && beforeSig === undefined) {
+                                    beforeSig = dateMap.get(phase_list[0].clct_dt).get('sig');
+                                }
+                                else if (objMap.get('sig').length) {
+                                    beforeSig = objMap.get('sig');
+                                }
+
+                                if (beforeSig.length !== node_list.length) {
+                                    for (let node of node_list) {
+                                        let idx = objMap.get('sig').findIndex(obj => obj.seq_no === node.seq_no)
+                                        if (idx === -1) {
+                                            if (new Date(date).getTime() - new Date(dateArr[0]).getTime() != 0) {
+                                                let startTime = new Date(dateArr[0]).getTime();
+                                                let currTime = new Date(date).getTime();
+                                                let beforeObj = null
+
+                                                for (let ii = currTime; ii >= startTime; ii -= 1000) {
+                                                    const key = getDateTimeToStr(new Date(ii));
+                                                    if (dateMap.get(key)) {
+                                                        const beforeIdx = dateMap.get(key).get('sig').findIndex(obj => obj.seq_no === node.seq_no);
+
+                                                        if (beforeIdx > -1) {
+                                                            beforeObj = dateMap.get(key).get('sig')[beforeIdx];
+                                                            break;
+                                                        }
+                                                    }
+                                                }
+                                                if (beforeObj) {
+                                                    beforeSig.push({
+                                                        seq_no : beforeObj.seq_no,
+                                                        clct_dt : date,
+                                                        service_id : beforeObj.service_id,
+                                                        a_end_angle : beforeObj.a_end_angle,
+                                                        a_end_lat:beforeObj.a_end_lat,
+                                                        a_end_lng:beforeObj.a_end_lng,
+                                                        a_flow_no:beforeObj.a_flow_no,
+                                                        a_head_angle:beforeObj.a_head_angle,
+                                                        a_head_lat:beforeObj.a_head_lat,
+                                                        a_head_lng: beforeObj.a_head_lng,
+                                                        a_mid_lat:beforeObj.a_mid_lat,
+                                                        a_mid_lng:beforeObj.a_mid_lng,
+                                                        a_ring_phase:beforeObj.a_ring_phase,
+                                                        b_end_angle:beforeObj.b_end_angle,
+                                                        b_end_lat:beforeObj.b_end_lat,
+                                                        b_end_lng:beforeObj.b_end_lng,
+                                                        b_flow_no:beforeObj.b_flow_no,
+                                                        b_head_angle:beforeObj.b_head_angle,
+                                                        b_head_lat:beforeObj.b_head_lat,
+                                                        b_head_lng:beforeObj.b_head_lng,
+                                                        b_mid_lat:beforeObj.b_mid_lat,
+                                                        b_mid_lng:beforeObj.b_mid_lng,
+                                                        b_ring_phase:beforeObj.b_ring_phase,
+                                                        hold_phase:beforeObj.hold_phase,
+                                                        lat : beforeObj.lat,
+                                                        lng : beforeObj.lng,
+                                                        node_id : beforeObj.node_id,
+                                                        node_nm : beforeObj.node_nm,
+                                                        plan_class:beforeObj.plan_class,
+                                                        rem_dist:beforeObj.rem_dist,
+                                                        state : beforeObj.state,
+                                                    })
+                                                }
+
+                                            }
+
+                                        }
+                                    }
+                                }
+                                const sigData = beforeSig;
+                                history.event_list.push(eventData);
+                                history.phase_list.push(sigData);
+                            }
+                        }
+                    }
+
+                     _historyMap.get(history.service_id).set('obj', history);
+                     _historyMap.get(history.service_id).set('draw', null);
+                    str += `<tr class="hs_\${history.service_id}" onclick="drawHistory('\${history.service_id}')"><td>\${history.clct_dt}</td><td>\${history.service_id}</td><td>\${history.service_nm}</td></tr>`;
+                }
+            }
+
+            $historyLoading.css('display', 'none');
+
+            if (str === '') {
+                $emptyBox.css('display', 'flex');
+            }
+
+            $historyTable.html(str);
+        }, true, ()=>{
+            $emptyBox.css('display', 'flex');
+            $historyLoading.css('display', 'none');
+        });
+    }
+
+    function drawHistory(serviceId) {
+        unsetEvpCurr();
+        if (_historyMap.size) {
+            _historyMap.forEach((obj, key)=>{
+                const beforeDraw = obj.get('draw');
+                if (beforeDraw && serviceId !== key) {
+                    beforeDraw.clear();
+                    obj.set('draw', null);
+                }
+            });
+
+            if (_historyMap.get(serviceId).get('draw')) {
+                return;
+            }
+            else {
+                if (interval) {
+                    clearInterval(interval);
+                    interval = null;
+                    cnt = 0;
+                }
+            }
+        }
+
+        const obj = _historyMap.get(serviceId).get('obj');
+        $('#historyList tr.on').removeClass('on');
+        $('.hs_' + serviceId).addClass('on');
+        const drawObj = {...obj};
+        const { event_list, phase_list } = obj;
+
+        if (event_list && event_list.length) drawObj.event_list = [drawObj.event_list[0]];
+        if (phase_list && phase_list.length) drawObj.phase_list = phase_list[0];
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        const bottomInfo  = bottomFrame.find('#evpBottomInfo');
+
+        let str = '';
+
+        event_list.forEach((event, idx)=>{
+            str += getBottomListEl(obj, event, idx);
+        });
+
+        bottomInfo.html(str);
+        const draw = window.parent.drawHistory(drawObj);
+        _historyMap.get(serviceId).set('draw', draw);
+        bottomFrame.find('.bottomBody').scrollTop(0);
+        imageOnOff('play', true);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+    }
+
+    function imageOnOff(name, isOn) {
+        const el = $('#' + name);
+        const eventName = isOn ? 'addClass' : 'removeClass';
+        el[eventName]('on');
+
+        if (el.attr('src') === getImageName(name, !isOn)) {
+            el.attr('src', getImageName(name, isOn));
+        }
+    }
+
+    /**
+     * 긴급차량우선신호 bottom list 생성
+     * @param obj 서비스 정보
+     * @param event 발생 이벤트 정보
+     * @param idx 인덱스 번호
+     * @returns {string} html 리스트 목록 반환
+     */
+    function getBottomListEl(obj, event, idx) {
+        const regionNm = $('#region option[value="'+obj.service_id.substr(0,3)+'"]').text(); //왼쪽 3글자는 센터 번호를 의미
+        let className = '';
+
+        if (idx === 0) { //처음 그릴때 첫줄을 기반으로 그리므로 선택 표시
+            className = 'on';
+        }
+        let curSpd = '-';
+        if (event.cur_spd) { // 현재 스피드가 있을경우 천단위 콤마
+            curSpd = event.cur_spd.toLocaleString('ko-KR');
+        }
+
+        let remDist = '-';
+        if (event.rem_dist) {// 남은 거리가 있을경우 천단위 콤마
+            remDist = event.rem_dist.toLocaleString('ko-KR');
+        }
+
+
+        let clctDt = event.clct_dt.replace(/\:|\-| /gi, '') //시간 기호(':', '-', ' ') 제거 후 값만 추출 하여 table 로우 ID 부여
+        let str = `<tr onclick="moveLocation('\${obj.service_id}', this)" class="\${className}" id="\${obj.service_id}_\${clctDt}">`
+                    +'<td>'+event.clct_dt+'</td>'
+                    +'<td>'+regionNm+'</td>'
+                    +'<td>'+obj.service_id+'</td>'
+                    +'<td>'+obj.service_nm+'</td>'
+                    +'<td>'+obj.ev_no+'</td>'
+                    +'<td>'+event.cur_lng+'</td>'
+                    +'<td>'+event.cur_lat+'</td>'
+                    +'<td>'+curSpd+'</td>'
+                    +'<td>'+remDist+'</td>'
+                +'</tr>';
+        return str;
+    }
+
+    /**
+     * 시뮬레이션 재생 / 멈춤 버튼 활성화 비활성화 이미지명 가져오기
+     * @param name 요소 ID name (ID 와 이미지 이름이 동일함)
+     * @param isOn 활성화/비활성화(true/false)
+     * @returns {string} 이미지명 반환
+     */
+    function getImageName(name, isOn) {
+        let imgName = '/images/' + name;
+        const onName = '_on';
+        const extName = '.png';
+        if (isOn) {
+            imgName += onName;
+        }
+        return imgName + extName;
+    }
+
+
+    /**
+     * 시뭏레이션 재생
+     */
+    function playSimulation() {
+        if (!$('#play').hasClass('on')) return; //비활성화중일 때만 실행
+        if (interval) {
+            clearInterval(interval);
+            interval = null;
+        }
+        const serviceId = $('.history-table tr.on').attr('class').replace(/hs_| on/gi, '');// 선택 로우에서 service id 추출
+        let evp = _historyMap.get(serviceId); //Map 에 저장된 해당 service_id 데이터 정보 가져오기
+        let obj = evp.get('obj'); //object 정보
+        let draw = evp.get('draw'); // marker handler 정보
+        if (!draw) {// handler 가 없다면 생성
+            drawHistory(serviceId);
+            draw = evp.get('draw');
+        }
+
+        const { event_list, phase_list } = obj; //이벤트 리스트와 신호 리스트
+
+        //버튼 활성화/비활성화 적용
+        imageOnOff('stop', true);
+        imageOnOff('pause', true);
+        imageOnOff('play', false);
+
+        interval = setInterval(()=>{
+            cnt++;
+            if (event_list.length === cnt) {
+                return stopSimulation();
+            }
+            const bottomFrame = $('#iframeBottomList', parent.document).contents();
+            bottomFrame.find('#evpBottomInfo tr.on').removeClass('on');
+            let clctDt = event_list[cnt].clct_dt.replace(/\:|\-| /gi, '')
+            const bottomInfo = bottomFrame.find('#evpBottomInfo #'+obj.service_id +'_'+clctDt);
+            bottomInfo.addClass('on');
+            bottomFrame.find('.bottomBody').scrollTop(32 * cnt);
+
+            const position = window.parent.getKakaoPosition(event_list[cnt].cur_lat, event_list[cnt].cur_lng);
+            draw.car.moveMarker(position);
+            setSigState(phase_list[cnt], draw);
+
+        }, 500);
+    }
+
+    function setSigState(phase, draw) {
+        phase.forEach((sigObj)=>{
+            if (draw.sig.get(sigObj.service_id + '_' + sigObj.seq_no) === undefined) {
+                draw.sig.set(sigObj.service_id + '_' + sigObj.seq_no, window.parent.createSig(sigObj));
+            }
+            const evpSig = draw.sig.get(sigObj.service_id + '_' + sigObj.seq_no);
+            evpSig.createRing(sigObj);
+        });
+
+        draw.sig.forEach((obj, key)=>{
+            const idx = phase.findIndex((sig)=>{
+                return sig.service_id + '_' + sig.seq_no === key;
+            })
+
+            if (idx === -1) {
+                obj.deleteRing();
+                obj.setState(1);
+            }
+        })
+    }
+
+    function pauseSimulation() {
+        if (!$('#pause').hasClass('on')) {
+            return;
+        }
+        clearInterval(interval);
+        imageOnOff('pause', false);
+        imageOnOff('play', true);
+    }
+
+    function stopSimulation() {
+        if (!$('#stop').hasClass('on')) {
+            return;
+        }
+        clearInterval(interval);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+        imageOnOff('play', true);
+        cnt = 0;
+        const serviceId = $('.history-table tr.on').attr('class').replace(/hs_| on/gi, '');
+        clearSimulator();
+        drawHistory(serviceId);
+    }
+
+    function clearSimulator() {
+        _historyMap.forEach((obj)=>{
+            const beforeDraw = obj.get('draw');
+            if (beforeDraw) {
+                beforeDraw.clear();
+                obj.set('draw', null);
+            }
+        })
+    }
+
+    const initialArr = [
+        'ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ',
+        'ㅆ','ㅇ','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
+    ];
+
+    const mixIntialArr = {
+        'ㄳ': 'ㄱ' + 'ㅅ',
+        'ㄵ': 'ㄴ' + 'ㅈ',
+        'ㄶ': 'ㄴ' + 'ㅎ',
+        'ㄺ': 'ㄹ' + 'ㄱ',
+        'ㄻ': 'ㄹ' + 'ㅁ',
+        'ㄼ': 'ㄹ' + 'ㅂ',
+        'ㄽ': 'ㄹ' + 'ㅅ',
+        'ㄾ': 'ㄹ' + 'ㅌ',
+        'ㄿ': 'ㄹ' + 'ㅍ',
+        'ㅀ': 'ㄹ' + 'ㅎ',
+        'ㅄ': 'ㅂ' + 'ㅅ'
+    };
+
+    function getMixInitialSeparate(text) {
+        let str = '';
+        for (let ii = 0; ii < text.length; ii++) {
+            str += mixIntialArr[text[ii]] || text[ii];
+        }
+        return str;
+    }
+
+    function getChosung(text) {
+        let result = '';
+        for (let i = 0; i < text.length; i++) {
+            const code = text.charCodeAt(i);
+            if (code >= 0xAC00 && code <= 0xD7A3) {
+                const uniIndex = code - 0xAC00;
+                const cho = Math.floor(uniIndex / 588);
+                result += initialArr[cho];
+            } else {
+                result += text[i];
+            }
+        }
+        return result;
+    }
+    function isMatch(node, searchText) {
+        const { nodeid, name, regionid, lat, lng, addr1, addr2, addr3, ipaddr } = node;
+        const baseInfo = `\${regionid}_\${addr1}_\${nodeid}_\${name}`.toLowerCase();
+        const mixChosung = getMixInitialSeparate(searchText);
+        return (baseInfo.includes(searchText.toLowerCase()) || `\${lng},\${lat}` === searchText
+            || getChosung(addr1 || '').includes(mixChosung)
+            || getChosung(name || '').includes(mixChosung));
+    }
+
+    function searchTree(ev) {
+        if (!ev || ev.key === 'Enter') {
+            const searchText = $('#searchText').val();
+            let searchData = [];
+            if (searchText) {
+                treeJson.forEach((obj)=>{
+                    if (isMatch(obj, searchText)) {
+                        searchData.push(obj);
+                    }
+                });
+            }
+            else {
+                searchData = treeJson;
+            }
+            const newData = getIntTreeData(searchData);
+            const intTree = $('#intTree');
+            intTree.jstree(true).settings.core.data = newData;
+            intTree.jstree(true).refresh();
+        }
+    }
+
+    function getIntTreeData(data) {
+        const treeData = [];
+        window.parent._signalInfoArr = [];
+        if (data && data.length) {
+            const checkMap = new Map();
+            for (let obj of data) {
+                const {nodeid, name, regionid, lat, lng, addr1, addr2, addr3, ipaddr} = obj;
+                let regionId = regionid;
+                if (isNull(regionId)) {
+                    regionId = '9999';
+                }
+                const signal = new SignalInfo(nodeid, name, lat, lng, addr1, addr2, addr3, regionid);
+                window.parent._signalInfoArr.push(signal);
+                const intStatus = getStatus(signal.status);
+
+                if (!checkMap.get(regionId)) {
+                    let text = `<strong class="centerNm_\${regionId}">\${addr1}</strong>
+                                    &nbsp;( 전체 : <span class="intTotal" id="intTotal_\${regionId}" style="color:#4A90E2"></span> / 이상 :
+                                    <span class="intErrCnt" id="intErrCnt_\${regionId}" style="color:#D9534F"></span> )`;
+                    checkMap.set(regionId, true);
+                    const region = { "id": regionid, "parent": "#", "text": text,
+                        "data" : {name: addr1, id: regionid, type: 'region'} };
+                    treeData.push(region);
+                }
+
+                let text = `<div style="display: flex; gap: 5px; align-items: center; font-size: 11px;">
+                                    <div id="int-status-\${nodeid}" class="comm\${intStatus}"></div>
+                                    <div title="\${name} (\${nodeid})">\${name} (\${nodeid})</div>
+                                </div>`;
+                const node = { "id": regionid + '_' + nodeid, "parent": regionid, "text": text,
+                    "data" : { lat, lng ,name: name, id: nodeid, regionid : regionid, type: 'int'} };
+                treeData.push(node);
+            }
+            return treeData;
+        }
+    }
+</script>
+</body>
+</html>
+

+ 1399 - 0
src/main/webapp/WEB-INF/jsp/treeListFrame_new.jsp

@@ -0,0 +1,1399 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ page session="false" %>
+<!doctype html>
+<%--<html lang="ko" style="min-width:300px !important;overflow:hidden">--%>
+<html lang="ko">
+<head>
+    <%@ include file="/WEB-INF/jsp/common.jsp" %>
+    <meta name="_csrf" content="${_csrf.token}">
+    <meta name="_csrf_header" content="${_csrf.headerName}">
+    <link rel="stylesheet" type="text/css" href="/css/common.css">
+    <link rel="stylesheet" type="text/css" href="/css/main_new.css">
+    <link rel="stylesheet" href="/css/themes/default/style.css">
+    <title>교차로 목록</title>
+</head>
+<%--<body class="sang" style="min-width:305px !important;overflow:hidden">--%>
+<body class="sang">
+<div class="leftMenu">
+    <ul class="tabs">
+        <li class="on" onclick="goIntMenu()"><div id="tab1"><img src="/images/int_icon.png" width="17px" alt="교차로 아이콘">교차로</div></li>
+        <li onclick="goEvpMenu()"><div id="tab2"><img src="/images/evp_icon.png" width="17px" alt="긴급차량우선신호 아이콘">긴급차량우선신호</div></li>
+<%--        <li><a href="javascript:goIntMenu()" class="tab" id="tab1"><img src="images/tab01_on.png" alt="교차로메뉴"/></a></li>--%>
+<%--        <li><a href="javascript:goEvpMenu()" class="tab" id="tab2"><img src="images/tab02_off.png" alt="긴급차량우선신호메뉴"/></a></li>--%>
+<%--        <div style="position: absolute; left: 95px; top: 5px;"><img src="images/map/btn_alert_A.gif" style="width: 22px"></div>--%>
+    </ul>
+    <div id="onOffTree"></div>
+<%--    <div class="searchBox">--%>
+<%--        <input type="text" id="searchText" placeholder="검색어를 입력해주세요" autocomplete="false" onkeyup="searchTreeData(event)"><label for="searchText" style="display: none;"></label>--%>
+<%--        <div class="search-button" onclick="searchTreeData()">검색</div>--%>
+<%--    </div>--%>
+    <div class="evp-cur">
+        <div class="title">긴급차량우선신호 서비스 현황</div>
+        <div id="evp-cur-list">
+            <table>
+                <thead>
+                    <tr>
+                        <th width="23%">발생시각</th>
+                        <th width="40%">서비스 ID</th>
+                        <th width="37%">서비스명</th>
+                    </tr>
+                </thead>
+                <tbody class="evp-table">
+                    <tr>
+                        <td colspan="3" class="empty-evp">조회된 내용이 없습니다.</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <div class="historyBox">
+        <div class="title">긴급차량우선신호 서비스 이력</div>
+        <div>
+            <div>
+                <div>센터</div>
+                <label style="display: none;" for="region"></label>
+                <select id="region">
+                    <option value="ALL" selected>전체</option>
+                    <c:if test="${fn:length(center) != 0}">
+                        <c:forEach var="item" items="${center}">
+                            <option value="${item.centerId}">${item.centerNm}</option>
+                        </c:forEach>
+                    </c:if>
+                </select>
+            </div>
+            <div>
+                <div>시작</div>
+                <input type="date" id="start" autocomplete="false"><input type="time" id="start_time">
+                <label style="display: none;" for="start"></label>
+                <label style="display: none;" for="start_time"></label>
+            </div>
+            <div>
+                <div>종료</div>
+                <input type="date" id="end" autocomplete="false" ><input type="time" id="end_time">
+                <label style="display: none;" for="end"></label>
+                <label style="display: none;" for="end_time"></label>
+            </div>
+        </div>
+        <div class="history-btn" onclick="searchHistoryData()">검색</div>
+    </div>
+    <div id="intTree"></div>
+    <div id="historyList">
+        <table>
+            <thead>
+                <tr>
+                    <th width="23%">발생시각</th>
+                    <th width="40%">서비스 ID</th>
+                    <th width="37%">서비스명</th>
+                </tr>
+            </thead>
+            <tbody class="history-table"></tbody>
+        </table>
+        <div class="empty-history">조회된 내용이 없습니다.</div>
+        <div class="history-loading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
+    </div>
+    <div class="play-box">
+        <div>
+            <div>시뮬레이션 재생/멈춤</div>
+            <img id="play" onclick="playSimulation()" src="/images/play.png" alt="시뮬레이션 재생버튼" width="24px">
+            <img id="pause" onclick="pauseSimulation()" src="/images/pause.png" alt="시뮬레이션 멈춤버튼" width="24px">
+            <img id="stop" onclick="stopSimulation()" src="/images/stop.png" alt="시뮬레이션 초기화버튼" width="24px">
+        </div>
+    </div>
+    <div class="treeLoading"><img src="/css/themes/classic/throbber.gif" alt="로딩 이미지"></div>
+</div>
+<script src="/js/common/moment.js"></script>
+<script src="/js/common/common-all.js"></script>
+<script src="/js/signalInfo.js"></script>
+<script src="/js/common/jstree.min.js"></script>
+<script>
+
+    var treeJson = null;
+    var groupTreeJson = null;
+    var treeCtrl;
+    var onlineCenterStatusCheckId;
+    var offlineCenterStatusCheckId;
+    var onlineIntStatusCheckId;
+    var offlineIntStatusCheckId;
+    var _RegionCdArr = [];
+    var _GroupNoArr = [];
+    var intGroupLineArr = [];
+    var intGroupCircleArr = [];
+    var _groupTreeListCallBackData;
+
+    var onlineCenterStatusData;
+    var offlineCenterStatusData;
+    var onlineIntStatusData;
+    var offlineIntStatusData;
+    let _regionMap = new Map();
+    var _searchText = "";
+    let timeout = null;
+    const $start     = $('#start');
+    const $startTime = $('#start_time');
+    const $end       = $('#end');
+    const $endTime   = $('#end_time');
+    var _historyMap = new Map();
+    let _TreeList;
+    var interval = null;
+    var cnt = 0;
+
+    $start.val(getDateToStr(new Date()));
+    $end.val(getDateToStr(new Date()));
+
+    $startTime.val('00:00');
+    $endTime.val('23:59');
+
+    class TreeList {
+        constructor(id, nodeArr, isSearch) {
+            this.id = id;
+            this.nodeArr = nodeArr;
+            this.isSearch = isSearch;
+            this.init();
+        }
+
+        init = () => {
+            const container = $(`#\${this.id}`);
+            if (container.length === 0) {
+                return console.error("Please Check Element ID(Not Found Element)");
+            }
+            if (container.css('height') === 0 || container.css('width') === 0) {
+                console.error("Please Check Element Width or Height")
+            }
+            let ul = "";
+            if (this.isSearch) {
+                ul += `<div class="tree-search">
+                            <input type="text" id="searchText" placeholder="검색어를 입력해주세요" autocomplete="off">
+                            <div class="searchBtn">검 색</div>
+                        </div>`;
+            }
+            ul += `<ul class="tree-root"></ul>`;
+            const root = $(`<div class="tree-container">\${ul}</div>`)
+            container.append(root);
+            if (this.nodeArr && this.nodeArr instanceof Array && this.nodeArr.length) {
+                this.nodeArr.forEach((node)=>{
+                    const innerList = node.createEl(node);
+                    container.find('.tree-root').append($(innerList));
+                })
+            }
+
+            const searchBtn = $('.searchBtn');
+            searchBtn.on('click', ()=>{
+                this.searchKeyword();
+            });
+
+            $('#searchText').on('keyup', (e)=>{
+                if (e.key === 'Enter') {
+                    this.searchKeyword();
+                }
+            })
+        }
+
+        setNode = (node) => {
+            if (node && node.html) {
+                node.root
+            }
+        }
+
+        searchKeyword() {
+            const searchText = $("#searchText").val();
+            // const searchVal = this.nodeArr.filter((node)=> node.name.includes(searchText));
+            const nodeArr = $('.node-li');
+            nodeArr.removeClass('off');
+            if (isNull(searchText)) return;
+            let array = [];
+            this.nodeArr.forEach((node)=>{
+                array = array.concat(node.children);
+            });
+            if (array.length) {
+                nodeArr.addClass('off');
+
+                array.forEach((node)=>{
+                    const {addr1, name, lng, lat, nodeid} = node.obj;
+                    if ((addr1 && addr1.includes(searchText)) ||
+                        (name && name.includes(searchText)) ||
+                        (`\${lng},\${lat}` === searchText) ||
+                        nodeid.includes(searchText)) {
+                        $('#node-li-' + node.id).removeClass('off');
+                        $('#node-li-' + node.root).removeClass('off');
+                    }
+                })
+            }
+        }
+
+    }
+
+    class Node {
+        constructor({root, id, icon, text, state, isFolder, children, name, obj}) {
+            this.root = root;
+            this.id = id;
+            this.icon = icon;
+            this.text = text;
+            this.state = state;
+            this.isFolder = isFolder;
+            this.html;
+            this.name = name;
+            this.obj = obj;
+            this.children = children;
+        }
+
+        createEl = (node) => {
+            let img = "";
+            let nodeFolder= "";
+            let childDiv = "";
+            const { id, icon, text, children, isFolder} = node;
+            if (icon) {
+                img = `<img src="\${node.icon}" alt="node icon" style="user-select:none;">`;
+            }
+
+            if (isFolder) {
+                nodeFolder = `<div></div>`;
+            }
+
+            if (children && children instanceof Array && children.length) {
+                childDiv +=  `<ul class="node-group node-group-\${node.id}">`;
+                for (let child of children) {
+                    childDiv += this.createChildEl(child);
+                }
+                childDiv += `</ul>`;
+            }
+
+            return  `<li class="node-li" id="node-li-\${node.id}">
+                        <input type="checkbox" class="node_state" id="state_\${node.id}">
+                        <div style="width:100%;" onclick="toggleCheckbox(event, '\${node.id}')">
+                            \${nodeFolder}
+                            <span class="node-icon">\${img}</span>
+                            <div>\${node.text}</div>
+                        </div>
+                        \${childDiv}
+                    </li>`;
+        }
+
+        createChildEl = (node) => {
+            let img = "";
+            let nodeFolder= "";
+            let childDiv = "";
+            const { id, icon, text, children, isFolder} = node;
+            if (icon) {
+                img = `<img src="\${node.icon}" alt="node icon" style="user-select:none;">`;
+            }
+
+            return  `<li class="node-li" id="node-li-\${node.id}">
+                        <input type="checkbox" class="node_state" id="state_\${node.id}">
+                        <div onclick="nodeClick('\${node.id}', '\${node.root}', '\${node.obj.name}', '\${node.obj.lat}', '\${node.obj.lng}')">
+                            <span class="node-icon">\${img}</span>
+                            <div style="width: 100%;">\${node.text}</div>
+                        </div>
+                        \${childDiv}
+                    </li>`;
+        }
+
+        addChild = (node) => {
+            this.children.push(node);
+            $(`.node-group-\${this.id}`).append($(node.html));
+        }
+
+        removeChild = (nodeId) => {
+            if (this.children.length > 0) {
+                this.children = this.children.filter((node)=> node.id !== nodeId);
+                $(`#node-li-\${this.id} > #node-li-\${nodeId}`).remove();
+            }
+        }
+
+        setState = (isClick) => {
+            $(`.state_\${this.id}`).prop("checked", isClick);
+        }
+    }
+
+    let _timeIdx;
+    function nodeClick(nodeId, regionId, name, lat, lng){
+        parent._RegionCd = regionId;
+        $('.node-li').removeClass('on');
+        $('#node-li-'+ nodeId).addClass('on');
+        if (parent._Level > 3) parent.map.setLevel(1);
+        parent.map.panTo(new parent.kakao.maps.LatLng(lat, lng));
+        if (_timeIdx) {
+            clearTimeout(_timeIdx);
+            _timeIdx = "";
+        }
+        _timeIdx = setTimeout(() => {
+            parent.getSignalInfo();
+            parent.customOverlayFnc(name,lng,lat);
+        }, 100 * 2);
+    }
+
+    function toggleCheckbox(event, nodeId) {
+        event.stopPropagation(); // 이벤트 버블링 방지
+        document.getElementById(`state_\${nodeId}`).click();
+    }
+
+
+    function getDateToStr(date){
+        const year = date.getFullYear();
+        let month = date.getMonth() + 1;
+        let day = date.getDate();
+        if (month < 10){
+            month = '0' + month;
+        }
+        if (day < 10) {
+            day = '0' + day;
+        }
+        return year.toString() + '-' + month.toString() + '-' + day.toString();
+    }
+    function getDateTimeToStr(date) {
+        let str = getDateToStr(date);
+        let hour = date.getHours();
+        let minute = date.getMinutes();
+        let seconds = date.getSeconds();
+        if (hour < 10){
+            hour = '0' + hour;
+        }
+        if (minute < 10){
+            minute = '0' + minute;
+        }
+        if (seconds < 10){
+            seconds = '0' + seconds;
+        }
+        str += ' ';
+        str += hour + ':' + minute + ':' + seconds;
+        return str;
+    }
+
+    function setAddrMap(data) {
+
+        _regionMap.clear();
+        for (let ii = 0; ii < data.length; ii++) {
+            if (data[ii].addr1 == null || data[ii].addr1 == '-') {
+                continue;
+            }
+            _regionMap.set(data[ii].addr1, data[ii].regionid);
+        }
+    }
+
+    function getAddrSi() {
+        return Array.from(_regionMap.keys());
+    }
+    function getEngAddr(addr){
+        let region_id = _regionMap.get(addr);
+        if (region_id === null || region_id === undefined) {
+            return "U99"
+        }
+        return region_id;
+    }
+
+    // setTreeList();
+    getIntTreeList();
+    controllerOnOffFuc();
+
+    function searchTreeData(el) {
+        if (el && el.code !== "Enter") {
+            return;
+        }
+
+        const $searchText = $('#searchText');
+        _searchText = $searchText.val();
+        const oldTree = $('#intTree').jstree(true);
+        if (oldTree) {
+            oldTree.destroy();
+        }
+        setTreeList();
+        getIntTreeList();
+        $('#intTree').jstree("refresh");
+    }
+    /*
+    * 신호 제어기 상태 정보
+    * 1 : 정상, 2: 교차로 시각 오류 0: 마지막통신 시간 3초이상 지연시
+     */
+
+    function controllerOnOffFuc() {
+        if (timeout) clearTimeout(timeout);
+        const start = new Date();
+        let addrArr = getAddrSi();
+        for (let ll = 0; ll < addrArr.length; ll++) {
+
+            let ctnlCount =0;
+            let errorCount=0;
+            let now = new Date();
+            let preDate = moment(now.getTime()).add("-30", "s").format("YYYY-MM-DD HH:mm:ss");
+            let nxtDate = moment(now.getTime()).add("30", "s").format("YYYY-MM-DD HH:mm:ss");
+
+           // console.log( window.parent._signalInfoArr);
+            window.parent._signalInfoArr.forEach(function (el, idx) {
+                if (el.addr1 == addrArr[ll]) {
+                    ctnlCount++;
+                    if (!el.commStatus) {
+                        el.status = 0;  // 데이터 없음(현재 통신하고 있지 않음)
+                        errorCount++;
+                    }
+                    else {
+                        el.status = 1;
+                    }
+                    $('#int-status-'+ el.nodeId).attr('class', 'comm'+ el.status);
+                    // $('#' + el.nodeId + ' .jstree-anchor .jstree-icon').css({backgroundImage: 'url("images/intStatus_' + el.status + '.png")',
+                    //     backgroundPosition: 'center center'});
+                }
+            });
+            $('#intErrCnt_'+getEngAddr(addrArr[ll])).text(errorCount);
+            $('#intTotal_'+getEngAddr(addrArr[ll])).text(ctnlCount);
+        }
+        const end = new Date();
+        let diff = end.getTime() - start.getTime();
+        if (diff >= 1000) {
+            diff = 1000;
+        }
+
+        timeout = setTimeout(()=>controllerOnOffFuc(), 1000 - diff);
+    }
+
+
+    // function setTreeList() {
+    //     /**
+    //      ** ****************************************************
+    //      ** 레이어 리스트 Tree 생성
+    //      ** ****************************************************
+    //      */
+    //     treeCtrl = treeCtrl || (function () {
+    //
+    //         var Node = function (nodeName, data, id, icon) {
+    //             return {
+    //                 id: id,
+    //                 icon: icon,
+    //                 text: nodeName,
+    //                 state: {'opened': false, 'selected': false},
+    //                 data: data,         // @type: ol.Layer
+    //                 children: []
+    //             };
+    //         };
+    //
+    //         var _root = new Node('ROOT', 'item');
+    //
+    //         var findChild = function (parent, nodeName) {
+    //             for (var i = 0; i < parent.children.length; i++) {
+    //                 var child = parent.children[i];
+    //                 if (child.text == nodeName) {
+    //                     return child;
+    //                 }
+    //             }
+    //
+    //             return null;
+    //         };
+    //
+    //         // find parent which save item
+    //         var getParent = function (parent, group, data, id, icon, opt_make) {
+    //             opt_make = (typeof opt_make === 'undefined') ? true : false;
+    //
+    //             for (var i = 0; i < group.length; i++) {
+    //                 var child = findChild(parent, group[i]);
+    //                 // 없으면 Node 생성
+    //                 if (!child) {
+    //                     if (!opt_make) return null;
+    //                     child = new Node(group[i], data);
+    //                     parent.children.push(child);
+    //                 }
+    //                 parent = child;
+    //             }
+    //
+    //             return parent;
+    //         }
+    //
+    //         // group에 layer 아이템 저장
+    //         var _set = function (key, item) {
+    //             key.forEach(function (el) {
+    //                 var group = el.split('-');
+    //                 var parent = getParent(_root, group, item.data, item.id, item.icon);
+    //                 var child = new Node(item.name, item.data, item.id, item.icon);
+    //                 parent.children.push(child);
+    //                 return child;
+    //             });
+    //         };
+    //
+    //         // item인 node들만 반환 (=layer)
+    //         var _get = function (key) {
+    //             var group = key.split('-');
+    //             var parent = getParent(_root, group);
+    //             return parent.children.filter(function (node) {
+    //                 return node.type == 'item'
+    //             });
+    //         };
+    //
+    //         var _clear = function () {
+    //             _root.children = [];
+    //         };
+    //
+    //         var _setState = function (key, state) {
+    //             var group = key.split('-');
+    //             var node = getParent(_root, group, false);
+    //             if (!node) return false;
+    //
+    //             node['state'] = state;
+    //             return true;
+    //         };
+    //
+    //         var _toTreeJson = function () {
+    //             return _root.children;
+    //         };
+    //
+    //         return {
+    //             'root': _root,
+    //             'set': _set,
+    //             'get': _get,
+    //             'setState': _setState,
+    //             'toTreeJson': _toTreeJson,
+    //             'clear': _clear,
+    //         };
+    //
+    //     })(treeCtrl);
+    // }
+
+    function getIntTreeList() {
+        $('.treeLoading').show();
+        var url = 'getIntTreeList.do';
+        requestService(url, '', getIntTreeListCallback);
+    }
+    function nameSort(a, b) {
+        const aA = a.addr1;
+        const bA = b.addr1;
+        const x = a.name.toLowerCase();
+		const y = b.name.toLowerCase();
+        return aA > bA ? 1 : aA === bA ? (x > y ? 1 : x === y ? 0 : -1) : -1;
+        //return x < y ? -1 : x > y ? 1 : 0;
+    }
+
+
+    function getIntTreeListCallback(data) {
+        // treeCtrl.clear();
+
+        // var nodeId;
+        // var name;
+        // var lat;
+        // var lng;
+        // var addr1;
+        // var addr2;
+        // var addr3;
+        // var ipAddr;
+        //
+        // var nameCntOn;
+        // // data.sort(nameSort);
+        //
+        // //TODO: 20230221
+        // // setAddrMap(data);
+        setAddrMap(data);
+
+        const groupMap = new Map();
+        window.parent._signalInfoArr.length = 0;
+        if (data && data.length) {
+            data.sort(function (a, b) {
+                return a.addr1 > b.addr1 ? 1 : a.addr1 === b.addr1 ? a.name > b.name ? 1 : a.name === b.name ? 0 : -1 : -1;
+            });
+
+            for (let obj of data) {
+                const {nodeid, name, regionid, lat, lng, addr1, addr2, addr3, ipaddr} = obj;
+                let regionId = regionid;
+
+                if (isNull(regionId)) {
+                    regionId = '9999';
+                }
+                const signal = new SignalInfo(nodeid, name, lat, lng, addr1, addr2, addr3);
+                window.parent._signalInfoArr.push(signal);
+                const intStatus = getStatus(signal.status);
+
+                if (isNull(groupMap.get(regionId))) {
+                    let text = `&nbsp;<strong class="centerNm_\${regionId}">\${addr1}</strong>
+                                &nbsp;( 전체 : <span class="intTotal" id="intTotal_\${regionId}" style="color:#4A90E2"></span> / 이상 :
+                                <span class="intErrCnt" id="intErrCnt_\${regionId}" style="color:#D9534F"></span> )`;
+                    const nodeObj = {
+                        root : null,
+                        id : regionId,
+                        icon : null,
+                        text :text,
+                        state : false,
+                        isFolder : true,
+                        children: [],
+                        obj : obj,
+                        name : obj.addr1,
+                    }
+                    const groupNode = new Node(nodeObj);
+                    groupMap.set(regionId, groupNode);
+                }
+                let text = `<div style="width: 100%; display:flex; align-items:center; gap:5px;">
+                                <div id="int-status-\${nodeid}" class="comm\${intStatus}"></div>
+                                <div title="\${name} (\${nodeid})" style="width:100%;  white-space: nowrap; overflow: hidden;  text-overflow: ellipsis;" >\${name} (\${nodeid})</div>
+                            </div>`;
+                const nodeObj = {
+                    root : regionId,
+                    id : nodeid,
+                    icon : "",
+                    text :text,
+                    state : false,
+                    isFolder : false,
+                    children: [],
+                    obj: obj,
+                    name : obj.name,
+                }
+                const childNode = new Node(nodeObj);
+                groupMap.get(regionId).addChild(childNode);
+            }
+
+            const nodeArr = Array.from(groupMap.values());
+            _TreeList = new TreeList("intTree", nodeArr, true);
+            groupMap.forEach((obj, key)=>{
+                $(`#intTotal_${key}`).text(obj.children.length);
+            });
+        }
+
+        // for (let i = 0; i < data.length; i++) {
+        //     if (data[i].addr1 == null || data[i].addr1 == '-') {
+        //         continue;
+        //     }
+        //
+        //     nodeId = data[i].nodeid;
+        //     name = data[i].name;
+        //     lat = data[i].lat;
+        //     lng = data[i].lng;
+        //     addr1 = data[i].addr1;
+        //     addr2 = data[i].addr2;
+        //     addr3 = data[i].addr3;
+        //     ipAddr = data[i].ipaddr;
+        //
+        //     var signal = new SignalInfo(nodeId, name, lat, lng, addr1, addr2, addr3);
+        //     //부모 변수에 저장
+        //     //iframe이라서 직접 접근이 안됨
+        //     window.parent._signalInfoArr.push(signal);
+        //
+        //     var engAddr = getEngAddr(addr1);
+        //
+        //     nameCntOn = `&nbsp;<strong class="centerNm_\${engAddr}">\${addr1}</strong>
+        //                  &nbsp;( 전체 : <span class="intTotal" id="intTotal_\${engAddr}" style="color:#3396ff"></span> / 이상 :
+		// 			     <span class="intErrCnt" id="intErrCnt_\${engAddr}" style="color:#ef6060"></span> )`;
+        //
+        //     //guTitleOff = nameCntOn + '-' + addr2 + '&nbsp;';
+        //
+        //     var intNm = nodeId + '_' + name;
+        //     var intStatus = getStatus(signal.status);
+        //
+        //     function getStatus(stts){
+        //         if(stts == 1){
+        //             return "On"; //정상
+        //         }else if( stts == 2){
+        //             return "Off"; //교차로시각이상
+        //         }else{
+        //             return "Null"; // 통신시간 3초이상
+        //         }
+        //
+        //     }
+        //
+        //     let searchX = null;
+        //     let searchY = null;
+        //     let searchText = null;
+        //     if (_searchText) {
+        //         if (_searchText.indexOf(',') > -1) {
+        //             const coordArr = _searchText.split(',');
+        //             if (coordArr.length === 2) {
+        //                 searchX = coordArr[0].trim();
+        //                 searchY = coordArr[1].trim();
+        //                 if (lat.toString() !== searchX.toString() || lng.toString() !== searchY.toString()) {
+        //                     continue;
+        //                 }
+        //             }
+        //         }
+        //         else {
+        //             searchText = _searchText;
+        //             if (nodeId.indexOf(searchText) === -1 &&
+        //                 name.indexOf(searchText) === -1
+        //             ) {
+        //                 continue;
+        //             }
+        //         }
+        //     }
+        //     // treeCtrl.set([nameCntOn], {
+        //     //     name: name+" ("+nodeId+")",
+        //     //     id: nodeId,
+        //     //     icon: "images/comm" + intStatus + ".png",
+        //     //     data: {
+        //     //         'line': 'offline',
+        //     //         'intNo': nodeId, 'intNm': name,
+        //     //         'lat': lat, 'lng': lng,
+        //     //     }
+        //     // });
+        //
+        // }
+
+        // treeJson = treeCtrl.toTreeJson();
+
+        //
+        // $('#intTree').jstree({
+        //     'core': {
+        //         'data': treeJson,
+        //         "themes": {"stripes": true},
+        //     },
+        //     "plugins": ["wholerow"]
+        // });
+        //
+
+        // jsTree 클릭 이벤트 : 레이어 setVisible
+        // $('#intTree').bind('select_node.jstree', function (event, data) {
+        //
+        //     var parentNd = data.node.parent;
+        //     var children = data.node.children;
+        //
+        //     //parent._RegionCd = data.node.data.regionCd;
+        //
+        //     if (parentNd == '#') //시도 선택
+        //     {
+        //     } else if (parentNd != '#' && children.length > 0) //구 선택
+        //     {
+        //     } else {
+        //         var intNm = data.node.data.intNm;
+        //         var lng = data.node.data.lng;
+        //         var lat = data.node.data.lat;
+        //
+        //         if(parent._Level > 3) parent.map.setLevel(1);
+        //         parent.map.panTo(new parent.kakao.maps.LatLng(lat, lng));
+        //         setTimeout(
+        //             function() {
+        //                 parent.getSignalInfo();
+        //                 parent.customOverlayFnc(intNm,lng,lat);
+        //             }, 100 * 2);
+        //     }
+        //
+        // });
+
+        $('.treeLoading').hide();
+    }
+
+    function getStatus(stts){
+        if ( stts == 1 ) {
+            return "On"; //정상
+        }
+        else if( stts == 2 ){
+            return "Off"; //교차로시각이상
+        }
+        else{
+            return "Null"; // 통신시간 3초이상
+        }
+    }
+
+    function isNull(str) {
+        return (str === "" || str === null || str === undefined);
+    }
+
+    //유선 센터상테 체크..
+    function onlineCenterStatusCheck() {
+
+        if (onlineCenterStatusCheckId != null) clearTimeout(onlineCenterStatusCheckId);
+        onlineCenterStatusCheckId = setTimeout('onlineCenterStatusCheck()', 5 * 1000);
+
+        var url = 'getOnlineCenterStatus.do';
+        requestService(url, '', onlineCenterStatusCheckCallback);
+    }
+
+    function onlineCenterStatusCheckCallback(data) {
+        onlineCenterStatusData = data;
+        drawCenterStatus(0, data);
+    }
+
+    //무선 센터상테 체크..
+    function offlineCenterStatusCheck() {
+
+        if (offlineCenterStatusCheckId != null) clearTimeout(offlineCenterStatusCheckId);
+        offlineCenterStatusCheckId = setTimeout('offlineCenterStatusCheck()', 5 * 1000);
+
+        var url = 'getOfflineCenterStatus.do';
+        requestService(url, '', offlineCenterStatusCheckCallback);
+    }
+
+    function offlineCenterStatusCheckCallback(data) {
+        offlineCenterStatusData = data;
+        drawCenterStatus(1, data);
+    }
+
+    function drawCenterStatus(network, data) {
+        let regionCd;
+        let totCnt;
+        let errCnt;
+        let commState;
+        let commImg = '';
+
+        console.log(data);
+
+        for (let ii = 0; ii < data.length; ii++) {
+            regionCd = data[ii].REGION_CD;
+            totCnt = data[ii].TOT;
+            errCnt = data[ii].ERR_CNT;
+            commState = data[ii].COMM_STATE;
+
+            if (commState == 1) {
+                commImg = 'images/commOff.png';
+                $('.intErrCnt_' + network + '_' + regionCd).text(totCnt);
+            } else {
+                commImg = 'images/commOn.png';
+                $('.intErrCnt_' + network + '_' + regionCd).text(errCnt);
+            }
+            if (network == 0) $('.regionComm_' + network + '_' + regionCd).attr('src', commImg);
+
+            $('.intTotal_' + network + '_' + regionCd).text(totCnt);
+        }
+    }
+
+    //유선 제어기 상태 체크..
+    function onlineIntStatusCheck() {
+        if (_RegionCdArr.length > 0) {
+            if (onlineIntStatusCheckId != null) clearTimeout(onlineIntStatusCheckId);
+            onlineIntStatusCheckId = setTimeout('onlineIntStatusCheck()', 5 * 1000);
+
+            var url = 'getOnlineIntStatus.do';
+            var param = 'regionCd=' + _RegionCdArr;
+            requestService(url, param, onlineIntStatusCheckCallback);
+        }
+    }
+
+    function onlineIntStatusCheckCallback(data) {
+        onlineIntStatusData = data;
+        drawIntStatus(0, data);
+    }
+
+    //무선 제어기 상태 체크..
+    function offlineIntStatusCheck() {
+        if (_RegionCdArr.length > 0) {
+            if (offlineIntStatusCheckId != null) clearTimeout(offlineIntStatusCheckId);
+            offlineIntStatusCheckId = setTimeout('offlineIntStatusCheck()', 5 * 1000);
+
+            var url = 'getOfflineIntStatus.do';
+            var param = 'regionCd=' + _RegionCdArr;
+            requestService(url, param, offlineIntStatusCheckCallback);
+        }
+    }
+
+    function offlineIntStatusCheckCallback(data) {
+        offlineIntStatusData = data;
+        drawIntStatus(1, data);
+    }
+
+    function drawIntStatus(network, data) {
+        var commState;
+        if (network == 0) {
+            for (var i = 0; i < onlineCenterStatusData.length; i++) {
+                if (onlineCenterStatusData[i].REGION_CD == data[0].REGION_CD) {
+                    commState = onlineCenterStatusData[i].COMM_STATE;
+                    break;
+                }
+            }
+        } else {
+            commState = 0;
+        }
+
+        var regionCd;
+        var siguguCd;
+        var intNoArr;
+        var commOnOffFlagArr;
+        var commOnOffImg;
+
+        for (var ii = 0; ii < data.length; ii++) {
+            regionCd = data[ii].REGION_CD;
+            sigunguCd = data[ii].SIGUNGU_CD;
+            intNoArr = data[ii].INT_NO.split('|');
+            commOnOffFlagArr = data[ii].COMM_ON_OFF_FLAG.split('|');
+
+            $('.guIntTotal_' + network + '_' + sigunguCd).text(intNoArr.length);
+
+            //sigunguCd = 41190 부천시 , 41430 의왕시, 41410 군포시
+
+            //commState = 1;
+
+            //시군구 코드가 없으면 상태정보이미지가 없데이트가 안된다...
+            if (commState == 0) {
+                var guIntErrCnt = 0;
+                for (var jj = 0; jj < intNoArr.length; jj++) {
+                    if (commOnOffFlagArr[jj] == 1) {
+                        guIntErrCnt++;
+                        commOnOffImg = 'background-image:url("images/intStatusOff.png");background-position:center center';
+                    } else {
+                        commOnOffImg = 'background-image:url("images/intStatusOn.png");background-position:center center';
+                    }
+                    $('#' + regionCd + '_' + network + '_' + intNoArr[jj] + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                }
+                $('.guIntErrCnt_' + network + '_' + sigunguCd).text(guIntErrCnt);
+            } else {
+                for (var j = 0; j < intNoArr.length; j++) {
+                    commOnOffImg = 'background-image:url("images/intStatusOff.png");background-position:center center';
+                    $('#' + regionCd + '_' + network + '_' + intNoArr[j] + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                }
+                $('.guIntErrCnt_' + network + '_' + sigunguCd).text(intNoArr.length);
+            }
+
+        }
+    }
+
+
+    function goIntMenu() {
+        $('.tabs > li').eq(0).addClass('on');
+        $('.tabs > li').eq(1).removeClass('on');
+        // $('.treeLoading').hide();
+        $('#historyList').hide();
+        $('#intTree').show();
+        $('.searchBox').css('display', 'flex');
+        $('.historyBox').css('display', 'none');
+        $('.leftMenu .evp-cur').hide();
+        $('.evp_legend', parent.document).hide();
+        unsetEvpCurr();
+
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        bottomFrame.find('#cvibBottomBodyTable').show();
+        bottomFrame.find('#evpBottomBodyTable').hide();
+
+        if (_historyMap && _historyMap.size) {
+            if(interval) clearInterval(interval);
+            _historyMap.forEach((obj)=> {
+                if (obj.get('draw')) {
+                    obj.get('draw').clear();
+                    obj.set('draw', null);
+                }
+            });
+            $('.history-table tr.on').removeClass('on');
+            bottomFrame.find('#evpBottomInfo').empty();
+            imageOnOff('play', false);
+            imageOnOff('stop', false);
+            imageOnOff('pause', false);
+        }
+        //.show();
+    }
+
+    function unsetEvpCurr() {
+        if (window.parent._EmergencyMap && window.parent._EmergencyMap.size) {
+            window.parent._EmergencyMap.forEach((obj)=> obj.clear());
+            window.parent._EmergencyMap.clear();
+            $('.evp-table tr.on').removeClass('on');
+            window.parent._isClick = null;
+        }
+    }
+
+    function goEvpMenu() {
+        $('.tabs > li').eq(1).addClass('on');
+        $('.tabs > li').eq(0).removeClass('on');
+        $('.evp_legend', parent.document).show();
+        // $('.treeLoading').show();
+        $('#historyList').show();
+        $('#intTree').hide();
+        $('.searchBox').css('display', 'none');
+        $('.historyBox').css('display', 'flex');
+        $('.leftMenu .evp-cur').show();
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        bottomFrame.find('#cvibBottomBodyTable').hide();
+        bottomFrame.find('#evpBottomBodyTable').show();
+    }
+
+    function searchHistoryData() {
+        const $emptyBox = $('.empty-history');
+        const $historyLoading = $('.history-loading');
+        const $region       = $('#region');
+        const regionVal    = $region.val();
+        const startVal     = $start.val();
+        const endVal       = $end.val();
+        const startTimeVal = $startTime.val();
+        const endTimeVal   = $endTime.val();
+        if (!startVal) {
+            alert('검색시작 년월일을 입력해주세요');
+            return $start.focus();
+        }
+        if (!startTimeVal) {
+            alert('검색시작 시간을 입력해주세요');
+            return $startTime.focus();
+        }
+        if (!endVal) {
+            alert('검색종료 년월일을 입력해주세요');
+            return $end.focus();
+        }
+
+        if (!endTimeVal) {
+            alert('검색종료 시간을 입력해주세요');
+            return $endTime.focus();
+        }
+
+        const param = {
+            start : startVal + ' ' + startTimeVal + ':00',
+            end : endVal + ' ' + endTimeVal + ':59',
+            region : regionVal
+        }
+        if (param.start > param.end) {
+            alert('검색시작 일시가 종료 일시 보다 큽니다.');
+            return $end.focus();
+        }
+
+        const $historyTable = $('.history-table');
+        $historyTable.empty();
+        $emptyBox.css('display', 'none');
+        $historyLoading.css('display', 'flex');
+        imageOnOff('play', false);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+
+        if (_historyMap.size) {
+            _historyMap.forEach((obj)=>{
+                if (obj.get('draw')) obj.get('draw').clear();
+            });
+            _historyMap.clear();
+            if (interval) {
+                clearInterval(interval);
+                interval = null;
+                cnt = 0;
+            }
+            const bottomFrame = $('#iframeBottomList', parent.document).contents();
+            bottomFrame.find('#evpBottomInfo').empty();
+        }
+
+        window.parent.requestService('getEvpHistory.do', param, (rec)=>{
+            console.log(rec);
+            let str = ''
+            if (rec && rec.length) {
+                for (let history of rec) {
+                     _historyMap.set(history.service_id, new Map());
+                    const {event_list, phase_list, node_list} = history;
+
+                    const dateMap = new Map();
+                    if (event_list.length && phase_list.length) {
+                        event_list.forEach((eventObj)=>{
+                            dateMap.set(eventObj.clct_dt, new Map());
+                            dateMap.get(eventObj.clct_dt).set('event', eventObj);
+                            dateMap.get(eventObj.clct_dt).set('sig', []);
+                        });
+
+                        phase_list.forEach((obj)=>{
+                            if (!dateMap.get(obj.clct_dt)) {
+                                dateMap.set(obj.clct_dt, new Map());
+                                dateMap.get(obj.clct_dt).set('event', null);
+                                dateMap.get(obj.clct_dt).set('sig', []);
+
+                            }
+                            dateMap.get(obj.clct_dt).get('sig').push(obj);
+                        });
+
+                        if (dateMap.size) {
+                            history.event_list = [];
+                            history.phase_list = [];
+                            const dateArr = Array.from(dateMap.keys()).sort();
+                            let beforeSig;
+                            let beforeEvent;
+
+                            for (let date of dateArr) {
+                                const objMap = dateMap.get(date);
+                                if (objMap.get('event') === null && beforeEvent === undefined) {
+                                    beforeEvent = event_list[0];
+                                }
+                                else if (objMap.get('event')){
+                                    beforeEvent = objMap.get('event');
+                                }
+                                const eventData = {...beforeEvent};
+
+                                eventData.clct_dt = date;
+
+                                if (objMap.get('sig').length === 0 && beforeSig === undefined) {
+                                    beforeSig = dateMap.get(phase_list[0].clct_dt).get('sig');
+                                }
+                                else if (objMap.get('sig').length) {
+                                    beforeSig = objMap.get('sig');
+                                }
+
+                                if (beforeSig.length !== node_list.length) {
+                                    for (let node of node_list) {
+                                        let idx = objMap.get('sig').findIndex(obj => obj.seq_no === node.seq_no)
+                                        if (idx === -1) {
+                                            if (new Date(date).getTime() - new Date(dateArr[0]).getTime() != 0) {
+                                                let startTime = new Date(dateArr[0]).getTime();
+                                                let currTime = new Date(date).getTime();
+                                                let beforeObj = null
+
+                                                for (let ii = currTime; ii >= startTime; ii -= 1000) {
+                                                    const key = getDateTimeToStr(new Date(ii));
+                                                    if (dateMap.get(key)) {
+                                                        const beforeIdx = dateMap.get(key).get('sig').findIndex(obj => obj.seq_no === node.seq_no);
+
+                                                        if (beforeIdx > -1) {
+                                                            beforeObj = dateMap.get(key).get('sig')[beforeIdx];
+                                                            break;
+                                                        }
+                                                    }
+                                                }
+                                                if (beforeObj) {
+                                                    beforeSig.push({
+                                                        seq_no : beforeObj.seq_no,
+                                                        clct_dt : date,
+                                                        service_id : beforeObj.service_id,
+                                                        a_end_angle : beforeObj.a_end_angle,
+                                                        a_end_lat:beforeObj.a_end_lat,
+                                                        a_end_lng:beforeObj.a_end_lng,
+                                                        a_flow_no:beforeObj.a_flow_no,
+                                                        a_head_angle:beforeObj.a_head_angle,
+                                                        a_head_lat:beforeObj.a_head_lat,
+                                                        a_head_lng: beforeObj.a_head_lng,
+                                                        a_mid_lat:beforeObj.a_mid_lat,
+                                                        a_mid_lng:beforeObj.a_mid_lng,
+                                                        a_ring_phase:beforeObj.a_ring_phase,
+                                                        b_end_angle:beforeObj.b_end_angle,
+                                                        b_end_lat:beforeObj.b_end_lat,
+                                                        b_end_lng:beforeObj.b_end_lng,
+                                                        b_flow_no:beforeObj.b_flow_no,
+                                                        b_head_angle:beforeObj.b_head_angle,
+                                                        b_head_lat:beforeObj.b_head_lat,
+                                                        b_head_lng:beforeObj.b_head_lng,
+                                                        b_mid_lat:beforeObj.b_mid_lat,
+                                                        b_mid_lng:beforeObj.b_mid_lng,
+                                                        b_ring_phase:beforeObj.b_ring_phase,
+                                                        hold_phase:beforeObj.hold_phase,
+                                                        lat : beforeObj.lat,
+                                                        lng : beforeObj.lng,
+                                                        node_id : beforeObj.node_id,
+                                                        node_nm : beforeObj.node_nm,
+                                                        plan_class:beforeObj.plan_class,
+                                                        rem_dist:beforeObj.rem_dist,
+                                                        state : beforeObj.state,
+                                                    })
+                                                }
+
+                                            }
+
+                                        }
+                                    }
+                                }
+                                const sigData = beforeSig;
+                                history.event_list.push(eventData);
+                                history.phase_list.push(sigData);
+                            }
+                        }
+                    }
+
+                     _historyMap.get(history.service_id).set('obj', history);
+                     _historyMap.get(history.service_id).set('draw', null);
+                    str += `<tr class="hs_\${history.service_id}" onclick="drawHistory('\${history.service_id}')"><td>\${history.clct_dt}</td><td>\${history.service_id}</td><td>\${history.service_nm}</td></tr>`;
+                }
+            }
+
+            $historyLoading.css('display', 'none');
+
+            if (str === '') {
+                $emptyBox.css('display', 'flex');
+            }
+
+            $historyTable.html(str);
+        }, true, ()=>{
+            $emptyBox.css('display', 'flex');
+            $historyLoading.css('display', 'none');
+        });
+    }
+
+    function drawHistory(serviceId) {
+        unsetEvpCurr();
+        if (_historyMap.size) {
+            _historyMap.forEach((obj, key)=>{
+                const beforeDraw = obj.get('draw');
+                if (beforeDraw && serviceId !== key) {
+                    beforeDraw.clear();
+                    obj.set('draw', null);
+                }
+            });
+
+            if (_historyMap.get(serviceId).get('draw')) {
+                return;
+            }
+            else {
+                if (interval) {
+                    clearInterval(interval);
+                    interval = null;
+                    cnt = 0;
+                }
+            }
+        }
+
+        const obj = _historyMap.get(serviceId).get('obj');
+        $('#historyList tr.on').removeClass('on');
+        $('.hs_' + serviceId).addClass('on');
+        const drawObj = {...obj};
+        const { event_list, phase_list } = obj;
+
+        if (event_list && event_list.length) drawObj.event_list = [drawObj.event_list[0]];
+        if (phase_list && phase_list.length) drawObj.phase_list = phase_list[0];
+        const bottomFrame = $('#iframeBottomList', parent.document).contents();
+        const bottomInfo  = bottomFrame.find('#evpBottomInfo');
+
+        let str = '';
+
+        event_list.forEach((event, idx)=>{
+            str += getBottomListEl(obj, event, idx);
+        });
+
+        bottomInfo.html(str);
+        const draw = window.parent.drawHistory(drawObj);
+        _historyMap.get(serviceId).set('draw', draw);
+        bottomFrame.find('.bottomBody').scrollTop(0);
+        imageOnOff('play', true);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+    }
+
+    function imageOnOff(name, isOn) {
+        const el = $('#' + name);
+        const eventName = isOn ? 'addClass' : 'removeClass';
+        el[eventName]('on');
+
+        if (el.attr('src') === getImageName(name, !isOn)) {
+            el.attr('src', getImageName(name, isOn));
+        }
+    }
+
+    /**
+     * 긴급차량우선신호 bottom list 생성
+     * @param obj 서비스 정보
+     * @param event 발생 이벤트 정보
+     * @param idx 인덱스 번호
+     * @returns {string} html 리스트 목록 반환
+     */
+    function getBottomListEl(obj, event, idx) {
+        const regionNm = $('#region option[value="'+obj.service_id.substr(0,3)+'"]').text(); //왼쪽 3글자는 센터 번호를 의미
+        let className = '';
+
+        if (idx === 0) { //처음 그릴때 첫줄을 기반으로 그리므로 선택 표시
+            className = 'on';
+        }
+        let curSpd = '-';
+        if (event.cur_spd) { // 현재 스피드가 있을경우 천단위 콤마
+            curSpd = event.cur_spd.toLocaleString('ko-KR');
+        }
+
+        let remDist = '-';
+        if (event.rem_dist) {// 남은 거리가 있을경우 천단위 콤마
+            remDist = event.rem_dist.toLocaleString('ko-KR');
+        }
+
+
+        let clctDt = event.clct_dt.replace(/\:|\-| /gi, '') //시간 기호(':', '-', ' ') 제거 후 값만 추출 하여 table 로우 ID 부여
+        let str = `<tr onclick="moveLocation('\${obj.service_id}', this)" class="\${className}" id="\${obj.service_id}_\${clctDt}">`
+                    +'<td>'+event.clct_dt+'</td>'
+                    +'<td>'+regionNm+'</td>'
+                    +'<td>'+obj.service_id+'</td>'
+                    +'<td>'+obj.service_nm+'</td>'
+                    +'<td>'+obj.ev_no+'</td>'
+                    +'<td>'+event.cur_lng+'</td>'
+                    +'<td>'+event.cur_lat+'</td>'
+                    +'<td>'+curSpd+'</td>'
+                    +'<td>'+remDist+'</td>'
+                +'</tr>';
+        return str;
+    }
+
+    /**
+     * 시뮬레이션 재생 / 멈춤 버튼 활성화 비활성화 이미지명 가져오기
+     * @param name 요소 ID name (ID 와 이미지 이름이 동일함)
+     * @param isOn 활성화/비활성화(true/false)
+     * @returns {string} 이미지명 반환
+     */
+    function getImageName(name, isOn) {
+        let imgName = '/images/' + name;
+        const onName = '_on';
+        const extName = '.png';
+        if (isOn) {
+            imgName += onName;
+        }
+        return imgName + extName;
+    }
+
+
+    /**
+     * 시뭏레이션 재생
+     */
+    function playSimulation() {
+        if (!$('#play').hasClass('on')) return; //비활성화중일 때만 실행
+        if (interval) {
+            clearInterval(interval);
+            interval = null;
+        }
+        const serviceId = $('.history-table tr.on').attr('class').replace(/hs_| on/gi, '');// 선택 로우에서 service id 추출
+        let evp = _historyMap.get(serviceId); //Map 에 저장된 해당 service_id 데이터 정보 가져오기
+        let obj = evp.get('obj'); //object 정보
+        let draw = evp.get('draw'); // marker handler 정보
+        if (!draw) {// handler 가 없다면 생성
+            drawHistory(serviceId);
+            draw = evp.get('draw');
+        }
+
+        const { event_list, phase_list } = obj; //이벤트 리스트와 신호 리스트
+
+        //버튼 활성화/비활성화 적용
+        imageOnOff('stop', true);
+        imageOnOff('pause', true);
+        imageOnOff('play', false);
+
+        interval = setInterval(()=>{
+            cnt++;
+            if (event_list.length === cnt) {
+                return stopSimulation();
+            }
+            const bottomFrame = $('#iframeBottomList', parent.document).contents();
+            bottomFrame.find('#evpBottomInfo tr.on').removeClass('on');
+            let clctDt = event_list[cnt].clct_dt.replace(/\:|\-| /gi, '')
+            const bottomInfo = bottomFrame.find('#evpBottomInfo #'+obj.service_id +'_'+clctDt);
+            bottomInfo.addClass('on');
+            bottomFrame.find('.bottomBody').scrollTop(32 * cnt);
+
+            const position = window.parent.getKakaoPosition(event_list[cnt].cur_lat, event_list[cnt].cur_lng);
+            draw.car.moveMarker(position);
+            setSigState(phase_list[cnt], draw);
+
+        }, 500);
+    }
+
+    function setSigState(phase, draw) {
+        phase.forEach((sigObj)=>{
+            if (draw.sig.get(sigObj.service_id + '_' + sigObj.seq_no) === undefined) {
+                draw.sig.set(sigObj.service_id + '_' + sigObj.seq_no, window.parent.createSig(sigObj));
+            }
+            const evpSig = draw.sig.get(sigObj.service_id + '_' + sigObj.seq_no);
+            evpSig.createRing(sigObj);
+        });
+
+        draw.sig.forEach((obj, key)=>{
+            const idx = phase.findIndex((sig)=>{
+                return sig.service_id + '_' + sig.seq_no === key;
+            })
+
+            if (idx === -1) {
+                obj.deleteRing();
+                obj.setState(1);
+            }
+        })
+    }
+
+    function pauseSimulation() {
+        if (!$('#pause').hasClass('on')) {
+            return;
+        }
+        clearInterval(interval);
+        imageOnOff('pause', false);
+        imageOnOff('play', true);
+    }
+
+    function stopSimulation() {
+        if (!$('#stop').hasClass('on')) {
+            return;
+        }
+        clearInterval(interval);
+        imageOnOff('stop', false);
+        imageOnOff('pause', false);
+        imageOnOff('play', true);
+        cnt = 0;
+        const serviceId = $('.history-table tr.on').attr('class').replace(/hs_| on/gi, '');
+        clearSimulator();
+        drawHistory(serviceId);
+    }
+
+    function clearSimulator() {
+        _historyMap.forEach((obj)=>{
+            const beforeDraw = obj.get('draw');
+            if (beforeDraw) {
+                beforeDraw.clear();
+                obj.set('draw', null);
+            }
+        })
+    }
+</script>
+</body>
+</html>
+