const id = s => document.getElementById(s);
let configEditor;
$(async () => {
const gswFeatures = await fetchApi('GET', 'features');
if (gswFeatures) {
await setRandomBg().catch(() => { });
setInterval(setRandomBg, 60000);
let unsavedChanges = false;
const confirmText = 'You have unsaved changes! Are you sure you want to ';
$(window).on('beforeunload', e => {
if (unsavedChanges) return confirmText + 'navigate away?';
});
$('#logout').click(e => {
e.preventDefault();
if (unsavedChanges && !confirm(confirmText + 'log out?')) return;
logout();
});
activateMenu();
$('body > nav > ul > li > a').click(e => {
e.preventDefault();
activateMenu(e.currentTarget);
});
$('#top-menu').removeClass('invisible');
$('body > footer').removeClass('d-none');
// Configuration
const gswConfig = await fetchApi('GET', 'config');
if (gswConfig && gswConfig.config) {
configEditor = new JSONEditor(id('config-form'), {
"theme": "bootstrap4",
"iconlib": "fontawesome5",
"object_layout": "normal",
"template": "mustache",
"show_errors": "always",
"required_by_default": 0,
"no_additional_properties": 1,
"display_required_only": 0,
"remove_empty_properties": 1,
"keep_oneof_values": 0,
"ajax": 0,
"ajaxCredentials": 0,
"show_opt_in": 1,
"disable_edit_json": 0,
"disable_collapse": 1,
"disable_properties": 1,
"disable_array_add": 0,
"disable_array_reorder": 1,
"disable_array_delete": 0,
"enable_array_copy": 1,
"array_controls_top": 0,
"disable_array_delete_all_rows": 1,
"disable_array_delete_last_row": 1,
"prompt_before_delete": 1,
"schema": {
"title": "Configuration",
"type": "array",
"default": [],
"minItems": 0,
"uniqueItems": true,
"format": "tabs-top",
"items": {
"title": "Game Server",
"type": "object",
"required": [
"name",
"type",
"host",
"port"
],
"properties": {
"name": {
"title": "Name",
"type": "string",
"options": {
"grid_columns": 12
}
},
"type": {
"title": "Gamedig type",
"description": "Look for the GameDig Type ID in the games list.",
"type": "string",
"options": {
"grid_columns": 6
}
},
"appId": {
"title": "Steam App ID",
"description": "Look for the AppID in the apps list.",
"type": "integer",
"format": "number",
"options": {
"grid_columns": 6
}
},
"host": {
"title": "Host name or IP",
"type": "string",
"options": {
"grid_columns": 6
}
},
"port": {
"title": "Port number",
"type": "integer",
"minimum": 1,
"maximum": 65535,
"format": "number",
"options": {
"grid_columns": 6
}
},
"updateIntervalMinutes": {
"title": "Update interval (minutes) [NOT IMPLEMENTED]",
"type": "integer",
"default": 5,
"minimum": 1,
"maximum": 60,
"format": "range",
"options": {
"grid_columns": 12
}
},
"graphHistoryHours": {
"title": "Graph history time span (hours)",
"type": "integer",
"default": 12,
"minimum": 1,
"maximum": 24,
"format": "range",
"options": {
"grid_columns": 12
}
},
"timezoneOffset": {
"title": "Time zone offset of the server",
"default": 0,
"type": "number",
"minimum": -12,
"maximum": 14,
"format": "range",
"options": {
"grid_columns": 6,
"grid_break": true
}
},
"discord": {
"type": "array",
"title": "Discord channels",
"description": "The simplest way to get a discord channel ID is to enable developer mode in settings, then right clicking on the channel name will give you the Copy ID option.",
"minItems": 0,
"uniqueItems": true,
"items": {
"type": "object",
"title": "Channel",
"required": [
"channelId"
],
"properties": {
"channelId": {
"title": "Channel ID",
"type": "string"
}
}
},
"format": "table",
"options": {
"grid_columns": 12
}
},
"telegram": {
"type": "array",
"title": "Telegram chats",
"description": "The simplest way to get a telegram chat ID is to invite @getidsbot and use /start to make it post (among other data) the chat ID.",
"minItems": 0,
"uniqueItems": true,
"items": {
"type": "object",
"title": "Chat",
"required": [
"chatId"
],
"properties": {
"chatId": {
"type": "string",
"title": "Chat ID"
}
}
},
"format": "table",
"options": {
"grid_columns": 12
}
}
},
"format": "grid",
"headerTemplate": "{{#self.name}}{{self.name}}{{/self.name}}{{^self.name}}{{self.type}}:{{self.host}}:{{self.port}}{{/self.name}}"
}
},
startval: gswConfig.config
});
configEditor.on('ready', () => {
addExportButton();
$('#config-form').submit(e => {
e.preventDefault();
});
$('#spinner').addClass('d-none');
$('#protected-section').removeClass('d-none');
let editorCurrVal = configEditor.getValue();
let formCurrVal = actualFormValues();
$('#config-form input, #config-form textarea').keyup(() => {
unsavedChanges = checkForChanges(formCurrVal);
});
configEditor.on('change', () => {
unsavedChanges = checkForChanges(formCurrVal);
});
$('#save-config-submit').click(async e => {
e.preventDefault();
if (!configEditor.isEnabled()) return;
configEditor.disable();
$(e.currentTarget).addClass('_btn');
const uerr = configEditor.validate();
if (uerr.length) {
notif('warning', 'Validation errors', uerr.map(err => err.path + ' ' + err.message).join('
'), undefined, 3 + uerr.length);
} else {
const res = await fetchApi('POST', 'config', configEditor.getValue());
if (res && res.message) {
editorCurrVal = configEditor.getValue();
formCurrVal = actualFormValues();
unsavedChanges = false;
dismissSaveConfig();
notif('success', '✔️ Changes saved successfully.', undefined, undefined, 3);
} else notif('danger', '⚠️ Error while saving config.', (res ? res.error : undefined), undefined, 3);
}
$(e.currentTarget).removeClass('_btn');
configEditor.enable();
});
$('#save-config-reset').click(e => {
e.preventDefault();
if (!configEditor.isEnabled()) return;
configEditor.setValue(editorCurrVal, true);
unsavedChanges = false;
dismissSaveConfig();
if (formCurrVal !== actualFormValues()) document.location.reload();
});
});
} else notif('danger', '⚠️ Error while loading config.');
// Flush data
$('button[data-api]').click(async e => {
e.preventDefault();
const endpoint = e.currentTarget.dataset.api;
if (confirm('Are you sure you want to call `' + endpoint + '`?')) {
const res = await fetchApi('GET', endpoint);
if (res && res.message) notif('success', res.message, undefined, undefined, 3);
else notif('danger', (res ? res.error : undefined), undefined, undefined, 3);
}
});
}
});
function dismissSaveConfig() {
$('#save-config').addClass('animate__fadeOutDown');
setTimeout(() => {
if ($('#save-config').hasClass('animate__fadeOutDown')) $('#save-config').addClass('d-none');
}, 500);
}
// NOTE: sadly configEditor.getValue() can not be trusted when the
// change event originates from a keypress event
function actualFormValues() {
const formItems = $('#config-form .tab-pane:not([style*="display: none"]) input:enabled, #config-form .tab-pane:not([style*="display: none"]) textarea:enabled, #config-form .tab-pane:not([style*="display: none"]) select:enabled').toArray();
return JSON.stringify(formItems.map(e => {
if (e.type === 'checkbox') return e.checked ? e.value : undefined;
return e.value;
}));
}
function checkForChanges(formCurrVal) {
if (formCurrVal !== actualFormValues()) {
$('#save-config').removeClass('d-none');
$('#save-config').removeClass('animate__fadeOutDown');
return true;
} else {
dismissSaveConfig();
return false;
}
};
function addExportButton() {
const button_holder = configEditor.root.theme.getHeaderButtonHolder();
const exportBtn = configEditor.root.getButton('Export', 'save', 'Export As JSON File');
button_holder.appendChild(exportBtn);
configEditor.root.header.parentNode.insertBefore(button_holder, configEditor.root.header.nextSibling);
exportBtn.addEventListener('click', e => {
e.preventDefault();
const dt = (new Date()).toISOString().slice(0, 19).replace(/\D/g, '');
const filename = dt + '-gsw.config.json';
const blob = new Blob([JSON.stringify(configEditor.getValue(), null, 2)], {
type: 'application/json;charset=utf-8'
});
if (window.navigator && window.navigator.msSaveOrOpenBlob) window.navigator.msSaveOrOpenBlob(blob, filename);
else {
const a = document.createElement('a');
a.download = filename;
a.href = URL.createObjectURL(blob);
a.dataset.downloadurl = ['text/plain', a.download, a.href].join(':');
a.dispatchEvent(new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': false
}));
}
}, false);
}
function logout() {
$(window).off('beforeunload');
$('#save-config').addClass('d-none');
$('#top-menu').addClass('invisible');
$('#protected-section').addClass('d-none');
$('#spinner').addClass('d-none');
$('#bye').removeClass('d-none');
$('body > footer').addClass('d-none');
if (configEditor) configEditor.destroy();
window.localStorage.removeItem('btoken');
}
function notif(type, html1, html2, html3, dismissable) {
let closeTimeout = 0;
if (typeof dismissable === 'number') {
closeTimeout = dismissable;
dismissable = true;
}
const el = $('