/** * 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 = [] /** * @typedef {Object} MessageBank * @property {number} index * @property {string} description * @property {string} language * @property {number} aNN_ID * @property {string} voice_Type * @property {string} message_Detail * @property {string} message_TAGS */ /** * List of Messagebank data loaded from server * taruh di sini karena dipakai di banyak tempat * @type {MessageBank[]} */ window.messagebankdata ??= []; /** * Reload message bank from server * @param {string} APIURL API URL endpoint, default "MessageBank/" * @param {Function|null} callback Optional callback function to execute after loading */ function reloadMessageBank(APIURL = "MessageBank/", callback=null) { window.messagebankdata ??= []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { window.messagebankdata.push(...okdata); console.log("Loaded " + window.messagebankdata.length + " message bank items"); window.selectedmessagerow = null; if (callback && typeof callback === 'function') callback(); } }, (errdata) => { alert("Error loading messagebank : " + errdata.message); }); } /** * @typedef {Object} ScheduleBank * @property {number} index * @property {string} description * @property {string} day * @property {string} time * @property {string} soundpath * @property {number} repeat * @property {boolean} enable * @property {string} broadcastZones * @property {string} language */ /** List of Schedulebank data loaded from server * @type {ScheduleBank[]} */ window.schedulebankdata = []; /** * Reload timer bank from server * taruh di sini karena dipakai di banyak tempat * @param {string} APIURL API URL endpoint, default "ScheduleBank/" * @param {Function|null} callback Optional callback function to execute after loading */ function reloadTimerBank(APIURL = "ScheduleBank/", callback=null) { window.schedulebankdata = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { window.schedulebankdata.push(...okdata); selectedschedulerow = null; console.log("Loaded " + window.schedulebankdata.length + " schedule bank items"); if (callback && typeof callback === 'function') callback(); } }, (errdata) => { alert("Error loading schedulebank : " + errdata.message); }); } /** * @typedef {Object} BroadcastZone * @property {number} index * @property {string} description * @property {String} SoundChannel * @property {String} Box * @property {String} Relay */ /** * List of broadcast zones available * @type {BroadcastZone[]} */ window.BroadcastZoneList ??= []; /** * Reload broadcast zones from server * taruh di sini karena dipakai di banyak tempat * @param {String} APIURL API URL endpoint (default "BroadcastZones/") * @param {Function|null} callback Optional callback function to execute after loading */ function reloadBroadcastZones(APIURL = "BroadcastZones/", callback=null) { window.BroadcastZoneList = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { //console.log("reloadBroadcastZones : ", okdata) window.BroadcastZoneList.push(...okdata); console.log("Loaded " + window.BroadcastZoneList.length + " broadcast zone items"); window.selectedbroadcastzonerow = null; if (callback && typeof callback === 'function') callback(); } else console.log("reloadBroadcastZones: okdata is not array"); }, (errdata) => { alert("Error loading broadcast zones : " + errdata.message); }); } /** * @typedef {Object} SoundBank * @property {number} index * @property {string} description * @property {string} tag * @property {string} category * @property {string} language * @property {string} voiceType * @property {string} path */ /** * @typedef {Object} Select2item * @property {number} id * @property {string} text */ /** * List of Soundbank data loaded from server * taruh di sini karena dipakai di banyak tempat * @type {SoundBank[]} */ window.soundbankdata = []; /** * List of sound files in the soundbank directory, that ends with .wav or .mp3 * taruh di sini karena dipakai di banyak tempat * @type {string[]} */ window.soundbankfiles = []; /** * Select2 data source * See https://select2.org/data-sources/formats * @type {Select2item[]} */ window.select2data = []; /** * Reload sound bank from server * @param {String} APIURL API URL endpoint, default "SoundBank/" * @param {Function|null} callback Optional callback function to execute after loading */ function reloadSoundBank(APIURL = "SoundBank/", callback=null) { window.soundbankdata = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { window.soundbankdata.push(...okdata); window.selectedsoundrow = null; console.log("Loaded " + window.soundbankdata.length + " sound bank items"); if (callback && typeof callback === 'function') callback(); } }, (errdata) => { alert("Error loading soundbank : " + errdata.message); }); } /** * Reload soundbank files from server * @param {String} APIURL API URL endpoint (default "SoundBank/") * @param {Function|null} callback Optional callback function to execute after loading */ function reloadSoundbankFiles(APIURL = "SoundBank/",callback=null) { window.soundbankfiles = []; fetchAPI(APIURL + "ListFiles", "GET", {}, null, (okdata) => { // okdata is a string contains elements separated by semicolon ; if (Array.isArray(okdata)) { window.soundbankfiles = okdata.filter(item => item.trim().length > 0); // refill select2data window.select2data = window.soundbankfiles.map((item, index) => ({ id: index + 1, text: item })); console.log("Loaded " + window.soundbankfiles.length + " sound bank files"); if (callback && typeof callback === 'function') callback(); } else console.log("reloadSoundbankFiles: okdata is not array"); }, (errdata) => { alert("Error loading soundbank files : " + errdata.message); }); } /** * @typedef {Object} SoundChannel * @property {number} index - The index of the sound channel. * @property {string} channel - The name of the sound channel. * @property {string} ip - The IP address associated with the sound channel. */ /** * @type {SoundChannel[]} */ window.soundChannels = []; /** * Reload sound channels from server * @param {String} APIURL API URL endpoint (default "SoundChannel/") * @param {Function|null} callback Optional callback function to execute after loading */ function reloadSoundChannel(APIURL = "SoundChannel/", callback=null) { window.soundChannels = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { //console.log("reloadSoundChannel : ", okdata) window.soundChannels.push(...okdata); window.selectedsoundchannelrow = null; console.log("Loaded " + window.soundChannels.length + " sound channel items"); if (callback && typeof callback === 'function') callback(); } else console.log("reloadSoundChannel: okdata is not array"); }, (errdata) => { alert("Error loading sound channels : " + errdata.message); }); } /** * @typedef {Object} LanguageBank * @property {number} index * @property {string} tag * @property {string} language * */ /** List of Languagebank data loaded from server * @type {LanguageBank[]} */ window.languagebankdata = []; /** * Reload language bank from server * @param {string} APIURL API URL endpoint, default "LanguageLink/" * @param {Function|null} callback Optional callback function to execute after loading */ function reloadLanguageBank(APIURL = "LanguageLink/", callback=null) { window.languagebankdata = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { window.languagebankdata.push(...okdata); window.selectedlanguagerow = null; console.log("Loaded " + window.languagebankdata.length + " language bank items"); if (callback && typeof callback === 'function') callback(); } }, (errdata) => { alert("Error loading languagebank : " + errdata.message); }); } /** * 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} */ function ListItem(text, className = "") { return $('
  • ').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} cbOK function that accept object data * @param {Function} 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(); reloadMessageBank(); reloadTimerBank(); reloadBroadcastZones(); reloadSoundBank(); reloadSoundbankFiles(); reloadSoundChannel(); reloadLanguageBank(); // 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": $('#networkstatus').text("Network : " + data) 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" }) });