Index: arms/css/override.css =================================================================== diff -u -r0e57ce258d45d3292d533715d09848e82a838c21 -rbdfa1e19b5c80752c583ed09eed066f990162c24 --- arms/css/override.css (.../override.css) (revision 0e57ce258d45d3292d533715d09848e82a838c21) +++ arms/css/override.css (.../override.css) (revision bdfa1e19b5c80752c583ed09eed066f990162c24) @@ -1,338 +1,2175 @@ -@font-face { - font-family: 'Nanum Gothic'; - font-style: normal; - font-weight: 400; - src: url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.eot'); /* IE9 Compat Modes */ - src: local('NanumGothic'), - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.woff2') format('woff2'), /* Super Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.woff') format('woff'), /* Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.ttf') format('truetype'), /* Safari, Android, iOS */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-regular.svg#NanumGothic') format('svg'); /* Legacy iOS */ +@import "common.css"; +@import "community.css"; + +.notifications .alert a { + color: #f8f8f8; + font-weight: 700; + margin-left: 5px; } -/* nanum-gothic-800 - latin_korean */ -@font-face { - font-family: 'Nanum Gothic'; - font-style: normal; - font-weight: 800; - src: url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.eot'); /* IE9 Compat Modes */ - src: local('NanumGothic ExtraBold'), local('NanumGothic-ExtraBold'), - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.woff2') format('woff2'), /* Super Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.woff') format('woff'), /* Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.ttf') format('truetype'), /* Safari, Android, iOS */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-800.svg#NanumGothic') format('svg'); /* Legacy iOS */ + +/* 스크롤 테마 */ +::-webkit-scrollbar { + width: 8px; /* 스크롤바의 너비 */ + height: 8px; } -/* nanum-gothic-700 - latin_korean */ -@font-face { - font-family: 'Nanum Gothic'; - font-style: normal; - font-weight: 700; - src: url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.eot'); /* IE9 Compat Modes */ - src: local('NanumGothic Bold'), local('NanumGothic-Bold'), - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.woff2') format('woff2'), /* Super Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.woff') format('woff'), /* Modern Browsers */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.ttf') format('truetype'), /* Safari, Android, iOS */ - url('../../reference/common-design/font/NanumGothic/nanum-gothic-v11-latin_korean-700.svg#NanumGothic') format('svg'); /* Legacy iOS */ +::-webkit-scrollbar-thumb { + height: 30%; /* 스크롤바의 길이 */ + background: #313131; /* 스크롤바의 색상 */ + + border-radius: 10px; } -html, -body { - font-family: 'Nanum Gothic' !important; +::-webkit-scrollbar-track { + background-color: #4b4b4b; /*스크롤바 뒷 배경 색상*/ } -body.org { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjEwJSIgY3k9IjAiIHI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9InJnYmEoNzQsIDEyMSwgMTQ3LCAwLjg5KSIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSJyZ2JhKDIyNiwgMjI2LCAyMjYsIDAuODcpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJyZ2JhKDE4NSwgMTM1LCAxMzEsIDAuODcpIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4="), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background-color: rgba(74, 121, 147, 0.89); - background: radial-gradient(farthest-side ellipse at 10% 0, rgba(74, 121, 147, 0.89), rgba(204, 204, 204, 0.85) 60%, rgba(185, 135, 131, 0.87) 120%), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background: -webkit-radial-gradient(10% 0, farthest-side ellipse, rgba(74, 121, 147, 0.89), rgba(204, 204, 204, 0.85) 60%, rgba(185, 135, 131, 0.87) 120%), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background: -moz-radial-gradient(10% 0, farthest-side ellipse, rgba(74, 121, 147, 0.89), rgba(204, 204, 204, 0.85) 60%, rgba(185, 135, 131, 0.87) 120%), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); +.slimScrollBar { + background: #784a3d !important; } -body { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjEwJSIgY3k9IjAiIHI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9InJnYmEoMTAyLCAxMDUsIDEwNCwgMC45MCkiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0icmdiYSg5MywgMTEyLCAxMTksIDAuODkpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJyZ2JhKDE2MCwgMTc0LCAxNTAsIDAuOTApIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4="), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background-color: rgba(102, 105, 104, 0.9); - background: radial-gradient(farthest-side ellipse at 10% 0, rgba(40, 40, 40, 0.9), rgba(90, 100, 130, 0.89), rgba(60, 60, 60, 0.9)), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background: -webkit-radial-gradient(10% 0, farthest-side ellipse, rgba(40, 40, 40, 0.9), rgba(90, 100, 130, 0.89), rgba(60, 60, 60, 0.9)), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - background: -moz-radial-gradient(10% 0, farthest-side ellipse, rgba(40, 40, 40, 0.9), rgba(90, 100, 130, 0.89), rgba(60, 60, 60, 0.9)), url("../../reference/light-blue/img/bgnoise_lg_dark.png"); - padding-bottom: 10px; + +/* btn */ +.btn { + transition: background-color 0.2s; } -/* Font */ -.jstree-default-context{ - font-family: 'Nanum Gothic'; - font-size: 12px; +.btn-transparent.active { + color: white; + background: rgba(51, 51, 51, 0.55); + color: #ffffff; + background-color: #e5603b; + border-color: rgba(0, 0, 0, 0.15); } -.font10{ - font-family: 'Nanum Gothic'; - font-size: 10px; +.btn:focus, +.btn.focus, +.btn:active:focus, +.btn:active.focus, +.btn.active:focus, +.btn.active.focus { + outline: 0px auto -webkit-focus-ring-color; + outline-offset: -2px; } -.font11{ - font-family: 'Nanum Gothic'; - font-size: 11px; + +/* 멀티 셀렉트 할때 번지는 값 픽스 */ +.btn-block + .btn-block { + margin-top: 0px !important; } -.font12{ - font-family: 'Nanum Gothic'; - font-size: 12px; + +/* 멀티 셀렉트 */ +.search-input { + width: 100%; + background: rgba(51, 51, 51, 0.4) !important; + border: 1px solid rgba(51, 51, 51, 0.425) !important; } -.font13{ - font-family: 'Nanum Gothic'; - font-size: 13px; +.select2-search__field::-webkit-input-placeholder, +.select2-search__field::placeholder { + color: #999 !important; + padding-left: 6px; } -.font14{ - font-family: 'Nanum Gothic'; - font-size: 14px; + +/* 데이터그리드 왼쪽 상단 로우 개수 선택하는 옵션 배경값 변경 */ +select option { + background: #3b3d40 !important; } -.font15{ - font-family: 'Nanum Gothic'; - font-size: 15px; + +/*! Light */ +#light { + background-color: #000; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.8); + overflow: hidden; + padding: 0 0 4px; + width: 40px; + margin-left: 30px; + transform: rotate(270deg); } -.font16{ - font-family: 'Nanum Gothic'; - font-size: 16px; + +#light span { + border-radius: 50px; + display: block; + height: 30px; + margin: 4px auto 0; + opacity: 0.5; + width: 30px; } -.font17{ - font-family: 'Nanum Gothic'; - font-size: 17px; + +/*! Light colours */ +#red { + background-color: red; } -.font18{ - font-family: 'Nanum Gothic'; - font-size: 18px; + +#orange { + background-color: orange; } -.content { - position: relative; - padding: 30px 2.5641%; - -webkit-transition: margin-top 0.35s ease, opacity 0.2s; - -o-transition: margin-top 0.35s ease, opacity 0.2s; - transition: margin-top 0.35s ease, opacity 0.2s; - max-width: none; - width: auto; - opacity: 1; +#green { + background-color: green; } -.widgetheader { - border-radius: 3px; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - padding: 5px 5px 1px 5px; - color: #f8f8f8; - margin-bottom: 5px; - position: relative; +/*! Active states */ +#light span.active { + opacity: 1; } -#topicon{ - width:36px;height:35px; - position:fixed;right:1%;bottom:15px; - z-index:200; - transition:0.3s; - cursor:pointer; - opacity:0; - border-radius:50%; - overflow:hidden; - padding-left:-1px; - animation: doongdoong 1.3s linear infinite; +#light #red.active { + box-shadow: 0 0 10px red; } -@keyframes doongdoong{ - 0%{bottom:15px;} - 50%{bottom:10px;} - 100%{bottom:15px;} + +#light #orange.active { + box-shadow: 0 0 10px orange; } -.content .content-footer { - position: initial; - bottom: 0px; - color: #d2d2d2; +#light #green.active { + box-shadow: 0 0 10px green; } -.breadcrumb { - padding: 0px; - margin: 3px; - list-style: none; - background-color: transparent; - border-radius: 3px; +/* Dashboard 시작 */ +.c3-tooltip { + color: black !important; } -.darkBack { - background: rgba(51, 51, 51, 0.4) !important; - border: 1px solid rgba(51, 51, 51, 0.425) !important; +.c3-chart-arc path { + stroke-width: 0.4px; } +/* Dashboard 종료 */ +/* Analysis 분석 시작 */ +@media (max-width: 1200px) { + #quality-img { + display: none; + } +} + +.card-widget { + border: 0; + position: relative; +} + +.card { + margin-bottom: 1rem; +} + +.card { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-clip: border-box; + border: 0 solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; +} + +.widget-user .widget-user-image > img { + height: auto; + width: 200px; +} + +.widget-user .widget-user-image { + left: 50%; + margin-left: -100px; + position: absolute; + top: 300px; +} + +.widget-user .widget-user-header { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + height: 135px; + padding: 1rem; + text-align: center; +} + +.img-circle { + border-radius: 50%; +} + +img { + vertical-align: middle; + border-style: none; +} + +/* fade in - out box 처리 */ +.fade-in-box { + animation: fadein 1s; + -moz-animation: fadein 1s; /* Firefox */ + -webkit-animation: fadein 1s; /* Safari and Chrome */ + -o-animation: fadein 1s; /* Opera */ +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-moz-keyframes fadein { + /* Firefox */ + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes fadein { + /* Safari and Chrome */ + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-o-keyframes fadein { + /* Opera */ + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.fade-out-box { + animation: fadeout 3s; + -moz-animation: fadeout 3s; /* Firefox */ + -webkit-animation: fadeout 3s; /* Safari and Chrome */ + -o-animation: fadeout 3s; /* Opera */ + animation-fill-mode: forwards; +} + +@keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@-moz-keyframes fadeout { + /* Firefox */ + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@-webkit-keyframes fadeout { + /* Safari and Chrome */ + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@-o-keyframes fadeout { + /* Opera */ + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +/* 유니타이핑 */ +#blinker { + -webkit-animation: blink 1.2s step-end infinite; + -moz-animation: blink 1.2s step-end infinite; + animation: blink 1.2s step-end infinite; +} +@keyframes blink { + from, + to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-webkit-keyframes blink { + from, + to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-moz-keyframes blink { + from, + to { + opacity: 0; + } + 50% { + opacity: 1; + } +} + +.darkBack { + background: rgba(51, 51, 51, 0.425) !important; + border: 1px solid rgba(51, 51, 51, 0.425) !important; +} .searchDarkBack { - background: url(../../reference/lightblue4/docs/img/search-white.png) 5px 5px no-repeat rgba(51, 51, 51, 0.4) !important; - border: 1px solid rgba(51, 51, 51, 0.425) !important; + background: url(../../reference/lightblue4/docs/img/search-white.png) 5px 5px no-repeat rgba(51, 51, 51, 0.4) !important; + border: 1px solid rgba(51, 51, 51, 0.425) !important; } -/*****************/ -/** jNotify CSS **/ -/*****************/ -#jNotify { - position:absolute !important; - -webkit-transition: background-color 0.4s !important; - -o-transition: background-color 0.4s !important; - transition: background-color 0.4s !important; - border-radius: 5px !important; - border-color: transparent !important; - background-color: rgba(52, 52, 52, 0.425) !important; - margin-bottom: 1em !important; - color: #f8f8f8 !important; - font-weight: 500 !important; +/*데이터피커 css 처리*/ +.xdsoft_datetimepicker .xdsoft_label { + display: inline; + position: relative; + z-index: 9999; + margin: 0; + padding: 5px 3px; + font-size: 12px; + line-height: 20px; + font-weight: bold; + background-color: #fff; + float: left; + width: 182px; + text-align: center; + cursor: pointer; +} - background:#d8e6fc url('../info.png') no-repeat 15px center; - padding:10px; - padding-left:50px; - margin:15px; - z-index:99999; - -moz-border-radius : 5px; - border-radius:5px; - -webkit-border-radius:5px; +.xdsoft_datetimepicker .xdsoft_calendar th:first-child { + color: #ef7979 !important; } -#jNotify a {color:#35517c !important;text-decoration:none;} -/******************/ -/** jSuccess CSS **/ -/******************/ -#jSuccess { - position:absolute !important; - -webkit-transition: background-color 0.4s !important; - -o-transition: background-color 0.4s !important; - transition: background-color 0.4s !important; - border-radius: 5px !important; - border-color: transparent !important; - background-color: rgba(52, 52, 52, 0.425) !important; - margin-bottom: 1em !important; - color: #f8f8f8 !important; - font-weight: 500 !important; +.xdsoft_datetimepicker .xdsoft_calendar th:last-child { + color: #a0a0ff !important; +} - background:#E6EFC2 url('../success.png') no-repeat 15px center; - padding:10px; - padding-left:50px; - margin:15px; - z-index:99999; - -moz-border-radius : 5px; - border-radius:5px; - -webkit-border-radius:5px; +.xdsoft_day_of_week0 { + color: #ef7979 !important; } -#jSuccess a {color:#264409 !important;text-decoration:none;} +.xdsoft_day_of_week6 { + color: #a0a0ff !important; +} -/****************/ -/** jError CSS **/ -/****************/ -#jError { - position:absolute !important; - -webkit-transition: background-color 0.4s !important; - -o-transition: background-color 0.4s !important; - transition: background-color 0.4s !important; - border-radius: 5px !important; - border-color: transparent !important; - background-color: rgba(52, 52, 52, 0.425) !important; - margin-bottom: 1em !important; - color: #f8f8f8 !important; - font-weight: 500 !important; +.dd { + position: relative; + display: block; + margin: 0; + padding: 0; + max-width: 600px; + list-style: none; + font-size: 13px; + line-height: 20px; +} - background:#FBE3E4 url('../error.png') no-repeat 15px center; - padding:10px; - padding-left:50px; - margin:15px; - z-index:99999; - -moz-border-radius : 5px; - border-radius:5px; - -webkit-border-radius:5px; +.dd-list { + display: block; + position: relative; + margin: 0; + padding: 0; + list-style: none; } -#jError a {color:#8a1f11 !important;text-decoration:none;} -/** OVERLAY **/ -#jOverlay { - width:100%; - height:100%; - position:fixed; - top:0; - left:0; - z-index:99998 +.dd-list .dd-list { + padding-left: 30px; } -.mailbox-content .form-email-compose .control-group { - margin-bottom: 10px; +.dd-collapsed .dd-list { + display: none; } -.fileupload-buttonbar .btn, .fileupload-buttonbar .toggle { - margin-bottom: 5px; - background: rgba(51,51,51,0.4); - border-color: transparent; - color: #fff; - text-shadow: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - -webkit-transition: background-color 0.2s; - -moz-transition: background-color 0.2s; - -o-transition: background-color 0.2s; - transition: background-color 0.2s; +.dd-item, +.dd-empty, +.dd-placeholder { + display: block; + position: relative; + margin: 0; + padding: 0; + min-height: 20px; + font-size: 13px; + line-height: 20px; } -.fileinput-button { - position: relative; - overflow: hidden; - float: left; - margin-right: 4px; - color: #fff; - text-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; - border-color: #c5c5c5; - border-color: rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25); - padding: 2px 10px; - font-size: 11.05px; - line-height: 1.9; - border-radius: 0; - display: inline-block; - margin-bottom: 0; - font-weight: 300; - text-align: center; - vertical-align: middle; - cursor: pointer; - background-image: none; - border: 1px solid transparent; - white-space: nowrap; - padding: 4px 12px; - font-size: 13px; - line-height: 20px; - border-radius: 1px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; + +.dd-handle { + display: block; + margin: 5px 0; + padding: 5px 10px; + color: #f8f8f8; + text-decoration: none; + background: rgba(51, 51, 51, 0.4); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } -.fileinput-button input { - position: absolute; - top: 0; - right: 0; - margin: 0; - opacity: 0; - filter: alpha(opacity=0); - transform: translate(-300px, 0) scale(4); - font-size: 23px; - direction: ltr; - cursor: pointer; + +.dd-item > button { + display: block; + position: relative; + cursor: pointer; + float: left; + width: 25px; + height: 20px; + margin: 5px 0; + padding: 0; + text-indent: 100%; + white-space: nowrap; + overflow: hidden; + border: 0; + background: transparent; + font-size: 12px; + line-height: 1; + text-align: center; + font-weight: bold; + color: #f8f8f8; } -.fileupload-loading { - position: absolute; - left: 50%; - width: 128px; - height: 128px; - display: none; + +.dd-item > button:before { + content: "+"; + display: block; + position: absolute; + width: 100%; + text-align: center; + text-indent: 0; } -.dropzone { - border: 1px dashed #eee; - margin-top: 10px; - font-size: 13px; - color: #eee; - line-height: 100px; - vertical-align: middle; + +.dd-item > button[data-action="collapse"]:before { + content: "-"; } +.dd-placeholder, +.dd-empty { + margin: 5px 0; + padding: 0; + min-height: 30px; + background: rgba(51, 51, 51, 0.2); + border: 1px dashed rgba(255, 255, 255, 0.6); + box-sizing: border-box; + -moz-box-sizing: border-box; +} + +.dd-empty { + border: 1px dashed rgba(255, 255, 255, 0.6); + min-height: 100px; + background-size: 60px 60px; + background-position: 0 0, 30px 30px; +} + +.dd-dragel { + position: absolute; + pointer-events: none; + z-index: 9999; +} + +.dd-dragel > .dd-item .dd-handle { + margin-top: 0; +} + +.dd-dragel .dd-handle { + -webkit-box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1); + box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1); +} + +.dd-hover > .dd-handle { + background: #2ea8e5 !important; +} + +.nestable-lists { + display: block; + clear: both; + padding: 30px 0; + width: 100%; + border: 0; + border-top: 2px solid #ddd; + border-bottom: 2px solid #ddd; +} + +@media only screen and (min-width: 700px) { + .dd + .dd { + margin-left: 2%; + } +} +.dd-hover > .dd-handle { + background: #2ea8e5 !important; +} + +/* +* aside +*/ +.side-nav .category-wrap { + margin: 0; + border: none; + border-radius: 0; +} +.side-nav li a, +.side-nav .category-label { + color: inherit; + transition: background-color 0.2s; + text-decoration: none; + display: block; + padding: 10px; + border-radius: 6px; +} +.category-wrap li a { + padding: 4px 10px 4px 35px; +} + +.nav-tabs > li.active > a { + color: #a4c6ff !important; +} + +.widget-content { + margin: 10px 0 15px; +} + +.widget-content > div { + margin-top: 10px; +} + +.control-group p { + text-align: left; +} + .control-label { - float: left; - width: 160px; - padding-top: 5px; - text-align: right; + float: left; + width: 160px; + padding-top: 5px; + text-align: right; } + +/* +* 데이터테이블 커스텀 +*/ +table thead tr th { + overflow: hidden; + white-space: nowrap; +} + +table tbody tr td { + border-bottom: 1px solid rgba(68, 68, 68, 0.7); + box-sizing: border-box; +} + +.dataTables_scrollBody::-webkit-scrollbar { + height: 10px; +} + +.dataTables_scrollBody:hover::-webkit-scrollbar-track { + background-color: #414141; +} + +.dataTables_scrollBody:hover::-webkit-scrollbar-thumb { + background-color: #784a3d; +} + +.dataTables_scrollBody::-webkit-scrollbar-track, +.dataTables_scrollBody::-webkit-scrollbar-thumb { + background-color: transparent; +} + +.dataTables_scrollBody::-webkit-scrollbar-thumb { + border-radius: 5px; +} + +.ribbon { + /* adjust the below to control the shape */ + --d: 5px; + --w: 100px; + --c: #333; + /**/ + + position: absolute; + top: 0; + right: 0; + transform: translate(29.29%, -100%) rotate(45deg); /* 29.29% = 100%*(1 - cos(45deg)) */ + color: #fff; + text-align: center; + width: var(--w); + transform-origin: bottom left; + padding: 5px 0 calc(var(--d) + 5px); + background: linear-gradient(rgba(0, 0, 0, 0.6) 0 0) bottom/100% var(--d) no-repeat var(--c); + clip-path: polygon( + 0 100%, + 0 calc(100% - var(--d)), + 50% calc(100% - var(--d) - var(--w) / 2), + 100% calc(100% - var(--d)), + 100% 100%, + calc(100% - var(--d)) calc(100% - var(--d)), + var(--d) calc(100% - var(--d)) + ); +} + +.landing { + padding: 0; +} + +/* +* Component: Timeline +* ------------------- +*/ +.timeline { + position: relative; + margin: 0 0 30px 0; + padding: 0; + list-style: none; +} +.timeline:before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + width: 4px; + background: #ddd; + left: 31px; + margin: 0; + border-radius: 2px; +} +.timeline > li { + position: relative; + margin-right: 10px; + margin-bottom: 15px; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li > .timeline-item { + background: rgba(51, 51, 51, 0.3); + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border-radius: 3px; + margin-top: 0; + color: #f8f8f8; + margin-left: 60px; + padding: 0; + position: relative; +} +.timeline > li > .timeline-item > .time { + color: #999; + float: right; + padding: 10px; + font-size: 12px; +} +.timeline > li > .timeline-item > .timeline-header { + margin: 0; + color: #555; + padding: 10px; + font-size: 13px; + line-height: 1.1; +} +.timeline > li > .timeline-item > .timeline-header > a { + font-weight: 600; +} +.timeline > li > .timeline-item > .timeline-body, +.timeline > li > .timeline-item > .timeline-footer { + padding: 10px; +} +.timeline > li > .fa, +.timeline > li > .glyphicon, +.timeline > li > .ion { + width: 30px; + height: 30px; + font-size: 15px; + line-height: 30px; + position: absolute; + color: #000; + background: #d2d6de; + border-radius: 50%; + text-align: center; + left: 18px; + top: 0; +} +.timeline > .time-label > span { + font-weight: 400; + padding: 5px; + display: inline-block; + background-color: #fff; + border-radius: 4px; +} +.timeline-inverse > li > .timeline-item { + background: rgba(51, 51, 51, 0.3); + border-left: 2px solid rgba(26, 26, 26, 0.425); + -webkit-box-shadow: none; + box-shadow: none; +} +.timeline-inverse > li > .timeline-item > .timeline-header { + border-bottom-color: #ddd; +} + +.timeline-inverse .arrow { + display: block; + position: absolute; + top: 10px; + left: -7px; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid rgba(26, 26, 26, 0.425); +} + +.modalDarkBack { + background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjEwJSIgY3k9IjAiIHI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9InJnYmEoMTAyLCAxMDUsIDEwNCwgMC45MCkiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0icmdiYSg5MywgMTEyLCAxMTksIDAuODkpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJyZ2JhKDE2MCwgMTc0LCAxNTAsIDAuOTApIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4="), + url("../../reference/light-blue/img/bgnoise_lg_dark.png"); + background-color: rgba(102, 105, 104, 0.9); + padding-bottom: 10px; + border: 2px dotted rgb(151 151 151 / 95%) !important; + background: linear-gradient( + -45deg, + rgb(59 59 59 / 93%), + rgb(74 78 91 / 91%), + rgb(83 83 83 / 90%), + rgb(40, 40, 40, 91%) + ); + background-size: 400% 400%; + -webkit-animation: gradient 15s ease infinite; + animation: gradient 15s ease infinite; +} + +@-webkit-keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +@keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.tooltip-inner { + max-width: 100% !important; +} + +/* +* Datepicker +*/ +.datepicker { + top: 0; + left: 0; + padding: 4px; + margin-top: 1px; + color: #555555; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + -ms-border-radius: 1px; + -o-border-radius: 1px; + border-radius: 1px; +} + +.datepicker:before { + content: ""; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; +} + +.datepicker:after { + content: ""; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + position: absolute; + top: -6px; + left: 7px; +} + +.datepicker > div { + display: none; +} + +.datepicker table { + width: 100%; + margin: 0; +} + +.datepicker td, +.datepicker th { + text-align: center; + width: 20px; + height: 20px; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + -ms-border-radius: 1px; + -o-border-radius: 1px; + border-radius: 1px; +} + +.datepicker td.day:hover { + background: #f8f8f8; + cursor: pointer; +} + +.datepicker td.day.disabled { + color: #f8f8f8; +} + +.datepicker td.old, +.datepicker td.new { + color: white; +} + +.datepicker td.active, +.datepicker td.active:hover { + background: #618fb0; + color: #fff; +} + +.datepicker td span { + display: block; + width: 47px; + height: 54px; + line-height: 54px; + float: left; + margin: 2px; + cursor: pointer; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + -ms-border-radius: 1px; + -o-border-radius: 1px; + border-radius: 1px; +} + +.datepicker td span:hover { + background: #eeeeee; +} + +.datepicker td span.active { + background: #618fb0; + color: #fff; +} + +.datepicker td span.old { + color: #999999; +} + +.datepicker th.switch { + width: 145px; +} + +.datepicker th.next, +.datepicker th.prev { + font-size: 19.5px; +} + +.datepicker thead tr:first-child th { + cursor: pointer; +} + +.datepicker thead tr:first-child th:hover { + background: #eeeeee; +} + +.project-date { + font-size: 13px; + color: #a4c6ff; +} + +.day-progress { + position: absolute; + top: 75%; + left: 50%; + translate: -50% -50%; + font-size: 14px; + font-weight: 600; +} + +.timeline:before { + left: 0px !important; + background: none; +} + +.time_element { + overflow-x: scroll; + white-space: nowrap; +} + +/** +* Chart +*/ +.echart-no-data { + position: relative; +} + +.echart-no-data .message { + position: absolute; + transform: translateY(-50%); + text-align: center; + width: 100%; + top: 50%; + left: 0; +} + +svg.overlay { + background-color: transparent; +} + +svg .node { + cursor: pointer; +} + +svg .node rect { + fill-opacity: 0.9; + shape-rendering: crispEdges; +} + +svg .node text { + pointer-events: none; + font-size: 12px; +} + +svg .link { + fill: none; + stroke: #ccc; + stroke-width: 1.5px; + stroke-opacity: 0.2; +} + +svg .link:hover { + stroke-opacity: 0.5; +} + +svg .templink { + fill: none; + stroke: red; + stroke-width: 3px; +} +svg .ghostCircle.show { + display: block; +} + +.dashboard-top-section { + min-height: 230px; +} + +.ms-choice { + height: 16px; + margin-top: unset; +} + +.needle, +.needle-center { + fill: #f1f2f6; +} + +#total-icon-legend { + position: absolute; + top: 250px; + right: 10px; +} + +#total-icon-legend > div { + display: flex; + align-items: center; + margin-bottom: 0.3em; +} + +#total-icon-legend > div:last-child { + margin-bottom: 0; +} + +#total-icon-legend > div > .text { + padding-top: 3px; + font-size: 10px; +} + +.circle { + display: inline-block; + background-color: red; + width: 11px; + height: 11px; + border-radius: 50px; + margin-top: 4px; + margin-right: 8px; + border: 1px solid white; +} + +.c3-bars path { + stroke: white; + stroke-width: 0.5px; +} + +@media (max-width: 768px) { + #chart-product-manpower svg { + position: absolute; + top: 100%; + } +} + +#chart-product-manpower { + position: relative; + height: 780px; +} + +#chart-product-manpower svg { + position: absolute; + top: 60%; + left: 50%; + transform: translate(-50%, -50%); +} + +.xlarge { + height: 500px !important; +} + +.chart-footer { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; /* 아이템을 왼쪽으로 정렬 */ + align-content: flex-start; /* 아이템을 위쪽으로 정렬 */ + width: 100%; +} +.chart-footer .footer-item { + border-top: 2px solid; + box-sizing: border-box; + width: 33.33%; /* 기본 너비 설정 */ + padding: 1px 2px; + background: rgba(51, 51, 51, 0.4); +} +.chart-footer .footer-item .item-name, +.chart-footer .footer-item .item-value { + text-align: center; + width: 100%; + margin-top: 2px; +} +.chart-footer .footer-item .item-value { + font-weight: bold; +} + +.chat-footer button { + margin-top: 0; +} + +#chart-manpower-requirement, +#chart-manpower-requirement-canvaswidget, +#chart-manpower-requirement-canvas { + width: 100% !important; +} + +#chart-manpower-requirement-label { + width: 100% !important; + position: relative !important; +} + +/* +* tree bar +*/ +#tree_bar_container .link { + fill: none; + stroke: #ccc; + stroke-opacity: 0.4; + stroke-width: 1px; +} +#tree_bar_container text { + font-family: "Arial Black", Gadget, sans-serif; + fill: white; + font-weight: bold; + font-size: 12px; + text-shadow: none; +} + +#tree_bar_container .xAxis .tick text { + fill: white; +} +#tree_bar_container .grid .tick line { + stroke: grey; + stroke-dasharray: 5, 10; + opacity: 0.7; +} +#tree_bar_container .grid path { + stroke-width: 0; +} + +#tree_bar_container .node circle { + fill: #999; +} +#tree_bar_container .node--internal circle { + fill: #555; +} + +#tree_bar_container .node--internal .root { + transform: translateX(10px); +} + +/* gant */ +#shcedule_update { + height: 28px; +} + +.project-progress .progress-group { + display: flex; + align-items: flex-start; + margin-bottom: 10px; +} + +.progress-group label { + flex-shrink: 0; + width: 150px; + line-height: 30px; + text-align: right; + margin-right: 5px; +} + +.progress-group .progress-desc { + display: flex; + align-items: flex-start; +} + +.progress-group .progress-desc .icon { + width: 38px; + height: 30px; +} + +.progress-group .progress-desc .progress-input { + flex-shrink: 0; + display: flex; + width: 125px; + align-items: center; +} + +.project-progress input { + width: 63px !important; + margin-right: 2px; + padding: 5px !important; + text-align: right; +} + +.project-progress .input-group-addon.input-desc { + display: flex; + width: auto; + text-align: left; + line-height: 18px; +} +.project-progress .input-group-addon .fa { + line-height: 15px; +} + +.project-progress .input-desc .font11 { + margin-left: 5px; + white-space: normal; + word-break: keep-all; +} + +/* vertical timeline chart */ +#vertical-timeline { + overflow-y: auto; + height: 732px; + padding-right: 20px; + margin: 0px; +} + +.timeline-container { + margin-bottom: 20px; +} + +.timeline-container ul { + margin: 0; + list-style: none; + position: relative; + color: #fff; + font-size: 13px; + padding: 15px 0 0 75px; +} + +.timeline-container ul:before { + content: ""; + width: 1px; + height: 100%; + position: absolute; + border-left: 2px dashed #fff; +} + +.timeline-container .session { + position: relative; + margin-left: 30px; + background-color: rgba(51, 51, 51, 1); + padding: 14px; + border-radius: 6px; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.12), 0 2px 2px rgba(0, 0, 0, 0.08); +} + +.timeline-container .session:not(:first-child) { + margin-top: 40px; +} + +.timeline-container .session > span { + width: 2px; + height: 100%; + background: #fff; + left: -30px; + top: 0; + position: absolute; +} + +.timeline-container .session > span:before { + content: ""; + width: 10px; + height: 10px; + border-radius: 50%; + border: 2px solid #fff; + position: absolute; + background: #5470c6ff; + left: -4px; + top: 0; +} + +.timeline-container .session .time-range { + height: 100%; +} + +.timeline-container .session .time-range .date { + position: absolute; + font-size: 10px; + top: 0; + left: -74px; + font-weight: bold; + line-height: normal; +} + +.timeline-container .session > span:after { + top: calc(100% - 5px); +} + +.timeline-container .session-content .version { + font-weight: 600; + font-size: 14px; +} + +.timeline-container .session-content .summary { + margin-top: 5px; + font-weight: 300; +} + +.timeline-container .session-content .project-names { + margin-top: 5px; +} + +.project-name { + margin: 2px 5px 2px 0; + padding: 3px 5px; + font-size: 11px; + font-weight: bold; + background-color: #2477FF; + border: none; + border-radius: 5px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 3px 10px 0 rgba(0, 0, 0, 0.19); +} + +.tree-container.widget.large, +.tree-container.widget.large .body { + height: auto; +} + +.vertical-chevron-up, +.vertical-chevron-down { + position: absolute; + left: 48%; + transform: translateY(-20px); + font-size: 20px; + cursor: pointer; +} + +.vertical-chevron-down { + transform: translateY(20px); +} + +.fa-chevron-up:hover, +.fa-chevron-down:hover { + opacity: 80%; +} + +@media screen and (max-width: 1295px) { + .timeline-period { + flex: 1 1 auto; + margin-bottom: 10px; + } +} + +.chat-message .chat-message-body { + margin-left: 0px !important; + padding: 8px 10px; + background: rgba(51, 51, 51, 0.3); + border-radius: 3px; + position: relative; +} + +.chat-message-body.on-left .arrow { + border-left: 5px solid #e5603b; +} + +.chat-message .chat-message-body.arrow:before { + content: ""; + display: block; + position: absolute; + top: 21px; + left: -7px; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #a4c6ff; +} + +.detail_qna .chat-message .sender { + display: flex; + flex-direction: column; + justify-content: center; +} + +.detail_qna .chat-message-body .arrow { + top: calc(50% - 5px); +} + +.detail_qna .chat-message .chat-message-body { + margin-left: 63px !important; +} + +.detail_qna .chat-message .chat-message-body.on-left { + margin-left: 0px !important; + border-right: 2px solid #e5603b; +} + +.detail_qna .chat-message-body .action-group { + margin-top: 15px; +} + +.detail_qna .chat-message-body .action-group button { + margin-right: 5px; + border: 0; + background-color: transparent; +} + +.detail_qna .date-separator { + text-align: center; + width: 180px; + margin: 30px auto; + padding: 6px; + font-size: 15px; + background: rgba(51, 51, 51, 0.3); + border-radius: 15px; +} + +.detail_qna .edit-text-area { + width: 100%; + resize: vertical; + background: transparent; +} + +.detail_qna .eidt-comment, +.detail_qna .edit-group { + display: none; +} + +.detail_qna .empty-message { + display: flex; + height: 550px; + justify-content: center; + align-items: center; +} + +.widget > .carousel { + margin: 0 -12px 5px; +} + +.carousel-control.left, +.carousel-control.right { + background: none; +} +.carousel-control i { + position: absolute; + top: 50%; + left: 50%; + z-index: 5; + display: inline-block; + width: 20px; + height: 45px; + margin-left: -10px; + margin-top: -10px; +} + +.carousel-indicators.outer { + bottom: -20px; +} +.carousel-indicators li { + background-color: rgba(51, 51, 51, 0.4); +} + +.service-portfolio .filter-options-wrap { + margin: 20px 0; +} +.service-portfolio .filter-option { + position: relative; + margin-bottom: 8px; + padding-left: 13px; + cursor: pointer; +} + +.service-portfolio .filter-active:before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 3px; + background-color: #e5603b; +} + +.service-portfolio .portfolio-wrap { + display: flex; + margin: 20px 0; +} + +.service-portfolio .portfolio-item { + float: none; +} + +.service-portfolio .portfolio-item-wrap { + position: relative; + height: 0; + padding-bottom: 100%; +} + +.service-portfolio .portfolio-item-wrap img { + width: 100%; +} + +.service-portfolio .portfolio-info { + opacity: 1; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + text-align: center; + z-index: 3; + transition: all ease-in-out 0.3s; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow-wrap: anywhere; +} + +.service-portfolio .portfolio-item-wrap:before { + content: ""; + background: rgba(40, 40, 40, 0.5); + position: absolute; + left: 5px; + right: 5px; + top: 5px; + bottom: 5px; + transition: all ease-in-out 0.3s; + z-index: 2; + opacity: 0; +} + +.service-portfolio .portfolio-info { + opacity: 0; + transition: all ease-in-out 0.3s; +} + +.service-portfolio .portfolio-info h4, +.service-portfolio .portfolio-info p { + font-size: 12px; +} + +.service-portfolio .portfolio-info .portfolio-links a { + color: #fff; + margin: 5px 2px 0; + font-size: 20px; + display: inline-block; + transition: 0.3s; +} + +.service-portfolio .portfolio-info .portfolio-links a:hover { + color: #148af9; +} + +.service-portfolio .portfolio-item-wrap:hover:before { + top: -10px; + left: -10px; + right: -10px; + bottom: -10px; + opacity: 1; +} + +.service-portfolio .portfolio-item-wrap:hover .portfolio-info { + opacity: 1; +} + +.req-table, +.req-select { + background: rgba(51, 51, 51, 0.8) !important; + border: 1px solid rgba(51, 51, 51, 0.825) !important; +} + +.req-table.dropdown-menu a:hover, +.req-table.dropdown-menu .active a, +.req-select.dropdown-menu a:hover, +.req-select.dropdown-menu .active a { + background: none; +} + +.req-table.dropdown-menu a:hover, +.req-select.dropdown-menu a:hover, +#select2-selected_pdService-container .select2-selection__placeholder, +.multiple-select .ms-choice > span.placeholder { + color: #f8f8f8; +} +.req-table.dropdown-menu .active a, +.req-select.dropdown-menu .active a, +#disabled_input_pdservice.form-control { + color: #a4c6ff; +} + +.req-select { + min-width: auto; +} + +#reqDataTable { + padding-top: 0; + margin-bottom: 0; + max-height: 745px; + background: 0 none; + overflow: auto; +} + +#reqDataTable .reqTable { + width: max-content; + min-width: 100%; + table-layout: fixed; +} + +#reqDataTable .reqTable th { + padding: 20px 10px 10px; + text-align: center; + position: sticky; + top: 0; + z-index: 1; + background-color: rgba(51, 51, 51, 0.9); +} + +#reqDataTable .reqTable td { + padding: 5px 10px; + text-align: center; +} + +#reqDataTable .reqTable td.depth1, +#reqDataTable .reqTable td.depth2, +#reqDataTable .reqTable td.depth3, +#reqDataTable .reqTable td.content { + text-align: left; +} + +#reqDataTable .reqTable .tbody input { + width: 100%; + border: 0 none; + background: 0 none; +} + +#reqDataTable .reqTable .tbody tr:nth-child(even) { + background-color: rgba(51, 51, 51, 0.325); +} +#reqDataTable .reqTable .tbody tr:first-child { + height: 37px !important; +} +#reqDataTable .reqTable .tbody tr td { + position: relative; + border-right: 2px solid #515256; +} + +#reqDataTable .reqTable { + cursor: default; +} + +#reqDataTable .reqTable th.version, +#reqDataTable .reqTable th.category, +#reqDataTable .reqTable th.id, +#reqDataTable .reqTable th.status, +#reqDataTable .reqTable th.priority, +#reqDataTable .reqTable th.difficulty, +#reqDataTable .reqTable th.createDate, +#reqDataTable .reqTable th.startDate, +#reqDataTable .reqTable th.endDate, +#reqDataTable .reqTable th.progress { + cursor: pointer; +} + +#reqDataTable .reqTable td.content { + cursor: text; +} + +#reqDataTable .reqTable th.version:after, +#reqDataTable .reqTable th.category:after, +#reqDataTable .reqTable th.id:after, +#reqDataTable .reqTable th.status:after, +#reqDataTable .reqTable th.priority:after, +#reqDataTable .reqTable th.difficulty:after, +#reqDataTable .reqTable th.createDate:after, +#reqDataTable .reqTable th.startDate:after, +#reqDataTable .reqTable th.endDate:after, +#reqDataTable .reqTable th.progress:after { + content: ""; + display: inline-block; + width: 19px; + height: 19px; + margin-left: 10px; + vertical-align: middle; + background-repeat: no-repeat; + background-position: center; + background-image: url(../img/datatable/sort_both.png); +} + +#reqDataTable .reqTable th.asc:after { + background-image: url(../img/datatable/sort_asc.png); +} + +#reqDataTable .reqTable th.desc:after { + background-image: url(../img/datatable/sort_desc.png); +} +#reqDataTable .root { + padding-left: 25px !important; + text-align: left !important; +} + +#reqDataTable .pivotTable { + min-width: 120%; +} + +#reqDataTable .pivotTable .highlight { + color: #a4c6ff; +} + +#reqDataTable .pivot-toggle { + margin-right: 3px; + padding: 0 3px; + background-color: transparent; +} +#reqDataTable .pivot-toggle .fa { + vertical-align: text-bottom; + font-size: 14px; + color: #a4c6ff; +} +#reqDataTable .pivot-toggle .fa-minus-square { + display: none; +} +#reqDataTable .pivot-toggle.active .fa-plus-square { + display: none; +} +#reqDataTable .pivot-toggle.active .fa-minus-square { + display: block; +} + +/* cost 분석 */ + +/* 비용 입력 */ +.cost-versions { + margin: 30px 0; + /*padding-left: 20px;*/ +} + +.cost-version, +.cost-input-complete { + margin-right: 10px; + padding: 3px 7px; + font-size: 11px; + font-weight: bold; + background-color: #5470c6ff; + border: none; + border-radius: 5px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 3px 10px 0 rgba(0, 0, 0, 0.19); +} + +.cost-input-complete { + font-size: 15px; + background-color: rgb(51, 51, 51); + box-shadow: none; + margin: 0; +} + +.cost-version:hover, +.cost-input-complete:hover { + background-color: #39393b; +} + +.cost-input-version, +.cost-input-manpower { + margin-bottom: 20px; + border-radius: 3px; + padding: 5px; + margin-bottom: 30px; + background: rgba(51, 51, 51, 0.425); + color: #f8f8f8; +} + +.cost-version-info { + width: 30%; + margin: 10px 0 10px 0; + padding: 20px; + border-radius: 7px; + background-color: #4b4a4a; +} + +.cost-version-info div { + padding-top: 10px; +} + +.cost-input, +.annual-income-input { + color: black; + width: 50%; + text-align: center; + border: none; + border-radius: 5px; + padding: 5px; + background: rgba(157, 156, 156, 0.22); +} + +.version-input { + width: 70%; +} + +.dt-center { + text-align: center; +} + +.more { + display: inline-block; + border: 1px solid rgba(255, 255, 255, 0.3); + letter-spacing: 0; + z-index: 1; + position: relative; + margin-top: 10px; + color: #fff; + font-size: 13px; +} + +.animated-button1 { + display: inline-block; + -webkit-transform: translate(0%, 0%); + transform: translate(0%, 0%); + overflow: hidden; + color: #fff; + font-size: 12px; + text-decoration: none; +} + +.animated-button1::before { + content: ""; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: #8593ad; + opacity: 0; + -webkit-transition: 0.2s opacity ease-in-out; + transition: 0.2s opacity ease-in-out; +} + +.animated-button1 span { + position: absolute; +} + +@keyframes animateTop { + 0% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + } + + 100% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +.animated-button1 span:nth-child(1) { + top: 0px; + left: 0px; + width: 100%; + height: 1px; + background: -webkit-gradient(linear, right top, left top, from(rgba(43, 8, 8, 0)), to(#fff)); + background: linear-gradient(to left, rgba(43, 8, 8, 0), #fff); + -webkit-animation: 2s animateTop linear infinite; + animation: 2s animateTop linear infinite; +} + +@keyframes animateRight { + 0% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + } + + 100% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } +} + +.animated-button1 span:nth-child(2) { + top: 0px; + right: 0px; + height: 100%; + width: 1px; + background: -webkit-gradient(linear, left bottom, left top, from(rgba(43, 8, 8, 0)), to(#fff)); + background: linear-gradient(to top, rgba(43, 8, 8, 0), #fff); + -webkit-animation: 2s animateRight linear -1s infinite; + animation: 2s animateRight linear -1s infinite; +} + +@keyframes animateBottom { + 0% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } + + 100% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + } +} + +.animated-button1 span:nth-child(3) { + bottom: 0px; + left: 0px; + width: 100%; + height: 1px; + background: -webkit-gradient(linear, left top, right top, from(rgba(43, 8, 8, 0)), to(#fff)); + background: linear-gradient(to right, rgba(43, 8, 8, 0), #fff); + -webkit-animation: 2s animateBottom linear infinite; + animation: 2s animateBottom linear infinite; +} + +@keyframes animateLeft { + 0% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } + + 100% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + } +} + +.animated-button1 span:nth-child(4) { + top: 0px; + left: 0px; + height: 100%; + width: 1px; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(43, 8, 8, 0)), to(#fff)); + background: linear-gradient(to bottom, rgba(43, 8, 8, 0), #fff); + -webkit-animation: 2s animateLeft linear -1s infinite; + animation: 2s animateLeft linear -1s infinite; +} + +.modal-dialog .modal-content { + padding: 20px; +} + +.modal-dialog .widget-controls { + top: 20px !important; + right: 20px; +} + +@media (min-width: 768px) { + .modal-dialog { + width: 80%; + } +} + +.page-header .search-query { + width: 300px; +} +.page-header .search-query:focus { + width: 340px; +} + +#nav-search-button { + display: inline-block; + color: rgba(51, 51, 51, 0.5); + width: 40px; + padding: 0; + font-size: 19.5px; + outline: 0; + background: none; + text-align: center; + vertical-align: middle; + line-height: 36px; + text-shadow: none; +} + +#nav-search-button.highlight, +#nav-search-button.highlight a { + background: rgba(51, 51, 51, 0.5); + color: #b8b8b8; + border-radius: 3px; + font-size: 17px; + width: 30px; + line-height: 30px; +} + +/* 칸반 보드 */ +.kanban-top { + width: 100%; + margin-bottom: 30px; +} + +.kanban-top:last-child { + margin-bottom: 0; +} + +.kanban-board { + background: rgba(51, 51, 51, .425) !important; + margin-bottom: 12px !important; +} + +.kanban-item { + display: flex; + justify-content: space-between; + align-items: flex-start; + background: rgba(51, 51, 51, 1) !important; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.12), 0 2px 2px rgba(0, 0, 0, 0.08); + border-radius: 6px; + cursor: pointer !important; +} + +.req_item { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 2rem; + word-break: keep-all; +} + +.kanban-drag { + padding: 10px !important; + overflow: auto; +} + +.show-info { + cursor: pointer; +} + +.req_item_tooltip { + position: absolute; + background-color: rgb(89 89 89 / 50%); + padding: 5px 10px; + border-radius: 4px; + font-size: 11px; + z-index: 900; +} + +.kanban_assist { + display: flex; + align-items: center; + justify-content: space-between; +} + +.kanban_search_clear { + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + cursor: pointer; + display: none; + z-index: 5; +} + +.kanban_stats { + display: flex; + flex-direction: row; + justify-content: space-between; + border-radius: 3px; + padding: 4.9px; + background: rgba(51, 51, 51, 0.425); + border: 1px solid rgba(51, 51, 51, 0.5); +} + +.kanban-board header { + font-size: 14px !important; + padding: 14px !important; +} + +/* drawio */ +.drawio-time { + color: #f8f8f8; + margin-left: 10px; +} + +.drawio-gallery { + display: flex; + flex-wrap: wrap; + flex-direction: row; + align-items: center; + justify-content: space-between; + /*background: rgba(51, 51, 51, 0.425);*/ + border-radius: 3px; +} + +.drawio-image { + display: flex; + align-items: center; + justify-content: space-evenly; + width: 200px; + height: 200px; + padding: 10px; + border-radius: 3px; +} + +.drawio-image img { + width: 100%; + height: 100%; + object-fit: contain; + background-color: #fff; +} + +.edit-red{ + border:1px solid #DB2A34; +} +.edit-red:hover{ + background-color: rgba(219,42,52,0.5); +} +.edit-red:hover .fa { + color: #FFFFFF !important; +} +.edit-red.active, +.edit-red.active.focus, +.edit-red.active:focus{ + background-color: rgba(219,42,52,0.5) !important; +} +.edit-red.active .fa, +.edit-red.active.focus .fa , +.edit-red.active:focus .fa { + color: #FFFFFF !important; +} +.edit-orange{ + border:1px solid #E49400; +} +.edit-orange:hover{ + background-color: rgba(228,148,0,0.5); +} +.edit-orange:hover .fa { + color: #FFFFFF !important; +} +.edit-orange.active, +.edit-orange.active.focus, +.edit-orange.active:focus{ + background-color: rgba(228,148,0,0.5) !important; +} +.edit-orange.active .fa, +.edit-orange.active.focus .fa, +.edit-orange.active:focus .fa { + color: #FFFFFF !important; +} +.edit-green{ + border:1px solid #2D8515; +} +.edit-green:hover{ + background-color: rgba(45,133,21,0.5); +} +.edit-green:hover .fa { + color: #FFFFFF !important; +} +.edit-green.active, +.edit-green.active.focus, +.edit-green.active:focus{ + background-color: rgba(45,133,21,0.5) !important; +} +.edit-green.active .fa, +.edit-green.active.focus .fa, +.edit-green.active:focus .fa { + color: #FFFFFF !important; +} +.edit-blue{ + border:1px solid #2477FF; +} +.edit-blue:hover{ + background-color: rgba(36,119,255,0.5); +} +.edit-blue:hover .fa { + color: #FFFFFF !important; +} +.edit-blue:hover{ + background-color: rgba(36,119,255,0.5); +} +.edit-blue:hover .fa { + color: #FFFFFF !important; +} +.edit-blue.active, +.edit-blue.active.focus, +.edit-blue.active:focus{ + background-color: rgba(36,119,255,0.5) !important; +} +.edit-blue.active .fa, +.edit-blue.active.focus .fa, +.edit-blue.active:focus .fa { + color: #FFFFFF !important; +} +.edit-grey{ + border:1px solid #8c8c8c; +} +.edit-grey:hover{ + background-color: rgba(140,140,140,0.5); +} +.edit-grey:hover .fa { + color: #FFFFFF !important; +} +.edit-grey:hover{ + background-color: rgba(140,140,140,0.5); +} +.edit-grey:hover .fa { + color: #FFFFFF !important; +} +.edit-grey.active, +.edit-grey.active.focus, +.edit-grey.active:focus{ + background-color: rgba(140,140,140,0.5) !important; +} +.edit-grey.active .fa, +.edit-grey.active.focus .fa, +.edit-grey.active:focus .fa { + color: #FFFFFF !important; +} + +div.slimScrollDiv > ul { + overflow: auto !important; +} + +/* 현황 관리 테이블 */ +td.details-control { + cursor: pointer; +} + +.child-table-container { + margin-left: 26px; +} \ No newline at end of file Index: arms/js/common.js =================================================================== diff -u --- arms/js/common.js (revision 0) +++ arms/js/common.js (revision bdfa1e19b5c80752c583ed09eed066f990162c24) @@ -0,0 +1,2093 @@ +//////////////////////////////////////////////////////////////////////////////////////// +//Const +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//인증관련 공통 변수 +//////////////////////////////////////////////////////////////////////////////////////// +let userName; +let fullName; +let userApplicationRoles; +let userAttributes; +let userEnabled; +let userGroups; +let userID; +let userRealmRoles; +let permissions; +let userEmail; +let scrollPos = 0; +//////////////////////////////////////////////////////////////////////////////////////// +//Document Ready +//////////////////////////////////////////////////////////////////////////////////////// +$(function () { + var str = window.location.href; + if (str.indexOf("php") > 0) { + console.table("313DEVGRP 커뮤니티에 오신것을 환영합니다."); + runScript(); + } else { + authUserCheck(); + } +}); + +function runScript() { + // Page load & 상단 페이지 로드 프로그래스바 + topbarConfig(); + topbar.show(); + setTimeout(function () { + $(".container").fadeIn("slow"); + topbar.hide(); + }, 2000); + + /* 맨위로 아이콘 */ + rightBottomTopForwardIcon(); + + var urlParams = new URL(location.href).searchParams; + var onlyContents = urlParams.get("withoutLayer"); + if (isEmpty(onlyContents)) { + $("body").removeAttr("class"); + } else { + $("body").addClass("sidebar-hidden"); + $("header.page-header").hide(); + } + + $(document).on("shown.bs.tab", "a[data-toggle='tab']", function () { + window.dispatchEvent(new Event("resize")); + }); + + if (ajax_setup()) { + $(".loader").removeClass("hide"); + + var page = urlParams.get("page"); + if (isEmpty(page)) { + page = "index"; + } + if (includeLayout(page)) { + $.getScript("js/" + page + ".js", function () { + /* 로그인 인증 여부 체크 함수 */ + execDocReady(); + dwr_login(userName, userName); + menu_setting(); + }); + } + } +} + +function menu_setting() { + + console.log("롤에 의한 메뉴를 설정합니다."); + + var role_user = "ROLE_USER"; + var role_manager = "ROLE_MANAGER"; + var role_admin = "ROLE_ADMIN"; + + if( isEmpty(permissions) == false && permissions.indexOf(role_user) != -1){ + console.log("user 권한의 메뉴를 표현합니다."); + $("#menu_login").addClass("hide"); + $("#menu_dashboard").removeClass("hide"); + $("#menu_detail").removeClass("hide"); + } + if( isEmpty(permissions) == false && permissions.indexOf(role_manager) != -1){ + console.log("manager 권한의 메뉴를 표현합니다."); + $("#menu_login").addClass("hide"); + $("#menu_dashboard").removeClass("hide"); + $("#menu_detail").addClass("hide"); + $("#menu_product").removeClass("hide"); + $("#menu_alm").removeClass("hide"); + $("#menu_requirement").removeClass("hide"); + } + if( isEmpty(permissions) == false && permissions.indexOf(role_admin) != -1){ + console.log("admin 권한의 메뉴를 표현합니다."); + $("#menu_login").addClass("hide"); + $("#menu_dashboard").removeClass("hide"); + $("#menu_detail").addClass("hide"); + $("#menu_product").removeClass("hide"); + $("#menu_alm").removeClass("hide"); + $("#menu_requirement").removeClass("hide"); + $("#menu_analysis").removeClass("hide"); + } + + + +} + +function widgsterWrapper() { + + $.fn.widgster.Constructor.prototype.fullscreen = function () { + var self = this.$element; + var $widget = $("#widget-fullscreen"); + var url = self.data("layout"); + + if(url) { + $widget.load(url, function() { + $widget.show(); + $widget.find("[data-widgster='restore']").show(); + $("body").css("overflow", "hidden"); + + self.trigger($.Event("fullscreened.widgster")); + widgsterDocReady(); + }); + } else { + console.log("[common :: widgsterWrapper] :: fullscreen layout 이 없습니다."); + } + + return false; + }; + + $.fn.widgster.Constructor.prototype.restore = function () { + var $widget = $("#widget-fullscreen"); + + $widget.empty(); + $widget.hide(); + $widget.find("[data-widgster='restore']").hide(); + + $("body").css("overflow", ""); + + this.$element.trigger($.Event("restored.widgster")); + + return false; + }; + +} + +function 로드_완료_이후_실행_함수() { + 톱니바퀴_초기설정(); + loadLocale(); + widgsterWrapper(); + 검색_이벤트_트리거(); + + 우측_상단_사용자_정보_설정(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 우측_상단_사용자_정보_설정 +//////////////////////////////////////////////////////////////////////////////////////// +function 우측_상단_사용자_정보_설정() { + + var str = window.location.href; + if (str.indexOf("php") > 0) { + var account_html = "not supported"; + $("#login_id").append(account_html); + }else { + var account_html = "\"" + userName + "\""; + $("#login_id").append(account_html); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// 플러그인 로드 모듈 ( 병렬 시퀀스 ) +//////////////////////////////////////////////////////////////////////////////////////// +function loadPlugin(url) { + return new Promise(function (resolve, reject) { + if (isJavaScriptFile(url)) { + $(".spinner").html( + ' ' + + getFileNameFromURL(url) + + { + ko: " 자바스크립트를 다운로드 중입니다...", + en: " JavaScript is downloading...", + jp: " JavaScriptをダウンロード中です..." + }[getCookie("locale") || "ko"] + ); + $.ajax({ + url: url, + dataType: "script", + cache: true, + success: function () { + // The request was successful + + console.log("[ common :: loadPlugin ] :: url = " + url + " 자바 스크립트 플러그인 로드 성공"); + resolve(); // Promise를 성공 상태로 변경 + }, + error: function () { + // The request failed + console.error("[ common :: loadPlugin ] :: url = " + url + " 플러그인 로드 실패"); + reject(); // Promise를 실패 상태로 변경 + } + }); + } else { + $(".spinner").html( + ' ' + + getFileNameFromURL(url) + + { + ko: " 스타일시트를 다운로드 중입니다...", + en: " Downloading stylesheet...", + jp: " スタイルシートをダウンロード中です..." + }[getCookie("locale") || "ko"] + ); + $("", { + rel: "stylesheet", + type: "text/css", + href: url + }).appendTo("head"); + console.log("[ common :: loadPlugin ] :: url = " + url + " 스타일시트 플러그인 로드 성공"); + resolve(); + } + }); +} + +function getFileNameFromURL(url) { + var parts = url.split("/"); + return parts[parts.length - 1]; +} + +function isJavaScriptFile(filename) { + return filename.endsWith(".js"); +} + +function loadPluginGroupSequentially(group) { + return group.reduce(function (promise, url) { + return promise.then(function () { + return loadPlugin(url); + }); + }, Promise.resolve()); +} + +function loadPluginGroupsParallelAndSequential(groups) { + var promises = groups.map(function (group) { + return loadPluginGroupSequentially(group); + }); + return Promise.all(promises).then(function () { + 로드_완료_이후_실행_함수(); + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// include 레이아웃 html 파일을 로드하는 함수 +//////////////////////////////////////////////////////////////////////////////////////// +function includeLayout(page) { + var includeArea = $("[data-include]"); + var str = window.location.href; + + $.each(includeArea, function () { + var self = $(this); + var url = self.data("include"); + console.log("[ common :: includeLayout ] url = " + url); + + var hrefLink = window.location.href; + var urlParams = new URL(location.href).searchParams; + var mode = urlParams.get("mode"); + + if (url.indexOf("content-header") !== -1) { + url = "html/" + page + "/content-header.html"; + self.load(url, function () { + self.removeAttr("data-include"); + }); + } else if (url.indexOf("content-container") !== -1) { + url = "html/" + page + "/content-container.html"; + self.load(url, function () { + includeContents(self); + self.removeAttr("data-include"); + }); + } else if (url.indexOf("page-sidebar") !== -1) { + if (mode == "detail" || hrefLink.indexOf("detail.html") > 0) { + url = "/arms/html/detail/page-sidebar.html"; + self.load(url, function () { + self.removeAttr("data-include"); + }); + } else if (str.indexOf("controltower") > 0) { + url = "/controltower/html/template/page-sidebar.html"; + self.load(url, function () { + self.removeAttr("data-include"); + }); + } else { + url = "/arms/html/template/page-sidebar.html"; + self.load(url, function () { + self.removeAttr("data-include"); + }); + } + } else { + self.load(url, function () { + self.removeAttr("data-include"); + }); + } + }); + + return true; +} +//////////////////////////////////////////////// +// content-container 내부 첨부한 html include +//////////////////////////////////////////////// +function includeContents(contentContainer) { + var includeArea = contentContainer.find("[data-include]"); + + $.each(includeArea, function () { + var self = $(this); + var url = self.data("include"); + console.log(url); + + self.load(url, function (content) { + self.replaceWith(content); + }); + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 상단 페이지 로드 프로그래스바 설정 +//////////////////////////////////////////////////////////////////////////////////////// +function topbarConfig() { + topbar.config({ + autoRun: true, + barThickness: 3, + barColors: { + 0: "rgba(26, 188, 156, .9)", + ".25": "rgba(52, 152, 219, .9)", + ".50": "rgba(241, 196, 15, .9)", + ".75": "rgba(230, 126, 34, .9)", + "1.0": "rgba(211, 84, 0, .9)" + }, + shadowBlur: 10, + shadowColor: "rgba(0, 0, 0, .6)" + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +//슬림스크롤 +//////////////////////////////////////////////////////////////////////////////////////// +function makeSlimScroll(targetElement) { + $(targetElement).slimScroll({ + height: "250px", + railVisible: true, + railColor: "#222", + railOpacity: 0.3, + wheelStep: 10, + allowPageScroll: false, + disableFadeOut: false + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 맨위로 아이콘 +//////////////////////////////////////////////////////////////////////////////////////// +function rightBottomTopForwardIcon() { + $("#topicon").click(function () { + $("html, body").animate({ scrollTop: 0 }, 400); + return false; + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 로그인 인증 여부 체크 함수 +//////////////////////////////////////////////////////////////////////////////////////// +function authUserCheck() { + var str = window.location.href; + if (str.indexOf("community") > 0) { + runScript(); + } else { + $.ajax({ + url: "/auth-user/me", + type: "GET", + timeout: 7313, + global: false, + statusCode: { + 200: function (json) { + console.log("[ common :: authUserCheck ] userName = " + json.preferred_username); + console.log("[ common :: authUserCheck ] sub = " + json.sub); + console.log("[ common :: authUserCheck ] roles = " + json.realm_access.roles); + console.log("[ common :: authUserCheck ] email = " + json.email); + console.log("[ common :: authUserCheck ] name = " + json.name); + userName = json.preferred_username; + permissions = json.realm_access.roles; + userID = json.sub; + userEmail = json.email; + fullName = json.name; + + runScript(); + }, + 401: function (json) { + $(".loader").addClass("hide"); + jError("클라이언트가 인증되지 않았거나, 유효한 인증 정보가 부족하여 요청이 거부되었습니다."); + location.href = "/oauth2/authorization/middle-proxy"; + return false; + }, + 403: function (json) { + jError("서버가 해당 요청을 이해했지만, 권한이 없어 요청이 거부되었습니다."); + return false; + } + } + }); + + return true; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 사용자 정보 로드 함수 +//////////////////////////////////////////////////////////////////////////////////////// +function getUserInfo() { + $.ajax({ + url: "/auth-user/search-user/" + userName, + data: { + sendData: "" + }, + type: "GET", + progress: true, + statusCode: { + 200: function (json) { + console.log("authUserCheck length = :: " + json.length); + if (json.length > 1) { + jError("중복된 사용자가 있습니다."); + } else if (json.length == 0) { + jError("사용자 정보가 조회되지 않습니다."); + } else { + userApplicationRoles = json[0].applicationRoles; + userAttributes = json[0].attributes; + userEnabled = json[0].enabled; + userGroups = json[0].groups; + userID = json[0].id; + userRealmRoles = json[0].realmRoles; + console.log("authUserCheck :: userApplicationRoles = " + userApplicationRoles); + console.log("authUserCheck :: userAttributes = " + userAttributes); + console.log("authUserCheck :: userEnabled = " + userEnabled); + console.log("authUserCheck :: userGroups = " + userGroups); + console.log("authUserCheck :: userID = " + userID); + console.log("authUserCheck :: userRealmRoles = " + userRealmRoles); + } + } + } + }); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 유틸 : 말줄임표 +//////////////////////////////////////////////////////////////////////////////////////// +function getStrLimit(inputStr, limitCnt) { + if (isEmpty(inputStr)) { + return ""; + } else if (inputStr.length >= limitCnt) { + return inputStr.substr(0, limitCnt) + "..."; + } else { + return inputStr; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//서버 바인딩 할 수가 없어서 프로토타입 목적으로 json 을 만들어서 로드하는 함수 +//////////////////////////////////////////////////////////////////////////////////////// +var getJsonForPrototype = function (url, bindTemplate) { + ajaxGet(url).then(function (data) { + bindTemplate(data); + }); +}; +var ajaxGet = (url) => + $.ajax({ + url, + type: "GET", + global: false, + statusCode: { + 200: function (data) { + return data.responseJSON; + } + } + }); + +function dateFormat(timestamp) { + var d = new Date(timestamp), // Convert the passed timestamp to milliseconds + yyyy = d.getFullYear(), + mm = ("0" + (d.getMonth() + 1)).slice(-2), // Months are zero based. Add leading 0. + dd = ("0" + d.getDate()).slice(-2), // Add leading 0. + hh = d.getHours(), + h = hh, + min = ("0" + d.getMinutes()).slice(-2), // Add leading 0. + ampm = "AM", + time; + + if (hh > 12) { + h = hh - 12; + ampm = "PM"; + } else if (hh === 12) { + h = 12; + ampm = "PM"; + } else if (hh == 0) { + h = 12; + } + + // ie: 2013-02-18, 8:35 AM + time = yyyy + "년" + mm + "월" + dd + "일 - " + h + ":" + min + " " + ampm; + + return time; +} + +function getToday() { + var date = new Date(); + return date.getFullYear() + "/" + ("0" + (date.getMonth() + 1)).slice(-2) + "/" + ("0" + date.getDate()).slice(-2); +} + +// 최대값, 최소값 +function maxValue(arr) { + if (isEmpty(arr)) { + return []; + } else { + return arr.reduce((max, val) => (max > val ? max : val)); + } +} + +function minValue(arr) { + if (isEmpty(arr)) { + return []; + } else { + return arr.reduce((min, val) => (min < val ? min : val)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// --- 왼쪽 사이드 메뉴 설정 --- // +//////////////////////////////////////////////////////////////////////////////////////// +function setSideMenu(categoryName, listName, collapse) { + console.log("[ common :: setSideMenu ] :: categoryName → " + categoryName + ", listName → " + listName); + + setTimeout(function () { + //jira_icon + if (categoryName === "sidebar_menu_jira") { + $("#side_jira_neutral_icon").removeClass("hidden"); + $("#side_jira_white_icon").addClass("hidden"); + } else { + $("#side_jira_neutral_icon").addClass("hidden"); + $("#side_jira_white_icon").removeClass("hidden"); + } //.jira_icon + + $(`#${categoryName}`).css({ color: "#a4c6ff" }); + $(`#${categoryName}`).css({ "font-weight": "900" }); + + if (isEmpty(listName)) { + console.log("[ common :: setSideMenu ] :: listName → is null"); + } else { + $(`#${listName}`).addClass("active"); + $(`#${listName}`).css({ color: "#a4c6ff" }); + $(`#${listName}`).css({ "font-weight": "900" }); + } + $(".spinner").html( + " 어플리케이션 API Data를 가져오는 중입니다..." + ); + }, 1000); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// -- jstree build 설정 -- // +//////////////////////////////////////////////////////////////////////////////////////// +function jsTreeBuild(jQueryElementID, serviceNameForURL) { + console.log("common :: jsTreeBuild : ( jQueryElementID ) → " + jQueryElementID); + console.log("common :: jsTreeBuild : ( serviceNameForURL ) → " + serviceNameForURL); + + console.log("common :: jsTreeBuild : ( href ) → " + $(location).attr("href")); + console.log("common :: jsTreeBuild : ( protocol ) → " + $(location).attr("protocol")); + console.log("common :: jsTreeBuild : ( host ) → " + $(location).attr("host")); + console.log("common :: jsTreeBuild : ( pathname ) → " + $(location).attr("pathname")); + console.log("common :: jsTreeBuild : ( search ) → " + $(location).attr("search")); + console.log("common :: jsTreeBuild : ( hostname ) → " + $(location).attr("hostname")); + console.log("common :: jsTreeBuild : ( port ) → " + $(location).attr("port")); + + $(jQueryElementID) + .bind("before.jstree", function (e, data) { + $("#alog").append(data.func + "
"); + $("li:not([rel='drive']).jstree-open > a > .jstree-icon").css( + "background-image", + "url(../reference/jquery-plugins/jstree-v.pre1.0/themes/toolbar_open.png)" + ); + $("li:not([rel='drive']).jstree-closed > a > .jstree-icon").css( + "background-image", + "url(../reference/jquery-plugins/jstree-v.pre1.0/themes/ic_explorer.png)" + ); + }) + .jstree({ + // List of active plugins + plugins: ["themes", "json_data", "ui", "crrm", "dnd", "search", "types"], + themes: { theme: ["lightblue4"] }, + //contextmenu + contextmenu: { + items: { + // Could be a function that should return an object like this one + create: { + separator_before: true, + separator_after: true, + label: "Create", + action: false, + submenu: { + create_file: { + seperator_before: false, + seperator_after: false, + label: "File", + action: function (obj) { + this.create(obj, "last", { + attr: { + rel: "default" + } + }); + } + }, + create_folder: { + seperator_before: false, + seperator_after: false, + label: "Folder", + action: function (obj) { + this.create(obj, "last", { + attr: { + rel: "folder" + } + }); + } + } + } + }, + ccp: { + separator_before: false, + separator_after: true, + label: "Edit", + action: false, + submenu: { + cut: { + seperator_before: false, + seperator_after: false, + label: "Cut", + action: function (obj) { + this.cut(obj, "last", { + attr: { + rel: "default" + } + }); + } + }, + paste: { + seperator_before: false, + seperator_after: false, + label: "Paste", + action: function (obj) { + this.paste(obj, "last", { + attr: { + rel: "folder" + } + }); + } + }, + + changeType: { + seperator_before: false, + seperator_after: false, + label: "Change Type", + submenu: { + toFile: { + seperator_before: false, + seperator_after: false, + label: "toFile", + action: function (obj) { + this.set_type("default"); + } + }, + toFolder: { + seperator_before: false, + seperator_after: false, + label: "toFolder", + action: function (obj) { + this.set_type("folder"); + } + } + } + } + } + } + } + }, + + // I usually configure the plugin that handles the data first + // This example uses JSON as it is most common + json_data: { + // This tree is ajax enabled - as this is most common, and maybe a bit more complex + // All the options are almost the same as jQuery's AJAX (read the docs) + ajax: { + // the URL to fetch the data + url: serviceNameForURL + "/getChildNode.do", + cache: false, + // the `data` function is executed in the instance's scope + // the parameter is the node being loaded + // (may be -1, 0, or undefined when loading the root nodes) + data: function (n) { + // the result is fed to the AJAX request `data` option + console.log("[ common :: jsTreeBuild ] :: json data load = " + JSON.stringify(n)); + return { + c_id: n.attr ? n.attr("id").replace("node_", "").replace("copy_", "") : 1 + }; + }, + success: function (n) { + n.forEach(item => { + let type = item.attr.rel; + if(type !== "folder"){ + if (item.reqStateEntity && item.reqStateEntity.c_title) { + let state = item.reqStateEntity.c_title; + item.data = mappingStateIcon(state) +" "+item.data; + } + } + }); + jSuccess("Product(service) Data Load Complete"); + $(jQueryElementID).jstree("search", $("#text").val()); + if (typeof changeMultipleSelected === "function") { + changeMultipleSelected(); + } + } + } + }, + // Configuring the search plugin + search: { + // As this has been a common question - async search + // Same as above - the `ajax` config option is actually jQuery's AJAX object + + /** + * v1 : 검색 버튼 클릭 시 API 호출 후 응답 데이터를 jstree 에 바인딩 + ajax: { + url: serviceNameForURL + "/searchNode.do", + // You get the search string as a parameter + data: function (str) { + return { + searchString: str + }; + }, + success: function (n) { + jSuccess("search data complete"); + } + } + */ + + /** + * v2 : 검색 버튼 클릭 시 jstree 노드 필터링을 통해 검색 + */ + show_only_matches: true, + search_callback: function (str, node) { + return node.data().search(str); + } + }, + // Using types - most of the time this is an overkill + // read the docs carefully to decide whether you need types + types: { + // I set both options to -2, as I do not need depth and children count checking + // Those two checks may slow jstree a lot, so use only when needed + max_depth: -2, + max_children: -2, + // I want only `drive` nodes to be root nodes + // This will prevent moving or creating any other type as a root node + valid_children: ["drive"], + types: { + // The default type + default: { + // I want this type to have no children (so only leaf nodes) + // In my case - those are files + valid_children: "none", + // If we specify an icon for the default type it WILL OVERRIDE the theme icons + icon: { + image: "../reference/jquery-plugins/jstree-v.pre1.0/themes/attibutes.png" + } + }, + // The `folder` type + folder: { + // can have files and other folders inside of it, but NOT `drive` nodes + valid_children: ["default", "folder"], + icon: { + image: "../reference/jquery-plugins/jstree-v.pre1.0/themes/ic_explorer.png" + } + }, + // The `drive` nodes + drive: { + // can have files and folders inside, but NOT other `drive` nodes + valid_children: ["default", "folder"], + icon: { + image: "../reference/jquery-plugins/jstree-v.pre1.0/themes/home.png" + }, + // those prevent the functions with the same name to be used on `drive` nodes + // internally the `before` event is used + start_drag: false, + move_node: false, + delete_node: false, + remove: false + } + } + }, + // UI & core - the nodes to initially select and open will be overwritten by the cookie plugin + + // the UI plugin - it handles selecting/deselecting/hovering nodes + ui: { + // this makes the node with ID node_4 selected onload + initially_select: ["node_4"] + }, + // the core plugin - not many options here + core: { + // just open those two nodes up + // as this is an AJAX enabled tree, both will be downloaded from the server + initially_open: ["node_2", "node_3"] + } + }) + .bind("create.jstree", function (e, data) { + $.post( + serviceNameForURL + "/addNode.do", + { + ref: data.rslt.parent.attr("id").replace("node_", "").replace("copy_", ""), + c_position: data.rslt.position, + c_title: data.rslt.name, + c_type: data.rslt.obj.attr("rel") + }, + function (r) { + if (r.status) { + $(data.rslt.obj).attr("id", "node_" + r.id); + jNotify("Notification : Add Node, Complete !"); + } else { + $.jstree.rollback(data.rlbk); + } + $(jQueryElementID).jstree("refresh"); + } + ); + }) + .bind("remove.jstree", function (e, data) { + data.rslt.obj.each(function () { + $.ajax({ + async: false, + type: "POST", + url: serviceNameForURL + "/removeNode.do", + data: { + c_id: this.id.replace("node_", "").replace("copy_", "") + }, + success: function (r) { + jNotify("Notification : Remove Node, Complete !"); + $(jQueryElementID).jstree("refresh"); + } + }); + }); + }) + .bind("rename.jstree", function (e, data) { + $.post( + serviceNameForURL + "/alterNode.do", + { + c_id: data.rslt.obj.attr("id").replace("node_", "").replace("copy_", ""), + c_title: data.rslt.new_name, + c_type: data.rslt.obj.attr("rel") + }, + function (r) { + if (!r.status) { + $.jstree.rollback(data.rlbk); + } + jSuccess("Rename Node Complete"); + $(jQueryElementID).jstree("refresh"); + } + ); + }) + .bind("set_type.jstree", function (e, data) { + $.post( + serviceNameForURL + "/alterNodeType.do", + { + c_id: data.rslt.obj.attr("id").replace("node_", "").replace("copy_", ""), + c_title: data.rslt.new_name, + c_type: data.rslt.obj.attr("rel") + }, + function (r) { + jSuccess("Node Type Change"); + $(jQueryElementID).jstree("refresh"); + } + ); + }) + .bind("move_node.jstree", function (e, data) { + data.rslt.o.each(function (i) { + console.log("##### move :::", data); + $.ajax({ + async: false, + type: "POST", + url: serviceNameForURL + "/moveNode.do", + data: { + c_id: $(this).attr("id").replace("node_", "").replace("copy_", ""), + ref: data.rslt.cr === -1 ? 1 : data.rslt.np.attr("id").replace("node_", "").replace("copy_", ""), + c_position: data.rslt.cp + i, + c_title: data.rslt.name, + copy: data.rslt.cy ? 1 : 0, + multiCounter: i + }, + success: function (r) { + if (r.status) { + $.jstree.rollback(data.rlbk); + } else { + $(data.rslt.oc).attr("id", "node_" + r.id); + if (data.rslt.cy && $(data.rslt.oc).children("UL").length) { + data.inst.refresh(data.inst._get_parent(data.rslt.oc)); + } + } + + jNotify("Notification : Move Node Complete !"); + + $(jQueryElementID).jstree("refresh"); + } + }); + }); + }) + .bind("select_node.jstree", function (event, data) { + // `data.rslt.obj` is the jquery extended node that was clicked + if ($.isFunction(jsTreeClick)) { + console.log("[ jsTreeBuild :: select_node ] :: data.rslt.obj.data('id')" + data.rslt.obj.attr("id")); + console.log("[ jsTreeBuild :: select_node ] :: data.rslt.obj.data('rel')" + data.rslt.obj.attr("rel")); + console.log("[ jsTreeBuild :: select_node ] :: data.rslt.obj.data('class')" + data.rslt.obj.attr("class")); + console.log("[ jsTreeBuild :: select_node ] :: data.rslt.obj.children('a')" + data.rslt.obj.children("a")); + console.log("[ jsTreeBuild :: select_node ] :: data.rslt.obj.children('ul')" + data.rslt.obj.children("ul")); + jsTreeClick(data.rslt.obj); + } + }) + .bind("loaded.jstree", function (event, data) { + // 성능 이슈로 자동으로 전부 펼치기 닫음. + // setTimeout(function () { + // $(jQueryElementID).jstree("open_all"); + // }, 1500); + + $(jQueryElementID).slimscroll({ + height: "545px" + }); + }); + + $("#mmenu input, #mmenu button").click(function () { + switch (this.id) { + case "add_default": + case "add_folder": + $(jQueryElementID).jstree("create", null, "last", { + attr: { + rel: this.id.toString().replace("add_", "") + } + }); + break; + case "search": + $(jQueryElementID).jstree("search", document.getElementById("text").value); + break; + case "text": + break; + default: + $(jQueryElementID).jstree(this.id); + break; + } + }); + + $("#mmenu .form-search").submit(function (event) { + event.preventDefault(); + + $(jQueryElementID).jstree("search", document.getElementById("text").value); + }); + + function mappingStateIcon(key) { + if (key === "열림") { + return ''; + } else if (key === "진행중") { + return ''; + } else if (key === "해결됨") { + return ''; + } else if (key === "닫힘") { + return ''; + } + return ''; // 기본적으로 빈 문자열 반환 + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// UTIL : 널 체크 +//////////////////////////////////////////////////////////////////////////////////////// +var isEmpty = function (value) { + if ( + typeof value === "undefined" || + value == "" || + value == null || + value == undefined || + (value != null && typeof value == "object" && !Object.keys(value).length) + ) { + return true; + } else { + return false; + } +}; + +//////////////////////////////////////////////////////////////////////////////////////// +//데이터 테이블 +//////////////////////////////////////////////////////////////////////////////////////// +function dataTable_build( + jquerySelector, + ajaxUrl, + jsonRoot, + columnList, + rowsGroupList, + columnDefList, + selectList, + orderList, + buttonList, + + isServerSide, + scrollY, + data, + isAjax = true, + errorMode = true +){ + var isServerSide = false; + + return dataTable_extendBuild( + jquerySelector, + ajaxUrl, + jsonRoot, + columnList, + rowsGroupList, + columnDefList, + selectList, + orderList, + buttonList, + + isServerSide, + scrollY, + data, + isAjax, + errorMode = true + ); +} + +function dataTable_extendBuild( + jquerySelector, + ajaxUrl, + jsonRoot, + columnList, + rowsGroupList, + columnDefList, + selectList, + orderList, + buttonList, + isServerSide, + scrollY, + data, + isAjax = true, + errorMode = true +) { + var jQueryElementID = jquerySelector; + var reg = /[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]/gi; + var jQueryElementStr = jQueryElementID.replace(reg, ""); + var responsiveRender = { + details: { + renderer: function (api, rowIdx, columns) { + var outer = ""; + + var data = $.map(columns, function (col, i) { + return col.hidden + ? `
+
+ ${col.title} +
+
+ ${col.data} +
` + : ``; + }).join(``); + outer += data; + outer += ""; + + return outer ? $("").append(outer) : false; + } + } + }; + + console.log("[ common :: dataTableBuild ] :: jQueryElementStr → " + jQueryElementStr); + console.log("[ common :: dataTableBuild ] :: jQueryElementID → " + jQueryElementID); + console.log("[ common :: dataTableBuild ] :: columnList → " + columnList); + console.log("[ common :: dataTableBuild ] :: rowsGroupList → " + rowsGroupList); + console.log("[ common :: dataTableBuild ] :: href → " + $(location).attr("href")); + console.log("[ common :: dataTableBuild ] :: protocol → " + $(location).attr("protocol")); + console.log("[ common :: dataTableBuild ] :: host → " + $(location).attr("host")); + console.log("[ common :: dataTableBuild ] :: pathname → " + $(location).attr("pathname")); + console.log("[ common :: dataTableBuild ] :: search → " + $(location).attr("search")); + console.log("[ common :: dataTableBuild ] :: hostname → " + $(location).attr("hostname")); + console.log("[ common :: dataTableBuild ] :: port → " + $(location).attr("port")); + console.log("[ common :: dataTableBuild ] :: ajaxUrl → " + ajaxUrl); + + var options = { + serverSide: isServerSide, + stateDuration: -1, + destroy: true, + processing: true, + ordering: true, + stateSave: false, + responsive: false, + columns: columnList, + rowsGroup: rowsGroupList, + columnDefs: columnDefList.concat([ + { + targets: "_all", // 모든 컬럼에 적용. + createdCell: function (td) { + var $td = $(td); + // 숫자일 경우 우측 정렬 + if ($.isNumeric($td.text())) { + //$(td).addClass("dt-body-right"); + } + } + } + ]), + select: selectList, + order: orderList, + buttons: buttonList, + scrollX: true, + scrollY: scrollY, + language: { + processing: "", + loadingRecords: + ' 데이터를 처리 중입니다.', + paginate: { + next: '', + previous: '' + } + }, + pageLength: 10, // default pageLegth + initComplete: function (settings, json) { + console.log("dataTableBuild :: initComplete"); + if ($.isFunction(dataTableCallBack)) { + //데이터 테이블 그리고 난 후 시퀀스 이벤트 + dataTableCallBack(settings, json); + } + }, + drawCallback: function (tableInfo) { + console.log("dataTableBuild :: drawCallback"); + if ($.isFunction(dataTableDrawCallback)) { + //데이터 테이블 그리고 난 후 시퀀스 이벤트 + $("#" + tableInfo.sInstance) + .DataTable() + .columns.adjust() + .responsive.recalc(); + dataTableDrawCallback(tableInfo); + } + } + }; + + if (isAjax) { + options.ajax = { + url: ajaxUrl, + dataSrc: jsonRoot + }; + } else { + options.data = data; + } + + var tempDataTable = $(jQueryElementID).DataTable(options); + + $(jQueryElementID).on('page.dt', function() { + scrollPos = $(window).scrollTop(); + $(window).scrollTop(scrollPos); + }); + + $(jQueryElementID + " tbody").on("click", "tr", function () { + if ($(this).hasClass("selected")) { + $(this).removeClass("selected"); + } else { + tempDataTable.$("tr.selected").removeClass("selected"); + $(this).addClass("selected"); + } + + var selectedData = tempDataTable.row(this).data(); + + if (selectedData !== undefined) { + selectedData.selectedIndex = $(this).closest("tr").index(); + } + + var info = tempDataTable.page.info(); + + if (selectedData !== undefined && info !== undefined) { + selectedData.selectedPage = info.page; + } + + console.log("Show page: " + info.page + " of " + info.pages); + + dataTableClick(tempDataTable, selectedData); + }); + + // ----- 데이터 테이블 빌드 이후 스타일 구성 ------ // + //datatable 좌상단 datarow combobox style + $(".dataTables_length").find("select:eq(0)").addClass("darkBack"); + $(".dataTables_length").find("select:eq(0)").css("min-height", "30px"); + $(".dataTables_length").find("select:eq(0)").children().css("background", "#3B3D40"); + $(".dataTables_length").find("select:eq(0)").css("border-radius", "5px"); + $(jQueryElementID + "_wrapper").css("border-top", "1px solid rgba(51, 51, 51, 0.3)"); + $(jQueryElementID + "_wrapper").css("padding-top", "5px"); + + // ----- 데이터 테이블 빌드 이후 별도 스타일 구성 ------ // + //datatable 좌상단 datarow combobox style + $("body") + .find("[aria-controls='" + jQueryElementStr + "']") + .css("width", "50px"); + $(".dataTables_filter input[type=search]").css("width", "150px"); + $("select[name=" + jQueryElementStr + "]").css("width", "50px"); + + $.fn.dataTable.ext.errMode = function (settings, helpPage, message) { + console.log(message); + jError("Notification : Ajax Error, retry plz !"); + }; + + return tempDataTable; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//공통 AJAX setup 처리 +//////////////////////////////////////////////////////////////////////////////////////// +function ajax_setup() { + $(document) + .ajaxStart(function () { + $(".loader").removeClass("hide"); + }) + .ajaxSend(function (event, jqXHR, ajaxOptions) { + //$(".loader").addClass("hide"); + }) + .ajaxSuccess(function (event, jqXHR, ajaxOptions, data) { + //$(".loader").addClass("hide"); + }) + .ajaxError(function (event, jqXHR, ajaxSettings, thrownError) { + $(".loader").addClass("hide"); + if (jqXHR.status == 401) { + jError("클라이언트가 인증되지 않았거나, 유효한 인증 정보가 부족하여 요청이 거부되었습니다.", { + /** + * [options] + * autoHide / Boolean Default : true - jNotify closed after TimeShown ms or by clicking on it + * clickOverlay / Boolean Default : false - If false, disables closing jNotify by clicking on the background overlay. + * MinWidth / Integer Default : 200 - In pixel, the min-width css property of the boxes. + * TimeShown / Integer Default : 1500 - In ms, time of the boxes appearances. + * ShowTimeEffect / Integer Default : 200 - In ms, duration of the Show effect + * HideTimeEffect / Integer Default : 200 - In ms, duration of the Hide effect + * LongTrip / Integer Default : 15 - Length of the move effect ('top' and 'bottom' verticals positions only) + * HorizontalPosition / String Default : right - Horizontal position. Can be set to 'left', 'center', 'right' + * VerticalPosition / String Default : top - Vertical position. Can be set to 'top', 'center', 'bottom'. + * ShowOverlay / Boolean Default : true - If true, a background overlay appears behind the jNotify boxes + * ColorOverlay / String Default : #000 - Color of the overlay background (only Hex. color code) + * OpacityOverlay / Integer Default : 0.3 - Opacity CSS property of the overlay background. From 0 to 1 max. + */ + autoHide: true, // added in v2.0 + TimeShown: 3000, + HorizontalPosition: "center", + VerticalPosition: "top" + // onCompleted : function(){ // added in v2.0 + // alert('jNofity is completed !'); + // } + }); + location.href = "/oauth2/authorization/middle-proxy"; + } else if (jqXHR.status == 403) { + jError("서버가 해당 요청을 이해했지만, 권한이 없어 요청이 거부되었습니다.", { + /** + * [options] + * autoHide / Boolean Default : true - jNotify closed after TimeShown ms or by clicking on it + * clickOverlay / Boolean Default : false - If false, disables closing jNotify by clicking on the background overlay. + * MinWidth / Integer Default : 200 - In pixel, the min-width css property of the boxes. + * TimeShown / Integer Default : 1500 - In ms, time of the boxes appearances. + * ShowTimeEffect / Integer Default : 200 - In ms, duration of the Show effect + * HideTimeEffect / Integer Default : 200 - In ms, duration of the Hide effect + * LongTrip / Integer Default : 15 - Length of the move effect ('top' and 'bottom' verticals positions only) + * HorizontalPosition / String Default : right - Horizontal position. Can be set to 'left', 'center', 'right' + * VerticalPosition / String Default : top - Vertical position. Can be set to 'top', 'center', 'bottom'. + * ShowOverlay / Boolean Default : true - If true, a background overlay appears behind the jNotify boxes + * ColorOverlay / String Default : #000 - Color of the overlay background (only Hex. color code) + * OpacityOverlay / Integer Default : 0.3 - Opacity CSS property of the overlay background. From 0 to 1 max. + */ + autoHide: true, // added in v2.0 + TimeShown: 3000, + HorizontalPosition: "center", + VerticalPosition: "top" + // onCompleted : function(){ // added in v2.0 + // alert('jNofity is completed !'); + // } + }); + //location.href = "/oauth2/authorization/middle-proxy"; + } else if (jqXHR.status == 500) { + jError("서버가 해당 요청을 이해했지만, 실행 할 수 없습니다."); + } + }) + .ajaxComplete(function (event, jqXHR, ajaxOptions) { + Ladda.stopAll(); + }) + .ajaxStop(function () { + $(".loader").addClass("hide"); + }); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//공통 AJAX SAMPLE +//////////////////////////////////////////////////////////////////////////////////////// +function ajax_sample() { + $.ajax({ + url: "요청을 보낼 URL", + type: "요청 type(GET 혹은 POST)을 명시", + data: "서버로 보내지는 데이터", + contentType: "서버로 보내지는 데이터의 content-type, 기본값은 application/x-www-form-urlencoded", + dataType: "서버 응답으로 받는 데이터 타입", + statusCode: { + 200: function (data) { + ////////////////////////////////////////////////////////// + console.log("ajax_build :: url = " + ajaxUrl); + for (var key in data) { + var value = data[key]; + console.log(key + "=" + value); + } + + var loopCount = 3; + for (var i = 0; i < loopCount; i++) { + console.log("loop check i = " + i); + } + ////////////////////////////////////////////////////////// + jSuccess("신규 제품 등록이 완료 되었습니다."); + } + }, + beforeSend: function () { + //$("#regist_pdservice").hide(); 버튼 감추기 + }, + complete: function () { + //$("#regist_pdservice").show(); 버튼 보이기 + }, + error: function (e) { + jError("신규 제품 등록 중 에러가 발생했습니다."); + } + }); +} + +//데이터 테이블 하위에 상세 리스트 보이는거 지우기 +function hideDetail_Datagrid() { + $("#hostTable") + .DataTable() + .rows() + .every(function () { + // If row has details expanded + if (this.child.isShown()) { + // Collapse row details + this.child.hide(); + $(this.node()).removeClass("shown"); + } + }); +} + +////////////////////////////////////////// +// 페이지 이동 처리 +////////////////////////////////////////// +function goToTemplatePage(pageName) { + let current_page = getPageName(this.location.search); + console.log("[common :: goToTemplatePage] :: current_page => " + current_page); + if(current_page === "searchEngine") { + console.log("[common :: goToTemplatePage] :: 검색페이지"); + } else { + console.log("[common :: goToTemplatePage] :: pageName => " + pageName); + window.location.href = "template.html?page=" + pageName; + } +} + +////////////////////////////////////////// +// 검색어 포함 페이지 이동 처리 +////////////////////////////////////////// +function goToTemplatePageWithSearchString(pageName, searchString) { + console.log("[common :: goToTemplatePageWithSearchString] :: pageName => " + pageName+", 검색어 => " + searchString); + let current_page = getPageName(this.location.search); + + if(current_page === "searchEngine") { + console.log("[common :: goToTemplatePageWithSearchString] :: current_page => " + current_page); + console.log("[common :: goToTemplatePageWithSearchString] :: 검색페이지"); + $("#search-input").val(searchString); + $("#search-button").click(); + } else { + console.log("[common :: goToTemplatePageWithSearchString] :: 검색어와 함께 페이지 이동"); + window.location.href = "/arms/template.html?page=" + pageName+"&searchString="+searchString; + } +} + +function laddaBtnSetting(라따적용_클래스이름_배열) { + for (i = 0; i < 라따적용_클래스이름_배열.length; i++) { + // add css + $(라따적용_클래스이름_배열[i]).addClass("ladda-button"); + + // Bind progress buttons and simulate loading progress + Ladda.bind(라따적용_클래스이름_배열[i], { + callback: function (instance) { + var progress = 0; + var interval = setInterval(function () { + progress = Math.min(progress + 0.1, 1); + instance.setProgress(progress); + + if (progress === 1) { + instance.stop(); + clearInterval(interval); + } + }, 250); + } + }); + } +} + +class UrlBuilder { + constructor() { + this.baseUrl = ""; + this.queryParams = {}; + } + + setBaseUrl(url) { + this.baseUrl = url; + return this; + } + + addQueryParam(key, value) { + this.queryParams[key] = value; + return this; + } + + build() { + const queryString = new URLSearchParams(this.queryParams).toString(); + return `${this.baseUrl}?${queryString}`; + } +} + +function getCookie(cname) { + var name = cname + "="; + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(";"); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == " ") { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} + +///////////////////////////////// +//투어가이드모드 쿠키 - 가져오기 +///////////////////////////////// +function getTourGuideMode() { + let tourGuideMode = getCookie("tourGuideMode"); + if (tourGuideMode) { + return tourGuideMode; + } else { + //쿠키가 없을 때, "on"으로 체크 + setTourGuideMode("on"); + return getCookie("tourGuideMode"); + } +} + +///////////////////////////////// +//투어가이드 쿠키 저장/수정 +///////////////////////////////// +function setTourGuideMode(mode) { + console.log("[ common :: setTourGuideMode ] :: mode → " + mode); + $.cookie("tourGuideMode", mode, { expires: 7 }); + return mode; +} + +function tourGuideStart() { + let pageName = getPageName(this.location.search); + console.log("[ common :: tourGuideStart ] :: pageName → " + pageName); + let tg = TourGuideApi.makeInstance(pageName); + tg.start(); +} + +function checkTourGuideMode() { + let tourGuideMode = getTourGuideMode(); + + if (!checkMobileDevice()) { + if (tourGuideMode === "off") { + console.log("common :: checkTourGuideMode : tourGuideMode is Off"); + } else { + //mode 가 없거나 on 일때 + console.log("common :: checkTourGuideMode : tourGuideMode is On"); + setTourGuideMode("on"); + setTimeout(function () { + tourGuideStart(); + }, 1200); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//톱니바퀴(config) 투어가이드 이벤트리스너 +//////////////////////////////////////////////////////////////////////////////////////// +function tourGuideEventListener() { + console.log("common :: tourGuideEventListener : tourGuideEventListener 시작"); + let tgm = getCookie("tourGuideMode"); + if (tgm === "off") { + $("#tourGuideButtons #tour_guide_off").addClass("active"); + } else { + $("#tourGuideButtons #tour_guide_on").addClass("active"); + } + + $("#tourGuideButtons button").click(function () { + let $active_label = $(this).text().toLowerCase(); + console.log($active_label); + if ($active_label === "on") { + setTourGuideMode("on"); + } else { + setTourGuideMode("off"); + } + }); +} + +function getPageName() { + const queryString = location.search; + const params = new URLSearchParams(queryString); + const page = params.get("page"); + + if (!isEmpty(page)) { // page param의 위치 + return page; + } + return "페이지 이름 없음"; //페이지 이름이 없을 경우. +} + +//////////////////////////////////////////////////////////////////////// +// 모바일 디바이스인지 확인 +//////////////////////////////////////////////////////////////////////// +function checkMobileDevice() { + // 모바일 기기에서 접근했는지 확인 + const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + console.log("[ common :: checkMobileDevice] isMobile → " + isMobile); + return isMobile; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 헤더 :: 톱니바퀴 대응함수 +//////////////////////////////////////////////////////////////////////////////////////// +function 톱니바퀴_초기설정() { + //settings + var $settings = $("#settings"), + $sidebarSettings = $("#sidebar-settings"), + settingsState = { + sidebar: "left", + sidebarState: "auto", + displaySidebar: true + }, + $pageHeader = $(".page-header"), + $body = $("body"), + popoverReallyHide = function () { + $settings.data("bs.popover").hoverState = "out"; //yeah. cool BS3 fix. popover programmatic APi works only on HOVER + $settings.popover("hide"); + }, + popoverClose = function (e) { + var $popover = $settings.siblings(".popover"); + if ($popover.length && !$.contains($popover[0], e.target)) { + popoverReallyHide(); + $(document).off("click", popoverClose); + } + }, + sidebarSide = function (side) { + if (side == "right") { + $body.addClass("sidebar-on-right"); + } else { + $body.removeClass("sidebar-on-right"); + } + }, + sidebarState = function (state, triggerResize) { + var $template = $("#sidebar-settings-template"); + triggerResize = triggerResize == undefined ? true : false; + if (!$template[0]) { + return; + } + $sidebarSettings.html(_.template($template.html())({ sidebarState: state })); + if (state == "auto") { + $(".sidebar, .side-nav, .wrap, .logo").removeClass("sidebar-icons"); + } else { + $(".sidebar, .side-nav, .wrap, .logo").addClass("sidebar-icons"); + } + if (triggerResize) { + triggerChartsResize(); + } + }, + displaySidebar = function (display, triggerResize) { + triggerResize = triggerResize == undefined ? true : false; + if (display == true) { + $body.removeClass("sidebar-hidden"); + } else { + $body.addClass("sidebar-hidden"); + } + if (triggerResize) { + triggerChartsResize(); + } + }; + + sidebarSide(settingsState.sidebar); + sidebarState(settingsState.sidebarState, false); + displaySidebar(settingsState.displaySidebar, false); + + if (!$settings[0]) { + return; + } + + $settings + .popover({ + template: + '
' + + '
' + + '
' + + '
' + + "
" + + "
", + html: true, + animation: false, + placement: "bottom", + content: function () { + if (typeof _ === "undefined") { + return "미지원"; + } + return _.template($("#settings-template").html())(settingsState); + } + }) + .click(function (e) { + //close all open dropdowns + $(".page-header .dropdown.open .dropdown-toggle").dropdown("toggle"); + // need to remove popover on anywhere-click + $(document).on("click", popoverClose); + $(this).focus(); + tourGuideEventListener(); // 투어가이드 이벤트리스너 설정. + return false; + }); + + $(".page-header .dropdown-toggle").click(function () { + popoverReallyHide(); + $(document).off("click", popoverClose); + }); + //sidevar left/right + $pageHeader.on("click", ".popover #sidebar-toggle .btn", function () { + var $this = $(this), + side = $this.data("value"); + sidebarSide(side); + settingsState.sidebar = side; + localStorage.setItem("settings-state", JSON.stringify(settingsState)); + }); + + //sidebar visibility + $pageHeader.on("click", ".popover #display-sidebar-toggle .btn", function () { + var $this = $(this), + display = $this.data("value"); + displaySidebar(display); + settingsState.displaySidebar = display; + localStorage.setItem("settings-state", JSON.stringify(settingsState)); + }); + + //sidebar state {active, icons} + $sidebarSettings.on("click", ".btn", function () { + var $this = $(this), + state = $this.data("value"); + if (state == "icons") { + closeNavigation(); + } + sidebarState(state); + settingsState.sidebarState = state; + localStorage.setItem("settings-state", JSON.stringify(settingsState)); + }); + + //close navigation if sidebar in icons state + if (($("#sidebar").is(".sidebar-icons") || $(window).width() < 1049) && $(window).width() > 767) { + closeNavigation(); + } + + //imitate buttons radio behavior + $pageHeader.on("click", ".popover [data-toggle='buttons-radio'] .btn:not(.active)", function () { + var $this = $(this), + $buttons = $this.parent().find(".btn"); + $buttons.removeClass("active"); + setTimeout(function () { + $this.addClass("active"); + }, 0); + }); +} + +/** + * A global object containing theme specific colors, screen variables & color functions. + * @type Object + */ +window.LightBlue = { + screens: { + "xs-max": 767, + "sm-min": 768, + "sm-max": 991, + "md-min": 992, + "md-max": 1199, + "lg-min": 1200 + }, + + isScreen: function (size) { + var screenPx = window.innerWidth; + return ( + (screenPx >= this.screens[size + "-min"] || size == "xs") && + (screenPx <= this.screens[size + "-max"] || size == "lg") + ); + }, + + getScreenSize: function () { + var screenPx = window.innerWidth; + if (screenPx <= this.screens["xs-max"]) return "xs"; + if (screenPx >= this.screens["sm-min"] && screenPx <= this.screens["sm-max"]) return "sm"; + if (screenPx >= this.screens["md-min"] && screenPx <= this.screens["md-max"]) return "md"; + if (screenPx >= this.screens["lg-min"]) return "lg"; + }, + + //credit http://stackoverflow.com/questions/1507931/generate-lighter-darker-color-in-css-using-javascript + changeColor: function (color, ratio, darker) { + var pad = function (num, totalChars) { + var pad = "0"; + num = num + ""; + while (num.length < totalChars) { + num = pad + num; + } + return num; + }; + // Trim trailing/leading whitespace + color = color.replace(/^\s*|\s*$/, ""); + + // Expand three-digit hex + color = color.replace(/^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i, "#$1$1$2$2$3$3"); + + // Calculate ratio + var difference = Math.round(ratio * 256) * (darker ? -1 : 1), + // Determine if input is RGB(A) + rgb = color.match( + new RegExp( + "^rgba?\\(\\s*" + + "(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])" + + "\\s*,\\s*" + + "(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])" + + "\\s*,\\s*" + + "(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])" + + "(?:\\s*,\\s*" + + "(0|1|0?\\.\\d+))?" + + "\\s*\\)$", + "i" + ) + ), + alpha = !!rgb && rgb[4] != null ? rgb[4] : null, + // Convert hex to decimal + decimal = !!rgb + ? [rgb[1], rgb[2], rgb[3]] + : color + .replace(/^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i, function () { + return parseInt(arguments[1], 16) + "," + parseInt(arguments[2], 16) + "," + parseInt(arguments[3], 16); + }) + .split(/,/), + returnValue; + + // Return RGB(A) + return !!rgb + ? "rgb" + + (alpha !== null ? "a" : "") + + "(" + + Math[darker ? "max" : "min"](parseInt(decimal[0], 10) + difference, darker ? 0 : 255) + + ", " + + Math[darker ? "max" : "min"](parseInt(decimal[1], 10) + difference, darker ? 0 : 255) + + ", " + + Math[darker ? "max" : "min"](parseInt(decimal[2], 10) + difference, darker ? 0 : 255) + + (alpha !== null ? ", " + alpha : "") + + ")" + : // Return hex + [ + "#", + pad(Math[darker ? "max" : "min"](parseInt(decimal[0], 10) + difference, darker ? 0 : 255).toString(16), 2), + pad(Math[darker ? "max" : "min"](parseInt(decimal[1], 10) + difference, darker ? 0 : 255).toString(16), 2), + pad(Math[darker ? "max" : "min"](parseInt(decimal[2], 10) + difference, darker ? 0 : 255).toString(16), 2) + ].join(""); + }, + lighten: function (color, ratio) { + return this.changeColor(color, ratio, false); + }, + darken: function (color, ratio) { + return this.changeColor(color, ratio, true); + } +}; +function triggerChartsResize() { + try { + if (window.onresize) { + window.onresize(); + } + } catch (e) { + //just swallow it + } + $(window).trigger("resize"); +} + +function targetLink(path) { + const params = {}; + + window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, (str, key, value) => { + if (!isEmpty(path) && key === "page") { + params[key] = path; + } else { + params[key] = value; + } + }); + params["mode"] = "detail"; + + location.href = `/arms/detail.html?${new URLSearchParams(params).toString()}`; +} + +function gnuboardLink(bo_table) { + const params = {}; + + window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, (str, key, value) => { + if (!isEmpty(bo_table) && key === "page") { + params[key] = "index"; + } else { + if (key == "wr_id") { + console.log("skip"); + } else { + params[key] = value; + } + } + }); + + delete params["wr_id"]; + params["bo_table"] = bo_table; + params["mode"] = "detail"; + + location.href = `/php/gnuboard5/bbs/board.php?${new URLSearchParams(params).toString()}`; +} + +function gnuboardList(param) { + const params = {}; + var userMode = false; + window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, (str, key, value) => { + if (key == "mode") { + if (value == "detail") { + userMode = true; + } + } + params[key] = value; + }); + + if (userMode) { + params["mode"] = "detail"; + location.href = param + `&${new URLSearchParams(params).toString()}`; + } else { + location.href = param; + } +} + +function gnuboardIndex() { + const params = {}; + + window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, (str, key, value) => { + if (key == "wr_id") { + console.log("skip"); + } else { + params[key] = value; + } + }); + params["page"] = "index"; + params["mode"] = "detail"; + + location.href = `/php/gnuboard5/index.php?${new URLSearchParams(params).toString()}`; +} +//////////////////////////////////////////////////////////////////////////////////////// +// 국제화 설정 +//////////////////////////////////////////////////////////////////////////////////////// +function loadLocale() { + const locale = getCookie("locale"); + changeLocale(locale || "ko"); +} + +function changeLocale(locale) { + const allowedLocale = ["ko", "jp", "en"]; + const selectedLocale = allowedLocale.includes(locale) ? locale : "ko"; + const $localeMenu = $(".locale-menu"); + $localeMenu.find(".locale-item").removeClass("active"); + $localeMenu.find(".locale-selector[data-key=" + selectedLocale + "]").closest(".locale-item").addClass("active"); + setCookie("locale", selectedLocale, 365); + setLocale(selectedLocale); +} + +function setLocale(locale = "ko") { + $.ajax({ + url: `/arms/locales/${locale}.json`, + async: false, + dataType: "json" + }).done(function (data) { + bindLocaleText(flattenObject(data)); + }); +} + +function bindLocaleText(locales) { + const targets = document.querySelectorAll("[data-locale]"); + + targets.forEach((tag) => { + const content = locales[tag.dataset.locale]; + if (content === undefined) { + console.warn("해당 키에 대한 국제화 문자열이 없습니다.", tag.dataset.locale); + return; + } + if (isIncludeHTMLTag(content)) { + tag.innerHTML = sanitizeHTML(content); + } else if (isPlaceholder(tag)) { + tag.placeholder = content; + } else { + tag.textContent = content; + } + }); +} + +function isPlaceholder(tag) { + return tag.placeholder !== undefined; +} + +function isIncludeHTMLTag(content) { + return /<\/?[a-z][\s\S]*>/i.test(content); +} + +function sanitizeHTML(content) { + const allowedTags = ['span', 'small', 'strong', 'p', 'b', 'ul', 'li', 'br']; + return content.replace(/<(\w+)[^>]*>|<\/(\w+)>/g, (match, openTag, closeTag) => { + const tagName = (openTag || closeTag).toLowerCase(); + if (allowedTags.includes(tagName)) { + return match.replace(/ on\w+="[^"]*"| on\w+='[^']*'/g, ''); // 이벤트 핸들러 속성 제거 + } + return ''; // 허용되지 않은 태그 제거 + }); +} + +function flattenObject(obj, parentKey) { + let result = {}; + + Object.entries(obj).forEach(([key, value]) => { + const _key = parentKey ? parentKey + "." + key : key; + if (typeof value === "object") { + result = { ...result, ...flattenObject(value, _key) }; + } else { + result[_key] = value; + } + }); + + return result; +} + +///////////////////////////////////// +// 쿠키 설정 +///////////////////////////////////// +function setCookie(name, value, exp = 1) { + const path = "/arms"; + const date = new Date(); + date.setTime(date.getTime() + exp * 24 * 60 * 60 * 1000); + document.cookie = `${name}=${value};expires=${date.toUTCString()};path=${path}`; +} + +///////////////////////////////////// +// 검색_이벤트_트리거 +///////////////////////////////////// +function 검색_이벤트_트리거() { + $("#nav-search-input").on("focus", function(event) { + $("#nav-search-button").addClass("highlight"); + }); + + $("#nav-search-input").on("blur", function(event) { + $("#nav-search-button").removeClass("highlight"); + }); + // nav 검색창 + $("#search_form").on("submit", function (event) { + event.preventDefault(); + + let 검색어 = $("#nav-search-input").val().trim(); + if (검색어) { + console.log("[page-header :: nav-search-start] :: 검색어 입력 값 => " + 검색어); + setParameter("searchString", 검색어); + goToTemplatePageWithSearchString("searchEngine", 검색어); + } else { + console.log("[page-header :: nav-search-start] :: 검색어가 없습니다. 검색페이지로 이동합니다"); + goToTemplatePage("searchEngine"); + } + }); + + $("#nav-search-button").on("click", function (event) { + $("#search_form").trigger("submit"); + }); +} + +///////////////////////////////////////////// +// URL 파라미터값 찾기 +///////////////////////////////////////////// +function getParameter(param) { + param = param.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + let regex = new RegExp("[\\?&]" + param + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +///////////////////////////////////////////// +// URL 파라미터값 수정 +///////////////////////////////////////////// +function setParameter(param, value) { + var url = new URL(window.location.href); + url.searchParams.set(param, value); + // Replace the currnt URL without reloading the page + window.history.pushState({path:url.href}, '', url.href); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 날짜 확인 및 검증 +//////////////////////////////////////////////////////////////////////////////////////// +function timestampToDatepicker(timestamp) { + var d = new Date(timestamp), // Convert the passed timestamp to milliseconds + yyyy = d.getFullYear(), + mm = ("0" + (d.getMonth() + 1)).slice(-2), // Months are zero based. Add leading 0. + dd = ("0" + d.getDate()).slice(-2), // Add leading 0. + hh = d.getHours(), + h = hh, + min = ("0" + d.getMinutes()).slice(-2), // Add leading 0. + ampm = "AM", + time; + if (hh > 12) { + h = hh - 12; + ampm = "PM"; + } else if (hh === 12) { + h = 12; + ampm = "PM"; + } else if (hh == 0) { + h = 12; + } + + // ie: 2013-02-18, 8:35 AM + time = yyyy + "/" + mm + "/" + dd; + + return time; +} + +function isValidShortFormat(dateString) { + // MMM dd HH:mm 형식을 확인하는 정규식 + var regex = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{2} \d{2}:\d{2}$/; + return regex.test(dateString.trim()); +} + +function isValidISOFormat(dateString) { + // ISO 8601 형식을 확인하는 정규식 + var regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}[+-]\d{2}:\d{2}$/; + return regex.test(dateString); +} + +function parseDateString(dateString) { + var date; + if (isValidShortFormat(dateString)) { + var year = new Date().getFullYear(); // 현재 연도 가져오기 + var fullTimestamp = year + " " + dateString; // 연도를 포함한 전체 시간 문자열 + date = new Date(fullTimestamp); + } else if (isValidISOFormat(dateString)) { + date = new Date(dateString); // ISO 형식 그대로 Date 객체로 변환 + } else { + console.log("[ common :: parseDateString ] :: Invalid date format: " + dateString); + return " - "; + } + return timestampToDatepicker(date); +} + +//////////////////////////////////////////////////////////// +// spreadsheet 에서 stylesheet 와 js호출울 위해 추가 +//////////////////////////////////////////////////////////// +$.getStylesheet = function (href) { + $(".spinner").empty(); + $(".spinner").html( + ' ' + + { + ko: " 스타일시트를 다운로드 중입니다...", + en: " Downloading stylesheet...", + jp: " スタイルシートをダウンロード中です..." + }[getCookie("locale") || "ko"] + ); + $("",{ + rel: "stylesheet", + type: "text/css", + href: href + }).appendTo("head"); +}; + +$.getJavascript = function (href) { + $(".spinner").empty(); + $(".spinner").html( + ' ' + + { + ko: " 자바스크립트 라이브러리를 다운로드 중입니다...", + en: " Downloading the JavaScript library...", + jp: " JavaScriptライブラリをダウンロード中です..." + }[getCookie("locale") || "ko"] + ); +} Index: arms/js/reqStatus.js =================================================================== diff -u -r4d7f2730d4e62a8a954a0c68e830499e0d05c42c -rbdfa1e19b5c80752c583ed09eed066f990162c24 --- arms/js/reqStatus.js (.../reqStatus.js) (revision 4d7f2730d4e62a8a954a0c68e830499e0d05c42c) +++ arms/js/reqStatus.js (.../reqStatus.js) (revision bdfa1e19b5c80752c583ed09eed066f990162c24) @@ -355,7 +355,7 @@ var endPointUrl = "/T_ARMS_REQSTATUS_" + $("#selected_pdService").val() + "/requirement-linkedissue.do?version="+selectedVersionId; // 이슈리스트 데이터테이블 - dataTableLoad($("#selected_pdService").val(), endPointUrl); + setReqStatusTable(endPointUrl); $("#deleted_issue_report_modal").on("shown.bs.modal", function(event) { endPointUrl = "/T_ARMS_REQSTATUS_" + $("#selected_pdService").val() + "/deletedIssueList.do?version="+selectedVersionId; @@ -370,20 +370,311 @@ }); } +function setReqStatusTable(endPointUrl) { + $.ajax({ + url: "/auth-user/api/arms/reqStatus" + endPointUrl, + type: "GET", + contentType: "application/json;charset=UTF-8", + dataType: "json", + progress: true, + statusCode: { + 200: function (apiResponse) { + + let data = apiResponse.body; + let tableData = processData(data); + dataTableLoad("#reqstatustable", tableData); + } + } + }); +} + +function processData(data) { + + const nodes = {}; + data.forEach(item => { + nodes[item.key] = { ...item, children: [] }; + }); + + // 트리 구성 + data.forEach(item => { + if (!item.isReq && item.parentReqKey !== item.upperKey) { + // 상위 항목의 children에 추가 + let parent = nodes[item.upperKey]; + while (parent && parent.upperKey !== parent.parentReqKey) { + parent = nodes[parent.upperKey]; + } + if (parent) { + parent.children.push(nodes[item.key]); + } + } + }); + + // 상위 항목만 필터링 + return data.filter(item => item.isReq || (item.isReq === false && item.parentReqKey === item.upperKey)).map(item => nodes[item.key]); +} + +function format(d) { + return '
요구사항 구분ALM Issue KeyVersionALM Issue TitleALM projectALM Issue TypeALM AssigneeALM PriorityALM StatusALM CreatedALM UpdatedALM Resolution
'; +} + +function initializeChildTable(childrenData, container) { + var columnList = [ + { + name: "isReq", + title: "요구사항 구분", + data: "isReq", + render: function (data, type, row, meta) { + if (row.connectType === "subtask") { + return "
" + row.upperKey + "의 하위 이슈
"; + } else { + return "
" + row.upperKey + "의 연결 이슈
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "key", + title: "ALM Issue Key", + data: "key", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "pdServiceVersions", + title: "Version", + data: "pdServiceVersions", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + let verNameList = []; + let verHtml =``; + data.forEach(version_id => { + let versionInfo = versionListData.find(version => version["c_id"] === version_id); + if(versionInfo) { + verNameList.push(versionInfo["c_title"]); + verHtml+= versionInfo["c_title"]+`
`; + } + }); + return "
" + verHtml + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "summary", + title: "ALM Issue Title", + data: "summary", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + let displayText = data; + if (row.deleted) { + displayText = "" + data + ""; + } + return "
" + displayText + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "project.project_key", + title: "ALM project", + data: "project.project_name", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "issuetype.issuetype_name", + title: "ALM Issue Type", + data: "issuetype.issuetype_name", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "assignee.assignee_displayName", + title: "ALM Assignee", + data: "assignee.assignee_displayName", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + }, + className: "dt-body-left", + visible: true + }, + { + name: "priority.priority_name", + title: "ALM Priority", + data: "priority.priority_name", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "status.status_name", + title: "ALM Status", + data: "status.status_name", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + data + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "created", + title: "ALM Created", + data: "created", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + dateFormat(data) + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "updated", + title: "ALM Updated", + data: "updated", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + dateFormat(data) + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + }, + { + name: "resolutiondate", + title: "ALM Resolution", + data: "resolutiondate", + render: function (data, type, row, meta) { + if (isEmpty(data) || data === "false") { + return "
N/A
"; + } else { + return "
" + dateFormat(data) + "
"; + } + return data; + }, + className: "dt-body-left", + visible: true + } + ]; + + var columnDefList = [{ + "defaultContent": "
N/A
", + "targets": "_all" + }]; + var orderList = [[2, "asc"]]; + var rowsGroupList = []; + var buttonList = []; + + var childTable = container.find('table.child-table').DataTable({ + data: childrenData, + columns: columnList, + columnDefs: columnDefList, + order: orderList, + rowsGroup: rowsGroupList, + buttons: buttonList, + paging: false, + searching: false, + info: false, + responsive: true, + autoWidth: false + }); + + container.find('tbody').on('click', 'td.details-control', function() { + var tr = $(this).closest('tr'); + var row = childTable.row(tr); + + if (row.child.isShown()) { + row.child.hide(); + tr.removeClass('shown'); + } else { + row.child(format(row.data())).show(); + tr.addClass('shown'); + initializeChildTable(row.data().children, tr.next('tr').find('div.child-table-container')); + } + }); +} + //////////////////////////////////////////////////////////////////////////////////////// //데이터 테이블 //////////////////////////////////////////////////////////////////////////////////////// // -------------------- 데이터 테이블을 만드는 템플릿으로 쓰기에 적당하게 리팩토링 함. ------------------ // -function dataTableLoad(selectId, endPointUrl) { +function dataTableLoad(table, tableData) { var columnList = [ { name: "parentReqKey", title: "부모 요구사항 키", data: "parentReqKey", visible: false }, { + className: "details-control", + orderable: false, + data: null, + title: '', + defaultContent: '', + render: function(data, type, row) { + return row.children && row.children.length > 0 ? '' : ''; + }, + visible: true + }, + { name: "isReq", title: "요구사항 구분", data: "isReq", render: function (data, type, row, meta) { if (isEmpty(data) || data == false) { - return "
" + row.parentReqKey + "의 연결 이슈
"; + return "
" + row.parentReqKey + "의 연결 이슈
"; } else { return "
" + row.key + "
"; @@ -419,7 +710,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } // data-row 에 API에 맞는 param 설정 예정. @@ -459,7 +750,7 @@ } }); if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + verHtml + "
"; + return "
" + verHtml + "
"; } else { return "
" + verHtml + "
"; } @@ -483,7 +774,7 @@ displayText = "" + data + ""; } if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + displayText + "
"; + return "
" + displayText + "
"; } return "
" + displayText + "
"; } @@ -501,7 +792,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } return "
" + data + "
"; } @@ -519,7 +810,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } return "
" + data + "
"; } @@ -537,7 +828,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } else { return "
" + data + "
"; } @@ -556,7 +847,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } return "
" + data + "
"; } @@ -574,7 +865,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } return "
" + data + "
"; } @@ -593,7 +884,7 @@ } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + dateFormat(data) + "
"; + return "
" + dateFormat(data) + "
"; } return "
" + dateFormat(data) + "
"; } @@ -611,7 +902,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + dateFormat(data) + "
"; + return "
" + dateFormat(data) + "
"; } return "
" + dateFormat(data) + "
"; } @@ -629,7 +920,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + data + "
"; + return "
" + data + "
"; } return "
" + data + "
"; } @@ -647,7 +938,7 @@ return "
N/A
"; } else { if( isEmpty(row.isReq) || row.isReq == false){ - return "
" + dateFormat(data) + "
"; + return "
" + dateFormat(data) + "
"; } return "
" + dateFormat(data) + "
"; } @@ -663,10 +954,10 @@ "defaultContent": "
N/A
", "targets": "_all" }]; - var orderList = [[1, "asc"]]; - var jquerySelector = "#reqstatustable"; - var ajaxUrl = "/auth-user/api/arms/reqStatus" + endPointUrl; - var jsonRoot = "body"; + var orderList = [[2, "asc"]]; + var jquerySelector = table; + var ajaxUrl = ""; + var jsonRoot = ""; var buttonList = [ "copy", "excel", @@ -688,6 +979,7 @@ ]; var selectList = {}; var isServerSide = false; + var isAjax = false; reqStatusDataTable = dataTable_build( jquerySelector, @@ -700,7 +992,9 @@ orderList, buttonList, isServerSide, - 700 + 700, + tableData, + isAjax ); $("#reqstatustable").on('page.dt', function() { @@ -719,6 +1013,27 @@ // 데이터 테이블 데이터 렌더링 이후 콜백 함수. function dataTableCallBack(settings, json) { console.log("check"); + + // 테이블 행 클릭 이벤트 (하위 이슈 조회) + $('#reqstatustable tbody').on('click', 'td.details-control', function() { + const tr = $(this).closest('tr'); + const row = reqStatusDataTable.row(tr); + const icon = $(this).find('i'); + if (icon.length === 0) { + return; + } + + if (row.child.isShown()) { + row.child.hide(); + tr.removeClass('shown'); + icon.removeClass('fa-angle-up').addClass('fa-angle-down'); + } else { + row.child(format(row.data())).show(); + tr.addClass('shown'); + icon.removeClass('fa-angle-down').addClass('fa-angle-up'); + initializeChildTable(row.data().children, tr.next('tr').find('div.child-table-container')); + } + }); } function dataTableDrawCallback(tableInfo) {