1775 lines
70 KiB
HTML
1775 lines
70 KiB
HTML
<!DOCTYPE html>
|
|
<html lang='ko'>
|
|
<head>
|
|
<meta charset='UTF-8'>
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
|
<title>OSVC</title>
|
|
<link rel='stylesheet' href='./css/all.min.css'>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; }
|
|
body { display: flex; flex-direction: column; height: 100vh; background: #f4f4f4; }
|
|
.top-bar {
|
|
background-color: #5e5e60;
|
|
color: white;
|
|
padding: 28px;
|
|
text-align: left;
|
|
font-size: 30px;
|
|
font-weight: bold;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex: 1;
|
|
}
|
|
.sidebar {
|
|
width: 250px;
|
|
background: #2c3e50;
|
|
color: white;
|
|
padding: 20px;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
}
|
|
.sidebar h2 { font-size: 18px; margin-bottom: 20px; }
|
|
.sidebar ul { list-style: none; padding: 0; }
|
|
.sidebar ul li {
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
transition: 0.3s;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.sidebar ul li:hover { background: #34495e; }
|
|
.sidebar ul li.active { color: #1abc9c; font-weight: bold; }
|
|
.submenu {
|
|
display: none;
|
|
padding-left: 20px;
|
|
border-left: 2px solid #1abc9c;
|
|
margin-top: 5px;
|
|
}
|
|
.submenu li {
|
|
padding: 8px;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
background: #3b4c63;
|
|
margin-top: 2px;
|
|
}
|
|
.submenu li:hover { background: #1abc9c; }
|
|
.submenu li.active { color: #ffcc00; font-weight: bold; }
|
|
.content {
|
|
flex-grow: 1;
|
|
padding: 20px;
|
|
background: white;
|
|
}
|
|
.content-header {
|
|
font-size: 24px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.content-header {
|
|
font-size: 24px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.dashboard-container {
|
|
display: flex;
|
|
gap: 20px;
|
|
}
|
|
.dashboard-section {
|
|
flex: 1;
|
|
background: #fff;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
.section-half {
|
|
width: 40vw;
|
|
}
|
|
.system-status ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
.system-status ul li {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 8px;
|
|
}
|
|
.status-indicator {
|
|
width: 15px;
|
|
height: 15px;
|
|
border-radius: 50%;
|
|
background: green;
|
|
margin-right: 10px;
|
|
}
|
|
.status-text {
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.top-button {
|
|
width: 75px;
|
|
height: 75px;
|
|
background-color: #d32f2f; /* 빨간색 */
|
|
border-radius: 20%;
|
|
display: flex;
|
|
flex-direction: column; /* 세로 정렬 */
|
|
justify-content: center;
|
|
align-items: center;
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
|
cursor: pointer;
|
|
transition: 0.3s;
|
|
border: none;
|
|
outline: none;
|
|
}
|
|
|
|
.top-button i {
|
|
font-size: 30px;
|
|
color: white;
|
|
}
|
|
|
|
.top-button-text {
|
|
font-size: 10px;
|
|
color: white;
|
|
margin-top: 8px; /* 아이콘과 간격 */
|
|
font-weight: bold;
|
|
}
|
|
|
|
.top-button:hover {
|
|
background-color: #b71c1c; /* 어두운 빨간색 */
|
|
}
|
|
|
|
.top-button-green {
|
|
width: 75px;
|
|
height: 75px;
|
|
background-color: #2fd32f; /* 초록색 */
|
|
border-radius: 20%;
|
|
display: flex;
|
|
flex-direction: column; /* 세로 정렬 */
|
|
justify-content: center;
|
|
align-items: center;
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
|
cursor: pointer;
|
|
transition: 0.3s;
|
|
border: none;
|
|
outline: none;
|
|
}
|
|
|
|
.top-button-green i {
|
|
font-size: 30px;
|
|
color: white;
|
|
}
|
|
|
|
.top-button-green:hover {
|
|
background-color: #1cb71c; /* 어두운 초록색 */
|
|
}
|
|
|
|
.top-button-normal {
|
|
width: 75px;
|
|
height: 75px;
|
|
background-color: #3c4e60;
|
|
border-radius: 20%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
|
cursor: pointer;
|
|
transition: 0.3s;
|
|
border: none;
|
|
outline: none;
|
|
}
|
|
|
|
.top-button-normal i {
|
|
font-size: 30px;
|
|
color: white;
|
|
}
|
|
|
|
.top-button-normal:hover {
|
|
background-color: #1c2e40;
|
|
}
|
|
|
|
.top-info-normal {
|
|
width: 75px;
|
|
height: 75px;
|
|
background-color: #101010;
|
|
border-radius: 5%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
|
transition: 0.3s;
|
|
border: none;
|
|
outline: none;
|
|
}
|
|
|
|
|
|
|
|
.call-timer {
|
|
color: white;
|
|
font-size: 14px;
|
|
margin-bottom: 5px;
|
|
}
|
|
.time {
|
|
color: white;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
|
|
|
|
.input-box {
|
|
margin-bottom: 15px;
|
|
display: flex;
|
|
}
|
|
.input-box input, .combo-box select {
|
|
width: 350px;
|
|
padding: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
outline: none;
|
|
background: white;
|
|
}
|
|
|
|
.input-label {
|
|
vertical-align: middle;
|
|
margin-right: 10px;
|
|
line-height: 40px;
|
|
width:80px;
|
|
}
|
|
|
|
.input-label-long {
|
|
vertical-align: middle;
|
|
margin-right: 10px;
|
|
line-height: 40px;
|
|
width:120px;
|
|
}
|
|
|
|
.prev-label {
|
|
color: #808080;
|
|
vertical-align: middle;
|
|
margin-left: 5px;
|
|
margin-right: 10px;
|
|
line-height: 40px;
|
|
width:360px;
|
|
}
|
|
|
|
.apply-button {
|
|
padding: 10px 20px;
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
transition: background 0.3s;
|
|
}
|
|
.apply-button:hover {
|
|
background-color: #45a049;
|
|
}
|
|
|
|
|
|
.table-inner-button {
|
|
padding: 5px 15px;
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
transition: background 0.3s;
|
|
}
|
|
.table-inner-button:hover {
|
|
background-color: #409039;
|
|
}
|
|
|
|
.button-green {
|
|
background-color: #00ff00;
|
|
}
|
|
|
|
.button-orange {
|
|
background-color: #ffa500;
|
|
}
|
|
.button-orange:hover {
|
|
background-color: #ef9500;
|
|
}
|
|
|
|
.button-red {
|
|
background-color: #ff0000;
|
|
}
|
|
.button-red:hover {
|
|
background-color: #ef0000;
|
|
}
|
|
|
|
|
|
.table-container {
|
|
margin-top: 20px;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.15);
|
|
padding: 20px;
|
|
border-radius: 15px;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
width: 650px;
|
|
}
|
|
table {
|
|
width: 600px;
|
|
border-collapse: collapse;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
background: white;
|
|
}
|
|
thead {
|
|
background: #f4f4f4;
|
|
color: #333;
|
|
font-weight: bold;
|
|
border-bottom: 2px solid #ddd;
|
|
}
|
|
th, td {
|
|
padding: 10px;
|
|
text-align: center;
|
|
}
|
|
tbody tr:nth-child(even) {
|
|
background: #fafafa;
|
|
}
|
|
tbody tr:hover {
|
|
background: #f0f0f0;
|
|
transition: 0.3s;
|
|
}
|
|
th {
|
|
text-transform: none;
|
|
}
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
margin-top: 30px;
|
|
gap: 5px;
|
|
}
|
|
.pagination button {
|
|
padding: 5px 10px;
|
|
border: 1px solid #ccc;
|
|
background-color: white;
|
|
cursor: pointer;
|
|
border-radius: 5px;
|
|
}
|
|
.pagination button:hover {
|
|
background-color: #ddd;
|
|
}
|
|
.pagination .active {
|
|
background-color: #ddd;
|
|
font-weight: bold;
|
|
}
|
|
.search-box {
|
|
margin-bottom: 10px;
|
|
width: 100%;
|
|
}
|
|
.search-box input {
|
|
width: 50%;
|
|
padding: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
outline: none;
|
|
}
|
|
|
|
|
|
/* Dimmed 배경 */
|
|
.modal-overlay {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
/* 모달 스타일 */
|
|
.modal {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
width: 300px;
|
|
text-align: center;
|
|
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2);
|
|
position: relative;
|
|
}
|
|
.modal h2 {
|
|
margin: 0 0 10px;
|
|
}
|
|
.modal button {
|
|
margin-top: 20px;
|
|
padding: 8px 16px;
|
|
border: none;
|
|
background: #007bff;
|
|
color: white;
|
|
cursor: pointer;
|
|
border-radius: 5px;
|
|
}
|
|
.modal button:hover {
|
|
background: #0056b3;
|
|
}
|
|
/* 로딩 스피너 스타일 */
|
|
.spinner {
|
|
margin: 20px auto;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 5px solid #ccc;
|
|
border-top: 5px solid #007bff;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.toast {
|
|
visibility: hidden;
|
|
min-width: 250px;
|
|
min-height: 70px;
|
|
background-color: #333;
|
|
color: #fff;
|
|
text-align: center;
|
|
border-radius: 5px;
|
|
padding: 20px;
|
|
position: fixed;
|
|
/*
|
|
top: 20px;
|
|
left: 90%;
|
|
*/
|
|
font-size: 20px;
|
|
bottom: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 1000;
|
|
opacity: 0;
|
|
transition: opacity 0.5s;
|
|
}
|
|
.toast.show {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
|
|
.gallery {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
.gallery img {
|
|
width: 100px;
|
|
height: 100px;
|
|
cursor: pointer;
|
|
border: 2px solid transparent;
|
|
transition: border 0.1s;
|
|
}
|
|
.gallery img.selected {
|
|
border: 4px solid blue !important;
|
|
}
|
|
#selectedImage {
|
|
margin-top: 20px;
|
|
font-weight: bold;
|
|
}
|
|
</style>
|
|
<script>
|
|
let seconds = 0;
|
|
let minutes = 0;
|
|
let hours = 0;
|
|
var g_call_status = 'standby';
|
|
var g_content_page = '';
|
|
var g_input_updated_page = '';
|
|
var callStartTime = null;
|
|
var callDuration = 0;
|
|
var timerId;
|
|
var timerReload;
|
|
|
|
var g_modal_confirm = '';
|
|
var g_confirm_callback = null;
|
|
|
|
function formatSeconds(seconds) {
|
|
const hrs = Math.floor(seconds / 3600);
|
|
const mins = Math.floor((seconds % 3600) / 60);
|
|
const secs = seconds % 60;
|
|
|
|
// 두 자리 숫자로 포맷 (padStart는 문자열 메서드)
|
|
/*
|
|
const formatted = [
|
|
hrs.toString().padStart(2, '0'),
|
|
mins.toString().padStart(2, '0'),
|
|
secs.toString().padStart(2, '0')
|
|
].join(':');
|
|
*/
|
|
|
|
const formatted =
|
|
(hrs.toString() + '시간 ' +
|
|
mins.toString() + '분 ' +
|
|
secs.toString() + '초');
|
|
|
|
return formatted;
|
|
}
|
|
|
|
function updateTimer() {
|
|
// seconds++;
|
|
// if (seconds === 60) {
|
|
// seconds = 0;
|
|
// minutes++;
|
|
// if (minutes === 60) {
|
|
// minutes = 0;
|
|
// hours++;
|
|
// }
|
|
// }
|
|
|
|
if(callStartTime != null) {
|
|
// 현재 시간 가져오기
|
|
const now = new Date();
|
|
|
|
// 밀리초 차이 계산
|
|
let diffMs = Math.abs(now - callStartTime);
|
|
// 시간, 분, 초로 변환
|
|
hours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
diffMs %= (1000 * 60 * 60);
|
|
minutes = Math.floor(diffMs / (1000 * 60));
|
|
diffMs %= (1000 * 60);
|
|
seconds = Math.floor(diffMs / 1000);
|
|
}
|
|
|
|
if(callDuration > 0) {
|
|
let diffMs = callDuration * 1000;
|
|
hours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
diffMs %= (1000 * 60 * 60);
|
|
minutes = Math.floor(diffMs / (1000 * 60));
|
|
diffMs %= (1000 * 60);
|
|
seconds = Math.floor(diffMs / 1000);
|
|
}
|
|
|
|
let formattedTime =
|
|
(hours < 10 ? '0' + hours : hours) + ':' +
|
|
(minutes < 10 ? '0' + minutes : minutes) + ':' +
|
|
(seconds < 10 ? '0' + seconds : seconds);
|
|
|
|
|
|
document.getElementById('timer').textContent = formattedTime;
|
|
|
|
fetchHTML('./sip_status', { cache: 'no-cache' }).then(html => {
|
|
//var poller = document.getElementById('status-poller');
|
|
//document.getElementById('sip-status').value = html;
|
|
var jsonString = html;
|
|
try {
|
|
// JSON 파싱
|
|
const data = JSON.parse(jsonString);
|
|
var prev_call_status = g_call_status;
|
|
g_call_status = data.call_status;
|
|
|
|
if(data.call_status == 'calling') {
|
|
if(modalOverlay.style.display == 'flex') {
|
|
modalOverlay.style.display = 'none';
|
|
}
|
|
if(data.audio_mute_status == 'mute') {
|
|
document.getElementById('area-button-mute').style.display = `none`;
|
|
document.getElementById('area-button-unmute').style.display = `flex`;
|
|
} else {
|
|
document.getElementById('area-button-mute').style.display = `flex`;
|
|
document.getElementById('area-button-unmute').style.display = `none`;
|
|
}
|
|
|
|
if(data.video_mute_status == 'mute') {
|
|
document.getElementById('area-button-video-mute').style.display = `none`;
|
|
document.getElementById('area-button-video-unmute').style.display = `flex`;
|
|
} else {
|
|
document.getElementById('area-button-video-mute').style.display = `flex`;
|
|
document.getElementById('area-button-video-unmute').style.display = `none`;
|
|
}
|
|
document.getElementById('area-call-progress').style = `visibility:visible`;
|
|
|
|
if(data.usb_mic_status != null) {
|
|
if(data.usb_mic_status == 'connected') {
|
|
if(data.audio_input_device == 'usb') {
|
|
document.getElementById('area-button-input-aux').style.display = `flex`;
|
|
document.getElementById('area-button-input-mic').style.display = `none`;
|
|
} else {
|
|
document.getElementById('area-button-input-aux').style.display = `none`;
|
|
document.getElementById('area-button-input-mic').style.display = `flex`;
|
|
}
|
|
} else {
|
|
document.getElementById('area-button-input-aux').style.display = `none`;
|
|
document.getElementById('area-button-input-mic').style.display = `none`;
|
|
}
|
|
}
|
|
|
|
if(data.far_screen_file != null) {
|
|
//console.log(data.far_screen_file);
|
|
var farImg = document.getElementById('img-far-screen');
|
|
if(farImg != null) {
|
|
farImg.src = './' + data.far_screen_file;
|
|
}
|
|
}
|
|
if(data.near_screen_file != null) {
|
|
//console.log(data.near_screen_file);
|
|
var nearImg = document.getElementById('img-near-screen');
|
|
if(nearImg != null) {
|
|
nearImg.src = './' + data.near_screen_file;
|
|
}
|
|
}
|
|
} else if(data.call_status == 'standby') {
|
|
document.getElementById('area-call-progress').style = `visibility:hidden`;
|
|
callStartTime = null;
|
|
|
|
if(data.near_screen_file != null && data.near_screen_file.length > 0) {
|
|
//console.log(data.near_screen_file);
|
|
var nearImg = document.getElementById('img-near-screen');
|
|
if(nearImg != null) {
|
|
nearImg.src = './' + data.near_screen_file;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(data.call_start_time != null && callStartTime == null) {
|
|
callStartTime = new Date(data.call_start_time);
|
|
}
|
|
|
|
if(data.call_duration != null) {
|
|
callDuration = data.call_duration;
|
|
}
|
|
|
|
if(data.call_number != null) {
|
|
document.getElementById('text-call-number').innerHTML = data.call_number;
|
|
}
|
|
|
|
if(data.net_ip_address != null) {
|
|
document.getElementById('text-ip-address').innerHTML = data.net_ip_address;
|
|
}
|
|
|
|
if(data.sip_address != null) {
|
|
document.getElementById('text-sip-address').innerHTML = data.sip_address;
|
|
}
|
|
|
|
if(data.call_event_result != null) {
|
|
if(data.call_event_result == "call_failed") {
|
|
//fetchHTML('./request_action.html?req=property_reset¶m=call_event_result');
|
|
showModalPopup("통화를 실패했습니다.");
|
|
}
|
|
}
|
|
|
|
if(g_content_page == 'dashboard') {
|
|
console.log(data);
|
|
if(data.server_register_status != null) {
|
|
if(data.server_register_status == 'registered') {
|
|
console.log('server_register_status : ' + data.server_register_status);
|
|
document.getElementById('server-register-status').className = 'status-indicator';
|
|
} else if(data.server_register_status == 'registering') {
|
|
console.log('server_register_status : ' + data.server_register_status);
|
|
document.getElementById('server-register-status').className = 'status-indicator button-orange';
|
|
} else {
|
|
document.getElementById('server-register-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.hdmi1_in_status != null) {
|
|
if(data.hdmi1_in_status == 'plugin') {
|
|
document.getElementById('hdmi1-in-status').className = 'status-indicator';
|
|
} else {
|
|
document.getElementById('hdmi1-in-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.hdmi2_in_status != null) {
|
|
if(data.hdmi2_in_status == 'plugin') {
|
|
document.getElementById('hdmi2-in-status').className = 'status-indicator';
|
|
} else {
|
|
document.getElementById('hdmi2-in-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.hdmi1_out_status != null) {
|
|
if(data.hdmi1_out_status == 'connected') {
|
|
document.getElementById('hdmi1-out-status').className = 'status-indicator';
|
|
} else {
|
|
document.getElementById('hdmi1-out-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.hdmi2_out_status != null) {
|
|
if(data.hdmi2_out_status == 'connected') {
|
|
document.getElementById('hdmi2-out-status').className = 'status-indicator';
|
|
} else {
|
|
document.getElementById('hdmi2-out-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.usb_mic_status != null) {
|
|
if(data.usb_mic_status == 'connected') {
|
|
document.getElementById('usb-mic-status').className = 'status-indicator';
|
|
} else {
|
|
document.getElementById('usb-mic-status').className = 'status-indicator button-red';
|
|
}
|
|
}
|
|
|
|
if(data.total_duration != null) {
|
|
document.getElementById('total-duration').innerHTML = formatSeconds(data.total_duration);
|
|
}
|
|
|
|
} else if(g_content_page == 'normalSetting') {
|
|
if(g_input_updated_page != g_content_page) {
|
|
g_input_updated_page = g_content_page;
|
|
document.getElementById('input-device-name').value = data.device_name;
|
|
document.getElementById('select-display-type').value = data.far_view_display_id;
|
|
}
|
|
/*
|
|
document.getElementById('prev-device-name').innerHTML = data.device_name;
|
|
if(data.far_view_display_id == '2') {
|
|
document.getElementById('prev-display-type').innerHTML = '모니터1:근거리/컨텐츠, 모니터2:원거리';
|
|
} else {
|
|
document.getElementById('prev-display-type').innerHTML = '모니터1:원거리, 모니터2:근거리/컨텐츠';
|
|
}
|
|
*/
|
|
//document.getElementById('prev-display-type').innerHTML = data.far_view_display_id;
|
|
} else if(g_content_page == 'networkSetting') {
|
|
if(g_input_updated_page != g_content_page) {
|
|
g_input_updated_page = g_content_page;
|
|
document.getElementById('select-network-type').value = data.ipv4_iptype;
|
|
onIpTypeSelectedValue(document.getElementById('select-network-type'));
|
|
document.getElementById('input-ipaddress').value = data.ipv4_address;
|
|
document.getElementById('input-netmask').value = data.ipv4_netmask;
|
|
document.getElementById('input-gateway').value = data.ipv4_gateway;
|
|
document.getElementById('input-dns').value = data.ipv4_dns;
|
|
}
|
|
|
|
/*
|
|
document.getElementById('prev-network-type').innerHTML = data.ipv4_iptype;
|
|
document.getElementById('prev-ipv4-address').innerHTML = data.ipv4_address;
|
|
document.getElementById('prev-ipv4-netmask').innerHTML = data.ipv4_netmask;
|
|
document.getElementById('prev-ipv4-gateway').innerHTML = data.ipv4_gateway;
|
|
document.getElementById('prev-ipv4-dns').innerHTML = data.ipv4_dns;
|
|
*/
|
|
} else if(g_content_page == 'serverSetting') {
|
|
if(g_input_updated_page != g_content_page) {
|
|
g_input_updated_page = g_content_page;
|
|
document.getElementById('input-display-name').value = data.display_name;
|
|
document.getElementById('input-account-name').value = data.account_name;
|
|
document.getElementById('select-media-encryption').value = data.media_encryption == 'SRTP' ? 'srtpm' : 'none';
|
|
document.getElementById('select-transport').value = data.prefer_transport;
|
|
document.getElementById('input-server-address').value = data.server_address;
|
|
}
|
|
|
|
/*
|
|
document.getElementById('prev-display-name').innerHTML = data.display_name;
|
|
document.getElementById('prev-account-name').innerHTML = data.account_name;
|
|
document.getElementById('prev-media-encryption').innerHTML = data.media_encryption;
|
|
document.getElementById('prev-prefer-transport').innerHTML = data.prefer_transport;
|
|
document.getElementById('prev-server-address').innerHTML = data.server_address;
|
|
*/
|
|
} else if(g_content_page == 'layout') {
|
|
if(g_input_updated_page != g_content_page) {
|
|
g_input_updated_page = g_content_page;
|
|
let layout_id = 'layout' + data.display_split_mode;
|
|
document.getElementById(layout_id).click();
|
|
}
|
|
document.getElementById('prev-layout').innerHTML = "적용중인 레이아웃: " + layoutAlt[data.display_split_mode - 1];
|
|
}
|
|
|
|
if(prev_call_status != g_call_status) {
|
|
if(g_content_page == 'monitoring') {
|
|
selectMenu('menu-monitoring');
|
|
} else if(g_content_page == 'callStatistics') {
|
|
selectMenu('menu-statistics');
|
|
}
|
|
|
|
if(g_call_status == "standby") {
|
|
console.log("통화가 종료되었습니다.");
|
|
showModalPopup("통화가 종료되었습니다.");
|
|
}
|
|
|
|
triggerUpdate();
|
|
if(g_content_page == "callHistory") {
|
|
setTimeout(() => {if(g_content_page == "callHistory") { callHistoryFromUrl(document.getElementById('menu-history')) }}, 200);
|
|
} else if(g_content_page == "callAddressBook") {
|
|
setTimeout(() => {if(g_content_page == "callAddressBook") { callAddressBookFromUrl(document.getElementById('menu-address')) }}, 200);
|
|
}
|
|
}
|
|
|
|
// 결과 표시
|
|
console.log(data.call_status);
|
|
} catch (error) {
|
|
console.log(error);
|
|
//alert("올바른 JSON 형식이 아닙니다!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function triggerUpdate() {
|
|
fetchHTML('./trigger_update');
|
|
}
|
|
|
|
function showModalProgress() {
|
|
let modalOverlay = document.getElementById('modalOverlay');
|
|
let modalSpinner = document.getElementById('modalSpinner');
|
|
let modalMsg = document.getElementById('modalMsg');
|
|
|
|
modalMsg.innerHTML = '진행 중...';
|
|
modalSpinner.style.display = 'flex';
|
|
modalOverlay.style.display = 'flex';
|
|
}
|
|
|
|
function showModalPopup(msg) {
|
|
let modalOverlay = document.getElementById('modalOverlay');
|
|
let modalSpinner = document.getElementById('modalSpinner');
|
|
let modalMsg = document.getElementById('modalMsg');
|
|
|
|
modalMsg.innerHTML = msg;
|
|
modalSpinner.style.display = 'none';
|
|
modalOverlay.style.display = 'flex';
|
|
}
|
|
|
|
function showConfirmPopup(msg) {
|
|
let confirmOverlay = document.getElementById('confirmOverlay');
|
|
let confirmMsg = document.getElementById('confirmMsg');
|
|
|
|
confirmMsg.innerHTML = msg;
|
|
confirmOverlay.style.display = 'flex';
|
|
}
|
|
|
|
timerId = setInterval(updateTimer, 1000);
|
|
function startCallTimer() {
|
|
hours = 0;
|
|
minutes = 0;
|
|
seconds = 0;
|
|
updateTimer();
|
|
|
|
//let modalOverlay = document.getElementById('modalOverlay');
|
|
//modalOverlay.style.display = 'flex';
|
|
showModalProgress();
|
|
}
|
|
|
|
// *************************
|
|
// 테이블 관련 함수 영역
|
|
// *************************
|
|
function setupPagination(tableContainer) {
|
|
let currentPage = 1;
|
|
const rowsPerPage = 10;
|
|
const maxPageButtons = 5; // 최대 페이지 번호 개수
|
|
const table = tableContainer.querySelector('table');
|
|
const tbody = table.querySelector('tbody');
|
|
const rows = tbody.querySelectorAll('tr');
|
|
const paginationContainer = tableContainer.querySelector('.pagination');
|
|
const searchInput = tableContainer.querySelector(".search-box input");
|
|
|
|
function displayTableRows() {
|
|
if(searchInput) {
|
|
const searchText = searchInput.value.toLowerCase();
|
|
const filteredRows = Array.from(rows).filter(row =>
|
|
row.textContent.toLowerCase().includes(searchText)
|
|
);
|
|
const totalPages = Math.ceil(filteredRows.length / rowsPerPage);
|
|
|
|
rows.forEach(row => row.style.display = "none");
|
|
filteredRows.forEach((row, index) => {
|
|
row.style.display = (index >= (currentPage - 1) * rowsPerPage && index < currentPage * rowsPerPage) ? "table-row" : "none";
|
|
});
|
|
updatePaginationSearch(totalPages, filteredRows);
|
|
|
|
} else {
|
|
const totalPages = Math.ceil(rows.length / rowsPerPage);
|
|
rows.forEach((row, index) => {
|
|
row.style.display = (index >= (currentPage - 1) * rowsPerPage && index < currentPage * rowsPerPage) ? 'table-row' : 'none';
|
|
});
|
|
updatePagination(totalPages);
|
|
}
|
|
}
|
|
|
|
function changePageSearch(newPage, filteredRows) {
|
|
const totalPages = Math.ceil(filteredRows.length / rowsPerPage);
|
|
currentPage = Math.max(1, Math.min(newPage, totalPages));
|
|
displayTableRows();
|
|
}
|
|
|
|
function updatePaginationSearch(totalPages, filteredRows) {
|
|
paginationContainer.innerHTML = "";
|
|
let startPage = Math.max(1, currentPage - Math.floor(maxPageButtons / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
|
|
if (endPage - startPage + 1 < maxPageButtons) {
|
|
startPage = Math.max(1, endPage - maxPageButtons + 1);
|
|
}
|
|
|
|
let prevButton = document.createElement("button");
|
|
prevButton.innerText = "이전";
|
|
prevButton.disabled = currentPage === 1;
|
|
prevButton.onclick = () => changePageSearch(currentPage - 1, filteredRows);
|
|
paginationContainer.appendChild(prevButton);
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
let pageButton = document.createElement("button");
|
|
pageButton.innerText = i;
|
|
if (i === currentPage) {
|
|
pageButton.classList.add("active");
|
|
}
|
|
pageButton.onclick = () => changePageSearch(i, filteredRows);
|
|
paginationContainer.appendChild(pageButton);
|
|
}
|
|
|
|
let nextButton = document.createElement("button");
|
|
nextButton.innerText = "다음";
|
|
nextButton.disabled = currentPage === totalPages;
|
|
nextButton.onclick = () => changePageSearch(currentPage + 1, filteredRows);
|
|
paginationContainer.appendChild(nextButton);
|
|
}
|
|
|
|
|
|
function changePage(newPage) {
|
|
const totalPages = Math.ceil(rows.length / rowsPerPage);
|
|
currentPage = Math.max(1, Math.min(newPage, totalPages));
|
|
displayTableRows();
|
|
}
|
|
|
|
function updatePagination(totalPages) {
|
|
paginationContainer.innerHTML = "";
|
|
let startPage = Math.max(1, currentPage - Math.floor(maxPageButtons / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
|
|
if (endPage - startPage + 1 < maxPageButtons) {
|
|
startPage = Math.max(1, endPage - maxPageButtons + 1);
|
|
}
|
|
|
|
let prevButton = document.createElement("button");
|
|
prevButton.innerText = "이전";
|
|
prevButton.disabled = currentPage === 1;
|
|
prevButton.onclick = () => changePage(currentPage - 1);
|
|
paginationContainer.appendChild(prevButton);
|
|
|
|
if (startPage > 1) {
|
|
let firstPage = document.createElement("button");
|
|
firstPage.innerText = "1";
|
|
firstPage.onclick = () => changePage(1);
|
|
paginationContainer.appendChild(firstPage);
|
|
if (startPage > 2) {
|
|
let dots = document.createElement("span");
|
|
dots.innerText = "...";
|
|
paginationContainer.appendChild(dots);
|
|
}
|
|
}
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
let pageButton = document.createElement("button");
|
|
pageButton.innerText = i;
|
|
if (i === currentPage) {
|
|
pageButton.classList.add("active");
|
|
}
|
|
pageButton.onclick = () => changePage(i);
|
|
paginationContainer.appendChild(pageButton);
|
|
}
|
|
|
|
if (endPage < totalPages) {
|
|
if (endPage < totalPages - 1) {
|
|
let dots = document.createElement("span");
|
|
dots.innerText = "...";
|
|
paginationContainer.appendChild(dots);
|
|
}
|
|
let lastPage = document.createElement("button");
|
|
lastPage.innerText = totalPages;
|
|
lastPage.onclick = () => changePage(totalPages);
|
|
paginationContainer.appendChild(lastPage);
|
|
}
|
|
|
|
let nextButton = document.createElement("button");
|
|
nextButton.innerText = "다음";
|
|
nextButton.disabled = currentPage === totalPages;
|
|
nextButton.onclick = () => changePage(currentPage + 1);
|
|
paginationContainer.appendChild(nextButton);
|
|
}
|
|
|
|
if(searchInput) {
|
|
searchInput.addEventListener("input", displayTableRows);
|
|
}
|
|
|
|
displayTableRows();
|
|
}
|
|
|
|
/*
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.querySelectorAll('.table-container').forEach(setupPagination);
|
|
});
|
|
*/
|
|
|
|
function updateTablePager() {
|
|
document.querySelectorAll('.table-container').forEach(setupPagination);
|
|
}
|
|
|
|
async function fetchHTML(url) {
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
}
|
|
const html = await response.text();
|
|
return html;
|
|
} catch (error) {
|
|
console.error('Error fetching HTML:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
// *************************
|
|
// 좌측 메뉴 관련 함수 영역
|
|
// *************************
|
|
function toggleSubMenu(id) {
|
|
let submenu = document.getElementById(id);
|
|
let icon = document.getElementById(id + '-icon');
|
|
if (submenu) {
|
|
if (submenu.style.display === 'block') {
|
|
submenu.style.display = 'none';
|
|
icon?.classList.remove('fa-chevron-down');
|
|
icon?.classList.add('fa-chevron-right');
|
|
} else {
|
|
submenu.style.display = 'block';
|
|
icon?.classList.remove('fa-chevron-right');
|
|
icon?.classList.add('fa-chevron-down');
|
|
}
|
|
}
|
|
}
|
|
function openSubMenu(id) {
|
|
let submenu = document.getElementById(id);
|
|
let icon = document.getElementById(id + '-icon');
|
|
if (submenu) {
|
|
if (submenu.style.display !== 'block') {
|
|
submenu.style.display = 'block';
|
|
icon?.classList.remove('fa-chevron-right');
|
|
icon?.classList.add('fa-chevron-down');
|
|
}
|
|
}
|
|
}
|
|
function loadContent(content, element) {
|
|
document.getElementById('content-area').innerHTML = content;
|
|
document.querySelectorAll('.sidebar ul li, .submenu li').forEach(item => item.classList.remove('active'));
|
|
element.classList.add('active');
|
|
}
|
|
function changeContentHeader(headerInfo) {
|
|
document.getElementById('content-header').innerHTML = headerInfo;
|
|
}
|
|
|
|
function requestMute() {
|
|
fetchHTML('./request_action.html?req=audio_mute');
|
|
setTimeout(updateTimer, 100);
|
|
}
|
|
|
|
function requestUnMute() {
|
|
fetchHTML('./request_action.html?req=audio_unmute');
|
|
setTimeout(updateTimer, 100);
|
|
}
|
|
|
|
function requestVideoMute() {
|
|
fetchHTML('./request_action.html?req=video_mute');
|
|
setTimeout(updateTimer, 100);
|
|
}
|
|
|
|
function requestVideoUnMute() {
|
|
fetchHTML('./request_action.html?req=video_unmute');
|
|
setTimeout(updateTimer, 100);
|
|
}
|
|
|
|
function callStop() {
|
|
fetchHTML('./request_action.html?req=stop_call');
|
|
}
|
|
|
|
function setAudioSource(target) {
|
|
fetchHTML('./request_action.html?req=audio_source&target=' + target);
|
|
}
|
|
|
|
function callStart() {
|
|
var callNumber = document.getElementById('input-call-number').value;
|
|
if(callNumber.length > 0) {
|
|
callStartWithNumber(callNumber);
|
|
}
|
|
}
|
|
|
|
function saveAddressBook() {
|
|
var uri = document.getElementById('input-uri').value;
|
|
var name = document.getElementById('input-name').value;
|
|
var paramStr = '&uri=' + uri;
|
|
paramStr += '&name=' + name;
|
|
fetchHTML('./request_action.html?req=save_address&'+paramStr);
|
|
triggerUpdate();
|
|
setTimeout(() => {if(g_content_page == "callAddressBook") { callAddressBookFromUrl(document.getElementById('menu-address')) }}, 200);
|
|
}
|
|
|
|
function removeContact(peerUri) {
|
|
fetchHTML('./request_action.html?req=remove_address&uri='+peerUri);
|
|
triggerUpdate();
|
|
setTimeout(() => {if(g_content_page == "callAddressBook") { callAddressBookFromUrl(document.getElementById('menu-address')) }}, 200);
|
|
}
|
|
|
|
function removeHistoryConfirm() {
|
|
fetchHTML('./request_action.html?req=remove_history');
|
|
triggerUpdate();
|
|
setTimeout(() => {if(g_content_page == "callHistory") { callHistoryFromUrl(document.getElementById('menu-history')) }}, 200);
|
|
showToast("삭제 되었습니다.");
|
|
}
|
|
|
|
function removeHistory() {
|
|
g_confirm_callback = removeHistoryConfirm;
|
|
showConfirmPopup('삭제하시겠습니까?');
|
|
}
|
|
|
|
function callStartWithNumber(callNumber) {
|
|
fetchHTML('./request_action.html?req=start_call¶m='+callNumber);
|
|
startCallTimer();
|
|
document.getElementById('text-call-number').innerHTML = callNumber;
|
|
//document.getElementById('area-call-progress').style = `visibility:visible`;
|
|
}
|
|
|
|
function onIpTypeSelectedValue(selectElement) {
|
|
if(selectElement.options[selectElement.selectedIndex].text == 'DHCP') {
|
|
document.getElementById('area-static-ipssetting').style = `display:none`;
|
|
} else if(selectElement.options[selectElement.selectedIndex].text == 'STATIC') {
|
|
document.getElementById('area-static-ipssetting').style = `display:block`;
|
|
}
|
|
}
|
|
|
|
function applyNetwork() {
|
|
var netType = document.getElementById('select-network-type').value;
|
|
if(netType == 'dhcp') {
|
|
fetchHTML('./request_action.html?req=network_setting&type=dhcp');
|
|
showToast("적용 되었습니다");
|
|
} else if(netType == 'static') {
|
|
var ipAddress = document.getElementById('input-ipaddress').value;
|
|
var netmask = document.getElementById('input-netmask').value;
|
|
var gateway = document.getElementById('input-gateway').value;
|
|
var dns = document.getElementById('input-dns').value;
|
|
var paramStr = '&address=' + ipAddress;
|
|
paramStr += '&netmask=' + netmask;
|
|
paramStr += '&gateway=' + gateway;
|
|
paramStr += '&dns=' + dns;
|
|
fetchHTML('./request_action.html?req=network_setting&type=static' + paramStr);
|
|
showToast("적용 되었습니다");
|
|
}
|
|
//console.log('네트워크 타입 : ' + netType);
|
|
}
|
|
|
|
function applyServerSetting() {
|
|
var displayName = document.getElementById('input-display-name').value;
|
|
var accountName = document.getElementById('input-account-name').value;
|
|
var password = document.getElementById('input-account-password').value;
|
|
var serverAddr = document.getElementById('input-server-address').value;
|
|
var transport = document.getElementById('select-transport').value;
|
|
var mediaEncryption = document.getElementById('select-media-encryption').value;
|
|
var paramStr = '&display_name=' + displayName;
|
|
paramStr += '&account_name=' + accountName;
|
|
paramStr += '&password=' + password;
|
|
paramStr += '&server_address=' + serverAddr;
|
|
paramStr += '&transport=' + transport;
|
|
paramStr += '&media_encryption=' + mediaEncryption;
|
|
fetchHTML('./request_action.html?req=server_setting' + paramStr);
|
|
showToast("적용 되었습니다");
|
|
}
|
|
|
|
function applyLayoutSetting() {
|
|
var layout = document.getElementById('layout-selection').innerText;
|
|
fetchHTML('./request_action.html?req=layout_setting&layout=' + layout);
|
|
showToast("적용 되었습니다");
|
|
}
|
|
|
|
function applyMiscSetting() {
|
|
var deviceName = document.getElementById('input-device-name').value;
|
|
var displayType = document.getElementById('select-display-type').value;
|
|
var paramStr = '&device_name=' + deviceName;
|
|
paramStr += '&display_type=' + displayType;
|
|
|
|
fetchHTML('./request_action.html?req=misc_setting' + paramStr);
|
|
showToast("적용 되었습니다");
|
|
}
|
|
|
|
|
|
|
|
// *************************
|
|
// 메뉴별 컨텐츠 관련 함수 영역
|
|
// *************************
|
|
function callSendContent() {
|
|
g_content_page = "callSend";
|
|
pageChanged();
|
|
|
|
var source = `\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:10px'>전화 걸기</h3>\
|
|
<div class='input-box'>\
|
|
<input id='input-call-number' type='text' placeholder='전화번호'>\
|
|
</div>\
|
|
<div>\
|
|
<button class='top-button-green' onclick='callStart()'>\
|
|
<i class='fa-solid fa-phone-volume'></i>\
|
|
<div class='top-button-text'>전화걸기</div>\
|
|
</button>\
|
|
</div>\
|
|
</div>\
|
|
`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function callAddressBookContent() {
|
|
var source = `\
|
|
<div class='dashboard-container'>\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:10px'>주소록</h3>\
|
|
<div class='table-container'>\
|
|
<div class="search-box">\
|
|
<input type="text" placeholder="검색...">\
|
|
</div>\
|
|
<table>\
|
|
<thead>\
|
|
<tr>\
|
|
<th width='30%'>연결번호</th>\
|
|
<th width='30%'>이름</th>\
|
|
<th>제어</th>\
|
|
</tr>\
|
|
</thead>\
|
|
<tbody>\
|
|
<tr><td>7005</td><td>김가나</td><td><button class='table-inner-button' onclick='#'>통화</button><button class='table-inner-button button-orange' style='margin-left:3px' onclick='#'>수정</button><button class='table-inner-button button-red' style='margin-left:3px' onclick='#'>삭제</button></td></tr>\
|
|
<tr><td>7005</td><td>김나다</td><td><button class='table-inner-button' onclick='#'>통화</button><button class='table-inner-button button-orange' style='margin-left:3px' onclick='#'>수정</button><button class='table-inner-button button-red' style='margin-left:3px' onclick='#'>삭제</button></td></tr>\
|
|
<tr><td>7005</td><td>김다라</td><td><button class='table-inner-button' onclick='#'>통화</button><button class='table-inner-button button-orange' style='margin-left:3px' onclick='#'>수정</button><button class='table-inner-button button-red' style='margin-left:3px' onclick='#'>삭제</button></td></tr>\
|
|
<tr><td>7002</td><td>김라마</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7001</td><td>이가나</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7005</td><td>이나다</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7005</td><td>이다라</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7005</td><td>박가나</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7002</td><td>박나다</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7001</td><td>박다라</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7005</td><td>최가나</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
<tr><td>7005</td><td>최나다</td><td><button class='table-inner-button' onclick='#'>통화</button></td></tr>\
|
|
</tbody>\
|
|
</table>\
|
|
<div class='pagination'></div>\
|
|
</div>\
|
|
</div>\
|
|
<div class='dashboard-section'>\
|
|
<h3 style='margin-bottom:10px'></h3>\
|
|
<p><strong>Device Name:</strong> StudioX52-84B2ECFM</p>\
|
|
<p><strong>Software Version:</strong> 4.4.1-426075</p>\
|
|
<p><strong>MAC Address:</strong> 00:e0:db:84:b2:ec</p>\
|
|
</div>\
|
|
</div>`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function callAddressBookFromUrl(element) {
|
|
g_content_page = "callAddressBook";
|
|
pageChanged();
|
|
fetchHTML('./call_address').then(html => {
|
|
loadContent(html, element);
|
|
updateTablePager();
|
|
});
|
|
}
|
|
|
|
function callHistoryFromUrl(element) {
|
|
g_content_page = "callHistory";
|
|
pageChanged();
|
|
fetchHTML('./call_history').then(html => {
|
|
loadContent(html, element);
|
|
updateTablePager();
|
|
});
|
|
}
|
|
|
|
function dashboardFromUrl(element) {
|
|
g_content_page = "dashboard";
|
|
pageChanged();
|
|
fetchHTML('./dashboard').then(html => {
|
|
loadContent(html, element);
|
|
});
|
|
}
|
|
|
|
function callStatisticsFromUrl(element) {
|
|
g_content_page = "callStatistics";
|
|
pageChanged();
|
|
callStatisticsFromUrlProxy(element);
|
|
}
|
|
|
|
function callStatisticsFromUrlProxy(element) {
|
|
if(g_call_status != 'calling') {
|
|
loadContent("<h3 style='margin-bottom:10px'>현재 통화중이 아닙니다.</h3>", element);
|
|
return;
|
|
}
|
|
|
|
fetchHTML('./call_statistics').then(html => {
|
|
loadContent(html, element);
|
|
});
|
|
|
|
if(g_content_page == "callStatistics") {
|
|
timerReload = setTimeout(() => {callStatisticsFromUrlProxy(element)}, 1000);
|
|
}
|
|
}
|
|
|
|
function pageChanged() {
|
|
clearTimeout(timerReload);
|
|
g_input_updated_page = '';
|
|
}
|
|
|
|
function monitoringContent() {
|
|
g_content_page = "monitoring";
|
|
pageChanged();
|
|
if(g_call_status != 'calling') {
|
|
var source = `\
|
|
<h3 style='margin-bottom:10px'>현재 통화중이 아닙니다.</h3>\
|
|
<div class='dashboard-container' style='width:70vw;'>\
|
|
<div class='dashboard-section'>\
|
|
<h3 style='margin-bottom:10px'>근거리 화면</h3>\
|
|
<img id='img-near-screen' src='' style='width:100%'>\
|
|
</div>\
|
|
</div>`;
|
|
return source;
|
|
//return "<h3 style='margin-bottom:10px'>현재 통화중이 아닙니다.</h3>";
|
|
}
|
|
|
|
var source = `\
|
|
<div class='dashboard-container'>\
|
|
<div class='dashboard-section section-half'>\
|
|
<h3 style='margin-bottom:10px'>원거리 화면</h3>\
|
|
<img id='img-far-screen' src='' style='width:100%'>\
|
|
</div>\
|
|
<div class='dashboard-section section-half'>\
|
|
<h3 style='margin-bottom:10px'>근거리 화면</h3>\
|
|
<img id='img-near-screen' src='' style='width:100%'>\
|
|
</div>\
|
|
</div>`;
|
|
|
|
|
|
return source;
|
|
}
|
|
|
|
function miscSettingContent() {
|
|
g_content_page = "normalSetting";
|
|
setTimeout(updateTimer, 1);
|
|
pageChanged();
|
|
var source = `\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:20px'>일반 설정</h3>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label-long'>장치 이름</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-device-name' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-device-name'></label>\
|
|
</div>\
|
|
<div style='display: flex;margin-bottom: 20px'>\
|
|
<label class='input-label-long'>모니터 설정</label>\
|
|
<div class='combo-box'>\
|
|
<select id='select-display-type'>\
|
|
<option value=''></option>\
|
|
<option value='1'>모니터1:원거리, 모니터2:근거리/컨텐츠</option>\
|
|
<option value='2'>모니터1:근거리/컨텐츠, 모니터2:원거리</option>\
|
|
</select>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-display-type'></label>\
|
|
</div>\
|
|
<button class='apply-button' onclick='applyMiscSetting()'>적용</button>\
|
|
</div>\
|
|
`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function networkSettingContent() {
|
|
g_content_page = "networkSetting";
|
|
pageChanged();
|
|
setTimeout(updateTimer, 1);
|
|
var source = `\
|
|
<div class='dashboard-container'>\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:20px'>네트워크 설정</h3>\
|
|
<div style='display: flex;margin-bottom: 20px'>\
|
|
<label class='input-label'>IP할당방식</label>\
|
|
<div class='combo-box'>\
|
|
<select id='select-network-type' onchange='onIpTypeSelectedValue(this)'>\
|
|
<option value=''></option>\
|
|
<option value='dhcp'>DHCP</option>\
|
|
<option value='static'>STATIC</option>\
|
|
</select>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-network-type'></label>\
|
|
</div>\
|
|
<span id='area-static-ipssetting' style='display:none'>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label'>IP</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-ipaddress' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-ipv4-address'></label>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label'>NETMASK</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-netmask' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-ipv4-netmask'></label>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label'>GATEWAY</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-gateway' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-ipv4-gateway'></label>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label'>DNS</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-dns' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-ipv4-dns'></label>\
|
|
</div>\
|
|
</span>\
|
|
<button class='apply-button' onclick='applyNetwork()'>적용</button>\
|
|
</div>\
|
|
</div>`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function serverSettingContent() {
|
|
g_content_page = "serverSetting";
|
|
pageChanged();
|
|
setTimeout(updateTimer, 1);
|
|
var source = `\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:20px'>SIP 설정</h3>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label' style='width:120px'>표시명</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-display-name' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-display-name'></label>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label' style='width:120px'>인증 사용자명</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-account-name' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-account-name'></label>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label' style='width:120px'>인증 암호</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-account-password' type='text'>\
|
|
</div>\
|
|
</div>\
|
|
<div style='display: flex;'>\
|
|
<label class='input-label' style='width:120px'>SIP 서버주소</label>\
|
|
<div class='input-box'>\
|
|
<input id='input-server-address' type='text'>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-server-address'></label>\
|
|
</div>\
|
|
<div style='display: flex;margin-bottom: 15px'>\
|
|
<label class='input-label' style='width:120px'>트랜스포트</label>\
|
|
<div class='combo-box'>\
|
|
<select id='select-transport'>\
|
|
<option value='udp'>UDP</option>\
|
|
<option value='tcp'>TCP</option>\
|
|
<option value='tls'>TLS</option>\
|
|
</select>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-prefer-transport'></label>\
|
|
</div>\
|
|
<div style='display: flex;margin-bottom: 15px'>\
|
|
<label class='input-label' style='width:120px'>미디어 암호화</label>\
|
|
<div class='combo-box'>\
|
|
<select id='select-media-encryption'>\
|
|
<option value='none'>사용안함</option>\
|
|
<!--option value='srtpo'>SRTP 옵션</option-->\
|
|
<option value='srtpm'>SRTP</option>\
|
|
</select>\
|
|
</div>\
|
|
<label class='prev-label' id='prev-media-encryption'></label>\
|
|
</div>\
|
|
<button class='apply-button' onclick='applyServerSetting()' style='margin-top:15px'>적용</button>\
|
|
</div>\
|
|
`;
|
|
|
|
return source;
|
|
}
|
|
|
|
var layoutAlt = [
|
|
"원거리 전체화면, 근거리 우하단",
|
|
"원거리 전체화면, 근거리 우상단",
|
|
"원거리 전체화면, 근거리 좌상단",
|
|
"원거리 전체화면, 근거리 좌하단",
|
|
"원거리 대상단, 근거리 소하단",
|
|
"원거리 대좌단, 근거리 소우단",
|
|
"원거리 대하단, 근거리 소상단",
|
|
"원거리 반좌단, 근거리 반우단",
|
|
"원거리 전체화면, 근거리 없음",
|
|
"원거리 없음, 근거리 전체화면"
|
|
];
|
|
|
|
function layoutContent() {
|
|
g_content_page = "layout";
|
|
pageChanged();
|
|
setTimeout(updateTimer, 1);
|
|
var source = `\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:20px'>PIP 레이아웃 설정</h3>\
|
|
<div class="gallery" id="imageGallery">\
|
|
<img id='layout1' src="layout1.png" alt="원거리 전체화면, 근거리 우하단" onclick="selectImage(this)">\
|
|
<img id='layout2' src="layout2.png" alt="원거리 전체화면, 근거리 우상단" onclick="selectImage(this)">\
|
|
<img id='layout3' src="layout3.png" alt="원거리 전체화면, 근거리 좌상단" onclick="selectImage(this)">\
|
|
<img id='layout4' src="layout4.png" alt="원거리 전체화면, 근거리 좌하단" onclick="selectImage(this)">\
|
|
<img id='layout5' src="layout5.png" alt="원거리 대상단, 근거리 소하단" onclick="selectImage(this)">\
|
|
<img id='layout6' src="layout6.png" alt="원거리 대좌단, 근거리 소우단" onclick="selectImage(this)">\
|
|
<img id='layout7' src="layout7.png" alt="원거리 대하단, 근거리 소상단" onclick="selectImage(this)">\
|
|
<img id='layout8' src="layout8.png" alt="원거리 반좌단, 근거리 반우단" onclick="selectImage(this)">\
|
|
<img id='layout9' src="layout9.png" alt="원거리 전체화면, 근거리 없음" onclick="selectImage(this)">\
|
|
<img id='layout10' src="layout10.png" alt="원거리 없음, 근거리 전체화면" onclick="selectImage(this)">\
|
|
</div>\
|
|
<div id="selectedImage">선택된 레이아웃: 없음</div> \
|
|
<div id="prev-layout" style="margin-top:10px; padding-top:10px; padding-bottom:10px; font-weight:bold; background-color:#c0c0c0"> </div> \
|
|
<span id="layout-selection" style="display:none"></span> \
|
|
<button class='apply-button' onclick='applyLayoutSetting()' style='margin-top:15px'>적용</button>\
|
|
|
|
</div>\
|
|
`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function dashboardContent() {
|
|
g_content_page = "dashboard";
|
|
pageChanged();
|
|
|
|
var source = `<div class='dashboard-container'>\
|
|
<div class='dashboard-section system-status'>\
|
|
<h3 style='margin-bottom:10px'>시스템 상태</h3>\
|
|
<ul>\
|
|
<li><span class='status-indicator'></span>네트워크 연결</li>\
|
|
<li><span id='hdmi1-in-status' class='status-indicator button-orange'></span>HDMI 입력 1</li>\
|
|
<li><span id='hdmi2-in-status' class='status-indicator button-orange'></span>HDMI 입력 2</li>\
|
|
<li><span id='hdmi1-out-status' class='status-indicator button-orange'></span>HDMI 출력 1</li>\
|
|
<li><span id='hdmi2-out-status' class='status-indicator button-orange'></span>HDMI 출력 2</li>\
|
|
<li><span id='usb-mic-status' class='status-indicator button-orange'></span>USB 마이크 연결</li>\
|
|
<li><span id='server-register-status' class='status-indicator button-orange'></span>SIP서버 연결</li>\
|
|
</ul>\
|
|
</div>\
|
|
<div class='dashboard-section'>\
|
|
<h3 style='margin-bottom:10px'>시스템 정보</h3>\
|
|
<p class='status-text'><strong>Device Name:</strong> OSVC-C220</p>\
|
|
<p class='status-text'><strong>Software Version:</strong> v1.0.0</p>\
|
|
<p class='status-text'><strong>MAC Address:</strong> ----</p>\
|
|
</div>\
|
|
</div>\
|
|
</div>`;
|
|
|
|
return source;
|
|
}
|
|
|
|
function selectMenu(menuId) {
|
|
var item = document.getElementById(menuId);
|
|
if(item != null) {
|
|
if(menuId == 'menu-network') {
|
|
openSubMenu('settings');
|
|
} else if(menuId == 'menu-monitoring' || menuId == 'menu-statistics') {
|
|
openSubMenu('call-monitoring');
|
|
}
|
|
item.click();
|
|
}
|
|
}
|
|
|
|
function selectImage(imgElement) {
|
|
// 모든 이미지의 선택 스타일 제거
|
|
document.querySelectorAll('.gallery img').forEach(img => {
|
|
img.classList.remove('selected');
|
|
});
|
|
|
|
// 선택한 이미지에 클래스 추가
|
|
imgElement.classList.add('selected');
|
|
|
|
const match = imgElement.src.match(/\/([\w-]+)\.png$/);
|
|
document.getElementById('layout-selection').innerText = match[1];
|
|
|
|
// 선택된 이미지 정보 표시
|
|
document.getElementById('selectedImage').innerText = `선택된 레이아웃: ${imgElement.alt}`;
|
|
}
|
|
</script>
|
|
</head>
|
|
<body onload="selectMenu('menu-dashboard');triggerUpdate();updateTimer()">
|
|
<iframe name='runner' id='runner' style="position:absolute;left:0px;top:0px;width:1px;height:1px;visibility:hidden;"></iframe>
|
|
<!-- <iframe id='status-poller' style="position:absolute;left:0px;top:0px;width:1px;height:1px;visibility:hidden;">
|
|
<input id="sip-status" type="text">
|
|
</iframe> -->
|
|
<input id="sip-status" type="text" style="position:absolute;left:0px;top:0px;width:1px;height:1px;visibility:hidden;">
|
|
|
|
<div class='top-bar'>OSVC</div>
|
|
|
|
<span id='area-call-progress' style='visibility:hidden'>
|
|
<span class='top-info-normal' style='position:absolute;top:7px;left:250px;width:120px'>
|
|
<div class='call-timer'>통화 번호</div>
|
|
<div id='text-call-number' class='time'>7003</div>
|
|
</span>
|
|
|
|
<span class='top-info-normal' style='position:absolute;top:7px;left:380px;width:140px'>
|
|
<div class='call-timer'>통화 시간</div>
|
|
<div class='time' id='timer'>00:00:00</div>
|
|
</span>
|
|
|
|
<div id='area-button-video-unmute' style='position:absolute;top:7px;left:600px'>
|
|
<button class='top-button' onclick="requestVideoUnMute()">
|
|
<i class='fas fa-video'></i>
|
|
<div class='top-button-text'>영상 켜기</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div id='area-button-video-mute' style='position:absolute;top:7px;left:600px'>
|
|
<button class='top-button-green' onclick="requestVideoMute()">
|
|
<i class='fas fa-video-slash'></i>
|
|
<div class='top-button-text'>영상 끄기</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div id='area-button-unmute' style='position:absolute;top:7px;left:700px'>
|
|
<button class='top-button' onclick="requestUnMute()">
|
|
<i class='fas fa-microphone'></i>
|
|
<div class='top-button-text'>음소거 해제</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div id='area-button-mute' style='position:absolute;top:7px;left:700px'>
|
|
<button class='top-button-green' onclick="requestMute()">
|
|
<i class='fas fa-microphone'></i>
|
|
<div class='top-button-text'>음소거</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div style='position:absolute;top:7px;left:800px'>
|
|
<button class='top-button-normal' onclick="selectMenu('menu-statistics')">
|
|
<i class='fa-solid fa-signal'></i>
|
|
<div class='top-button-text'>통화 통계</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div style='position:absolute;top:7px;left:900px'>
|
|
<button class='top-button-normal' onclick="selectMenu('menu-monitoring')">
|
|
<i class='fa-solid fa-computer'></i>
|
|
<div class='top-button-text'>모니터링</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div style='position:absolute;top:7px;left:1000px'>
|
|
<button class='top-button' onclick='callStop()'>
|
|
<i class='fa-solid fa-phone-slash'></i>
|
|
<div class='top-button-text'>전화 끊기</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div id='area-button-input-aux' style='position:absolute;top:7px;left:1100px'>
|
|
<button class='top-button-normal' onclick='setAudioSource("aux")'>
|
|
<i class='fa-solid fa-circle-play'></i>
|
|
<div class='top-button-text'>AUX 입력</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div id='area-button-input-mic' style='position:absolute;top:7px;left:1100px'>
|
|
<button class='top-button-green' onclick='setAudioSource("mic")'>
|
|
<i class='fa-solid fa-microphone-lines'></i>
|
|
<div class='top-button-text'>MIC 입력</div>
|
|
</button>
|
|
</div>
|
|
|
|
</span>
|
|
|
|
<div style='position:absolute;top:7px;left:1300px'>
|
|
<div style='display:flex;margin-top:15px;'>
|
|
<div style='font-size:13px;color:white;margin-right:5px;width:55px;text-align:right'>IP주소:</div>
|
|
<div id='text-ip-address' style='font-size:15px;color:white'></div>
|
|
</div>
|
|
<div style='display:flex;margin-top:10px;'>
|
|
<div style='font-size:13px;color:white;margin-right:5px;width:55px;text-align:right'>SIP주소:</div>
|
|
<div id='text-sip-address' style='font-size:15px;color:white'></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class='container'>
|
|
<div class='sidebar'>
|
|
<h2>OSVC-C220</h2>
|
|
<ul>
|
|
<!-- <li id="menu-dashboard" onclick='loadContent(dashboardContent(), this)' class='active'>대시보드</li>-->
|
|
<li id="menu-dashboard" onclick='dashboardFromUrl(this)' class='active'>대시보드</li>
|
|
<li onclick='toggleSubMenu(`call-management`)'>통화 관리 <i id='call-management-icon' class='fas fa-chevron-right'></i></li>
|
|
<ul class='submenu' id='call-management'>
|
|
<!--<li onclick='loadContent(`<h3>전화 걸기</h3><p>통화 기록을 확인하세요.</p>`, this)'>전화 걸기</li>-->
|
|
<li onclick='loadContent(callSendContent(), this)'>전화 걸기</li>
|
|
<li id="menu-address" onclick='callAddressBookFromUrl(this)'>주소록</li>
|
|
<!--<li onclick='loadContent(callHistoryContent(), this);updateTablePager();'>최근 통화</li>-->
|
|
<li id="menu-history" onclick='callHistoryFromUrl(this)'>최근 통화</li>
|
|
</ul>
|
|
<li onclick='toggleSubMenu(`call-monitoring`)'>통화 상태 <i id='call-monitoring-icon' class='fas fa-chevron-right'></i></li>
|
|
<ul class='submenu' id='call-monitoring'>
|
|
<li id="menu-monitoring" onclick='loadContent(monitoringContent(), this)'>모니터링</li>
|
|
<li id="menu-statistics" onclick='callStatisticsFromUrl(this)'>통화통계</li>
|
|
</ul>
|
|
|
|
<li onclick='toggleSubMenu(`settings`)'>설정 <i id='settings-icon' class='fas fa-chevron-right'></i></li>
|
|
<ul class='submenu' id='settings'>
|
|
<li onclick='loadContent(miscSettingContent(), this);'>일반 설정</li>
|
|
<li id="menu-network" onclick='loadContent(networkSettingContent(), this)'>네트워크 설정</li>
|
|
<li onclick='loadContent(serverSettingContent(), this)'>SIP 설정</li>
|
|
<li onclick='loadContent(layoutContent(), this)'>화면 설정</li>
|
|
</ul>
|
|
</ul>
|
|
</div>
|
|
<div class='content'>
|
|
<!--<div id='content-header' class='content-header'>대시보드 - 시스템 상태</div>-->
|
|
<div id='content-area'>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 모달 배경 (dimmed) -->
|
|
<div class="modal-overlay" id="modalOverlay">
|
|
<!-- 모달 내용 -->
|
|
<div class="modal">
|
|
<h2 id="modalMsg" >진행 중...</h2>
|
|
<div id="modalSpinner" class="spinner"></div>
|
|
<button id="closeModalBtn">닫기</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-overlay" id="confirmOverlay">
|
|
<!-- 모달 내용 -->
|
|
<div class="modal">
|
|
<h2 id="confirmMsg" >진행 중...</h2>
|
|
<button id="yesModalBtn">예</button>
|
|
<button id="noModalBtn">아니오</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="toast" class="toast"></div>
|
|
|
|
<script>
|
|
const closeModalBtn = document.getElementById('closeModalBtn');
|
|
const yesModalBtn = document.getElementById('yesModalBtn');
|
|
const noModalBtn = document.getElementById('noModalBtn');
|
|
|
|
// 모달 닫기 (버튼 클릭)
|
|
closeModalBtn.addEventListener('click', () => {
|
|
modalOverlay.style.display = 'none';
|
|
});
|
|
yesModalBtn.addEventListener('click', () => {
|
|
g_modal_confirm = 'y';
|
|
confirmOverlay.style.display = 'none';
|
|
if(g_confirm_callback != null) {
|
|
g_confirm_callback();
|
|
}
|
|
});
|
|
noModalBtn.addEventListener('click', () => {
|
|
g_modal_confirm = 'n';
|
|
confirmOverlay.style.display = 'none';
|
|
});
|
|
|
|
function showToast(message) {
|
|
let toast = document.getElementById("toast");
|
|
toast.innerText = message;
|
|
toast.classList.add("show");
|
|
setTimeout(() => {
|
|
toast.classList.remove("show");
|
|
}, 3000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|