493 lines
16 KiB
JavaScript
493 lines
16 KiB
JavaScript
/**
|
|
* List of voice types available
|
|
* @type {string[]}
|
|
*/
|
|
window.voiceTypes = [];
|
|
/**
|
|
* List of categories available
|
|
* @type {string[]}
|
|
*/
|
|
window.categories = [];
|
|
/**
|
|
* List of languages available
|
|
* @type {string[]}
|
|
*/
|
|
window.languages = [];
|
|
|
|
/**
|
|
* List of scheduled days available
|
|
* @type {string[]}
|
|
*/
|
|
window.scheduledays = []
|
|
|
|
/**
|
|
* Create a list item element
|
|
* @param {String} text Text Content for the list item
|
|
* @param {String} className Specific class name for the list item
|
|
* @returns {JQuery<HTMLElement>}
|
|
*/
|
|
function ListItem(text, className = "") {
|
|
return $('<li>').addClass(className).text(text);
|
|
}
|
|
|
|
/**
|
|
* WebSocket connection
|
|
* @type {WebSocket}
|
|
*/
|
|
window.ws = null;
|
|
|
|
/**
|
|
* Send a command to the WebSocket server.
|
|
* @param {String} command command to send
|
|
* @param {String} data data to send
|
|
*/
|
|
function sendCommand(command, data) {
|
|
if (window.ws.readyState === WebSocket.OPEN) {
|
|
window.ws.send(JSON.stringify({ command, data }));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch API helper function
|
|
* @param {string} endpoint Endpoint URL
|
|
* @param {string} method Method (GET, POST, etc.)
|
|
* @param {Object} headers Headers to include in the request
|
|
* @param {Object} body Body of the request
|
|
* @param {Function} cbOK Callback function for successful response
|
|
* @param {Function} cbError Callback function for error response
|
|
*/
|
|
function fetchAPI(endpoint, method, headers = {}, body = null, cbOK, cbError) {
|
|
let url = window.location.origin + "/api/" + endpoint;
|
|
let options = {
|
|
method: method,
|
|
headers: headers
|
|
}
|
|
if (body !== null) {
|
|
options.body = JSON.stringify(body);
|
|
if (!options.headers['Content-Type']) {
|
|
options.headers['Content-Type'] = 'application/json';
|
|
}
|
|
}
|
|
fetch(url, options)
|
|
.then(async(response) => {
|
|
if (!response.ok) {
|
|
let msg ;
|
|
try{
|
|
let _xxx = await response.json();
|
|
msg = _xxx.message || response.statusText;
|
|
} catch {
|
|
msg = await response.statusText;
|
|
}
|
|
throw new Error(msg);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
cbOK(data);
|
|
})
|
|
.catch(error => {
|
|
cbError(error);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch asset file from /assets/img/*
|
|
* @param {String} url the filename to fetch, relative to /assets/img/
|
|
* @param {Function} cbOK callback function on success, will receive the object URL
|
|
* @param {Function} cbError callback function on error, will receive the error object
|
|
*/
|
|
function fetchImg(url, cbOK, cbError) {
|
|
url = "/assets/img/" + url;
|
|
fetch(url)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok ' + response.statusText);
|
|
}
|
|
return response.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
cbOK(url);
|
|
})
|
|
.catch(error => {
|
|
cbError(error);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reload voice types from server
|
|
*/
|
|
function getVoiceTypes() {
|
|
window.voiceTypes = [];
|
|
fetchAPI("VoiceType", "GET", {}, null, (okdata) => {
|
|
// okdata is a string contains elements separated by semicolon ;
|
|
if (Array.isArray(okdata)) {
|
|
window.voiceTypes = okdata.filter(item => item.trim().length > 0);
|
|
//console.log("Loaded " + voiceTypes.length + " voice types : " + voiceTypes.join(", "));
|
|
} else console.log("getVoiceTypes: okdata is not array");
|
|
}, (errdata) => {
|
|
alert("Error loading voice types : " + errdata.message);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reload categories from server
|
|
*/
|
|
function getCategories() {
|
|
window.categories = [];
|
|
fetchAPI("Category", "GET", {}, null, (okdata) => {
|
|
// okdata is a string contains elements separated by semicolon ;
|
|
if (Array.isArray(okdata)) {
|
|
window.categories = okdata.filter(item => item.trim().length > 0);
|
|
//console.log("Loaded " + categories.length + " categories : " + categories.join(", "));
|
|
} else console.log("getCategories: okdata is not array");
|
|
}, (errdata) => {
|
|
alert("Error loading categories : " + errdata.message);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reload languages from server
|
|
*/
|
|
function getLanguages() {
|
|
window.languages = [];
|
|
fetchAPI("Language", "GET", {}, null, (okdata) => {
|
|
// okdata is a string contains elements separated by semicolon ;
|
|
if (Array.isArray(okdata)) {
|
|
window.languages = okdata.filter(item => item.trim().length > 0);
|
|
//console.log("Loaded " + languages.length + " languages : " + languages.join(", ") );
|
|
} else console.log("getLanguages: okdata is not array");
|
|
}, (errdata) => {
|
|
alert("Error loading languages : " + errdata.message);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reload scheduled days from server
|
|
*/
|
|
function getScheduledDays() {
|
|
window.scheduledays = [];
|
|
fetchAPI("ScheduleDay", "GET", {}, null, (okdata) => {
|
|
// okdata is a string contains elements separated by semicolon ;
|
|
if (Array.isArray(okdata)) {
|
|
window.scheduledays = okdata.filter(item => item.trim().length > 0);
|
|
//console.log("Loaded " + scheduledays.length + " scheduled days : " + scheduledays.join(", ") );
|
|
} else console.log("getScheduledDays: okdata is not array");
|
|
}, (errdata) => {
|
|
alert("Error loading scheduled days : " + errdata.message);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Clear database mechanism
|
|
* @param {String} APIURL API URL endpoint
|
|
* @param {String} whattoclear what to clear
|
|
* @param {Function} cbOK callback function on success
|
|
* @param {Function} cbError callback function on error
|
|
*/
|
|
function DoClear(APIURL, whattoclear, cbOK, cbError) {
|
|
if (confirm(`Are you sure want to clear ${whattoclear} ? This procedure is not reversible`)) {
|
|
fetchAPI(APIURL + "List", "DELETE", {}, null, (okdata) => {
|
|
cbOK(okdata);
|
|
}, (errdata) => {
|
|
cbError(errdata);
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Export mechanism to XLSX file
|
|
* @param {String} APIURL API URL endpoint
|
|
* @param {String} filename target filename
|
|
* @param {Object} queryParams additional query parameters as object
|
|
*/
|
|
function DoExport(APIURL, filename, queryParams = {}) {
|
|
// send GET request to APIURL + "ExportXLSX"
|
|
// reply Content-Type is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
|
// reply Content-Disposition: attachment; filename=filename
|
|
// Use fetch to download the XLSX file as a blob and trigger download
|
|
let url = "/api/" + APIURL + "ExportXLSX";
|
|
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
url += "?" + new URLSearchParams(queryParams).toString();
|
|
}
|
|
|
|
fetch(url, {
|
|
method: "GET",
|
|
headers: {}
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Network response was not ok ' + response.statusText);
|
|
return response.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
a.remove();
|
|
window.URL.revokeObjectURL(url);
|
|
})
|
|
.catch(error => {
|
|
alert("Error export to " + filename + ": " + error.message);
|
|
});
|
|
return; // prevent the rest of the function from running
|
|
|
|
}
|
|
|
|
/**
|
|
* Import mechanism from XLSX file
|
|
* @param {String} APIURL API URL endpoint
|
|
* @param {Function<Object>} cbOK function that accept object data
|
|
* @param {Function<Error>} cbError function that accept error object
|
|
*/
|
|
function DoImport(APIURL, cbOK, cbError) {
|
|
// Open file selection dialog that accepts only .xlsx files
|
|
// then upload to server using fetchAPI at "api/ImportXLSX" with POST method
|
|
let fileInput = document.createElement('input');
|
|
fileInput.type = 'file';
|
|
fileInput.accept = '.xlsx';
|
|
fileInput.onchange = e => {
|
|
let file = e.target.files[0];
|
|
if (file) {
|
|
let formData = new FormData();
|
|
formData.append('file', file);
|
|
fetch("/api/" + APIURL + "ImportXLSX", { method: 'POST', body: formData })
|
|
.then(response => {
|
|
if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); }
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
cbOK(data);
|
|
})
|
|
.catch(error => {
|
|
cbError(error);
|
|
});
|
|
} else {
|
|
cbError(new Error("No file selected"));
|
|
}
|
|
};
|
|
fileInput.click();
|
|
fileInput.remove();
|
|
}
|
|
|
|
|
|
window.greencircle = null;
|
|
window.redcircle = null;
|
|
/**
|
|
* App entry point
|
|
*/
|
|
$(document).ready(function () {
|
|
document.title = "Automatic Announcement System"
|
|
if (window.greencircle === null){
|
|
fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
|
|
}
|
|
if (window.redcircle === null){
|
|
fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
|
|
}
|
|
const wsURL = window.location.pathname + '/ws'
|
|
if (chrome && chrome.runtime && chrome.runtime.lastError) {
|
|
alert("Runtime error: " + chrome.runtime.lastError.message);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
// reset status indicators
|
|
function resetStatusIndicators() {
|
|
$('#onlineindicator').attr('src', window.redcircle);
|
|
$('#cpustatus').text("CPU : N/A");
|
|
$('#ramstatus').text("RAM : N/A");
|
|
$('#diskstatus').text("Disk : N/A");
|
|
$('#networkstatus').text("Network : N/A");
|
|
$('#datetimetext').text("Date/Time : N/A");
|
|
}
|
|
|
|
|
|
resetStatusIndicators();
|
|
getVoiceTypes();
|
|
getCategories();
|
|
getLanguages();
|
|
getScheduledDays();
|
|
|
|
|
|
// Initialize WebSocket connection
|
|
window.ws = new WebSocket(wsURL);
|
|
|
|
window.ws.onopen = () => {
|
|
console.log('WebSocket connection established');
|
|
$('#onlineindicator').attr('src', window.greencircle);
|
|
};
|
|
window.ws.onmessage = (event) => {
|
|
if ($('#onlineindicator').attr('src') !== window.greencircle) {
|
|
$('#onlineindicator').attr('src', window.greencircle);
|
|
}
|
|
let rep = JSON.parse(event.data);
|
|
let cmd = rep.reply
|
|
let data = rep.data;
|
|
if (cmd && cmd.length > 0) {
|
|
switch (cmd) {
|
|
case "getCPUStatus":
|
|
$('#cpustatus').text("CPU : " + data)
|
|
break;
|
|
case "getMemoryStatus":
|
|
$('#ramstatus').text("RAM : " + data)
|
|
break;
|
|
case "getDiskStatus":
|
|
$('#diskstatus').text("Disk : " + data)
|
|
break;
|
|
case "getNetworkStatus":
|
|
//console.log("Network status: ", data);
|
|
let result = "";
|
|
let json = JSON.parse(data);
|
|
if (Array.isArray(json) && json.length> 0){
|
|
json.forEach((net)=>{
|
|
//console.log("Network interface: ", net);
|
|
if (result.length>0) result+="\n"
|
|
result+=`${net.displayName} (${net.ipV4addr.join(";")}) TX:${(net.txSpeed/1024).toFixed(1)} KB/s RX:${(net.rxSpeed/1024).toFixed(1)} KB/s; `
|
|
})
|
|
} else result = "N/A";
|
|
$('#networkstatus').text(result)
|
|
break;
|
|
case "getSystemTime":
|
|
$('#datetimetext').text(data)
|
|
break;
|
|
|
|
}
|
|
}
|
|
};
|
|
window.ws.onclose = () => {
|
|
console.log('WebSocket connection closed');
|
|
resetStatusIndicators();
|
|
|
|
};
|
|
|
|
// window.ws.onerror = (error) => {
|
|
// console.error('WebSocket error:', error);
|
|
// };
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
sendCommand("getCPUStatus", "")
|
|
sendCommand("getMemoryStatus", "")
|
|
sendCommand("getDiskStatus", "")
|
|
sendCommand("getNetworkStatus", "")
|
|
sendCommand("getSystemTime", "")
|
|
}, 1000)
|
|
|
|
let sidemenu = new bootstrap.Offcanvas('#offcanvas-menu');
|
|
$('#showmenu').click(() => { sidemenu.show(); })
|
|
|
|
$('#homelink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('overview.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Overview content loaded successfully");
|
|
}
|
|
});
|
|
});
|
|
$('#soundbanklink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('soundbank.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Soundbank content loaded successfully");
|
|
// pindah soundbank.js
|
|
|
|
} else {
|
|
console.error("Error loading soundbank content : ", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
|
|
})
|
|
$('#messagebanklink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('messagebank.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Messagebank content loaded successfully");
|
|
// pindah messagebank.js
|
|
|
|
} else {
|
|
console.error("Error loading messagebank content : ", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
|
|
})
|
|
$('#languagelink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('language.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Language content loaded successfully");
|
|
// pindah languagelink.js
|
|
|
|
} else {
|
|
console.error("Error loading language content : ", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
})
|
|
$('#broadcastzonelink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('broadcastzones.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Broadcast Zone content loaded successfully");
|
|
// pindah ke broadcastzones.js
|
|
|
|
} else {
|
|
console.error("Error loading broadcast zone content : ", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
})
|
|
$('#timerlink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('timer.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Timer content loaded successfully");
|
|
// pindah ke schedulebank.js
|
|
|
|
} else {
|
|
console.error("Error loading timer content : ", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
})
|
|
$('#loglink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('log.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Log content loaded successfully");
|
|
// pindah ke log.js
|
|
|
|
} else {
|
|
console.error("Error loading log content:", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
})
|
|
$('#usermanagement').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('usermanagement.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("User Management content loaded successfully");
|
|
// pindah ke usermanagement.js
|
|
|
|
} else {
|
|
console.error("Error loading user management content:", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
});
|
|
$('#settinglink').click(() => {
|
|
sidemenu.hide();
|
|
$('#content').load('setting.html', function (response, status, xhr) {
|
|
if (status === "success") {
|
|
console.log("Setting content loaded successfully");
|
|
//sendCommand("getSetting", "");
|
|
} else {
|
|
console.error("Error loading setting content:", xhr.status, xhr.statusText);
|
|
}
|
|
});
|
|
})
|
|
$('#logoutlink').click(() => {
|
|
window.location.href = "login.html"
|
|
})
|
|
|
|
}); |