mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-09-03 02:50:11 +00:00
1127 lines
47 KiB
JavaScript
1127 lines
47 KiB
JavaScript
const tabs = {
|
|
invitesEl: document.getElementById('invitesTab'),
|
|
accountsEl: document.getElementById('accountsTab'),
|
|
invitesTabButton: document.getElementById('invitesTabButton'),
|
|
accountsTabButton: document.getElementById('accountsTabButton'),
|
|
invites: function() {
|
|
if (tabs.invitesEl.classList.contains('unfocused')) {
|
|
tabs.accountsEl.classList.add('unfocused');
|
|
tabs.invitesEl.classList.remove('unfocused');
|
|
}
|
|
if (tabs.invitesTabButton.classList.contains("text-muted")) {
|
|
tabs.invitesTabButton.classList.remove("text-muted");
|
|
tabs.accountsTabButton.classList.add("text-muted");
|
|
}
|
|
},
|
|
accounts: function() {
|
|
populateUsers();
|
|
if (tabs.accountsEl.classList.contains('unfocused')) {
|
|
tabs.invitesEl.classList.add('unfocused');
|
|
tabs.accountsEl.classList.remove('unfocused');
|
|
}
|
|
if (tabs.accountsTabButton.classList.contains("text-muted")) {
|
|
tabs.accountsTabButton.classList.remove("text-muted");
|
|
tabs.invitesTabButton.classList.add("text-muted");
|
|
}
|
|
}
|
|
};
|
|
|
|
tabs.invitesTabButton.onclick = tabs.invites;
|
|
tabs.accountsTabButton.onclick = tabs.accounts;
|
|
|
|
tabs.invites();
|
|
|
|
// Used for theme change animation
|
|
function whichTransitionEvent() {
|
|
let t;
|
|
let el = document.createElement('fakeelement');
|
|
let transitions = {
|
|
'transition': 'transitionend',
|
|
'OTransition': 'oTransitionEnd',
|
|
'MozTransition': 'transitionend',
|
|
'WebkitTransition': 'webkitTransitionEnd'
|
|
};
|
|
|
|
for (t in transitions) {
|
|
if (el.style[t] !== undefined) {
|
|
return transitions[t];
|
|
}
|
|
}
|
|
}
|
|
var transitionEndEvent = whichTransitionEvent();
|
|
|
|
// Toggles between light and dark themes
|
|
function toggleCSS() {
|
|
let Els = document.querySelectorAll('link[rel="stylesheet"][type="text/css"]');
|
|
let cssEl = Els[0]
|
|
let remove = false;
|
|
if (Els.length != 1) {
|
|
cssEl = Els[1]
|
|
remove = true
|
|
}
|
|
let href = "bs" + bsVersion;
|
|
if (cssEl.href.includes(href + "-jf")) {
|
|
href += ".css";
|
|
} else {
|
|
href += "-jf.css";
|
|
}
|
|
let newEl = cssEl.cloneNode(true);
|
|
newEl.href = href
|
|
cssEl.parentNode.insertBefore(newEl, cssEl.nextSibling);
|
|
if (remove) {
|
|
Els[0].remove()
|
|
}
|
|
document.cookie = "css=" + href;
|
|
}
|
|
|
|
// Toggles between light and dark themes, but runs animation if necessary (dependent on window width for performance)
|
|
var buttonWidth = 0;
|
|
function toggleCSSAnim(el) {
|
|
let switchToColor = window.getComputedStyle(document.body, null).backgroundColor;
|
|
let maxWidth = 1500;
|
|
if (window.innerWidth < maxWidth) {
|
|
// Calculate minimum radius to cover whole screen
|
|
let radius = Math.sqrt(Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2));
|
|
let currentRadius = el.getBoundingClientRect().width / 2;
|
|
let scale = radius / currentRadius;
|
|
buttonWidth = window.getComputedStyle(el, null).width;
|
|
document.body.classList.remove('smooth-transition');
|
|
el.style.transform = `scale(${scale})`;
|
|
el.style.color = switchToColor;
|
|
el.addEventListener(transitionEndEvent, function() {
|
|
if (this.style.transform.length != 0) {
|
|
toggleCSS();
|
|
this.style.removeProperty('transform');
|
|
document.body.classList.add('smooth-transition');
|
|
};
|
|
}, false);
|
|
} else {
|
|
toggleCSS();
|
|
el.style.color = switchToColor;
|
|
}
|
|
}
|
|
|
|
var buttonColor = "custom";
|
|
// Predefined colors for 'theme' button
|
|
if (cssFile.includes("jf")) {
|
|
buttonColor = "rgb(255,255,255)";
|
|
} else if (cssFile == ('bs' + bsVersion + '.css')) {
|
|
buttonColor = "rgb(16,16,16)";
|
|
}
|
|
|
|
if (buttonColor != "custom") {
|
|
let fakeButton = document.createElement('i');
|
|
fakeButton.classList.add('fa', 'fa-circle', 'circle');
|
|
fakeButton.style = `color: ${buttonColor}; margin-left: 0.4rem;`;
|
|
fakeButton.id = "fakeButton";
|
|
let switchButton = document.createElement('button');
|
|
switchButton.classList.add('btn', 'btn-secondary');
|
|
switchButton.textContent = "Theme";
|
|
switchButton.onclick = function() {
|
|
let fakeButton = document.getElementById('fakeButton');
|
|
toggleCSSAnim(fakeButton);
|
|
};
|
|
let group = document.getElementById('headerButtons');
|
|
switchButton.appendChild(fakeButton);
|
|
group.appendChild(switchButton);
|
|
}
|
|
|
|
|
|
var loginModal = createModal('login');
|
|
var settingsModal = createModal('settingsMenu');
|
|
var userDefaultsModal = createModal('userDefaults');
|
|
var usersModal = createModal('users');
|
|
var restartModal = createModal('restartModal');
|
|
var refreshModal = createModal('refreshModal');
|
|
var aboutModal = createModal('aboutModal');
|
|
var deleteModal = createModal('deleteModal');
|
|
var newUserModal = createModal('newUserModal');
|
|
|
|
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>]
|
|
function parseInvite(invite, empty = false) {
|
|
if (empty) {
|
|
return ["None", "", 1];
|
|
}
|
|
let i = [invite["code"], "", 0, invite["email"]];
|
|
let time = ""
|
|
for (m of ["days", "hours", "minutes"]) {
|
|
if (invite[m] != 0) {
|
|
time += `${invite[m]}${m[0]} `;
|
|
}
|
|
}
|
|
i[1] = `Expires in ${time.slice(0, -1)}`;
|
|
if ('remaining-uses' in invite) {
|
|
i[4] = invite['remaining-uses'];
|
|
}
|
|
if (invite['no-limit']) {
|
|
i[4] = '∞';
|
|
}
|
|
if ('used-by' in invite) {
|
|
i[5] = invite['used-by'];
|
|
} else {
|
|
i[5] = [];
|
|
}
|
|
if ('created' in invite) {
|
|
i[6] = invite['created'];
|
|
}
|
|
if ('notify-expiry' in invite) {
|
|
i[7] = invite['notify-expiry'];
|
|
}
|
|
if ('notify-creation' in invite) {
|
|
i[8] = invite['notify-creation'];
|
|
}
|
|
return i;
|
|
}
|
|
|
|
function addItem(parsedInvite) {
|
|
let links = document.getElementById('invites');
|
|
let itemContainer = document.createElement('div');
|
|
itemContainer.id = parsedInvite[0];
|
|
let listItem = document.createElement('div');
|
|
// listItem.id = parsedInvite[0];
|
|
listItem.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'd-inline-block');
|
|
|
|
let code = document.createElement('div');
|
|
code.classList.add('d-flex', 'align-items-center', 'font-monospace');
|
|
code.setAttribute('style', 'width: 40%;');
|
|
let codeLink = document.createElement('a');
|
|
codeLink.textContent = parsedInvite[0].replace(/-/g, '-');
|
|
|
|
code.appendChild(codeLink);
|
|
|
|
listItem.appendChild(code);
|
|
|
|
let listRight = document.createElement('div');
|
|
listRight.setAttribute('style', 'text-align: right;');
|
|
let listText = document.createElement('span');
|
|
listText.id = parsedInvite[0] + '_expiry';
|
|
listText.setAttribute('style', 'margin-right: 1rem;');
|
|
listText.textContent = parsedInvite[1];
|
|
|
|
listRight.appendChild(listText);
|
|
|
|
if (parsedInvite[2] == 0) {
|
|
let inviteCode = window.location.href.split('#')[0] + 'invite/' + parsedInvite[0];
|
|
//
|
|
codeLink.href = inviteCode;
|
|
codeLink.classList.add('invite-link');
|
|
let copyButton = document.createElement('i');
|
|
copyButton.onclick = function() { toClipboard(inviteCode); };
|
|
copyButton.classList.add('fa', 'fa-clipboard', 'icon-button');
|
|
copyButton.setAttribute('style', 'margin-right: 0.5rem; margin-left: 0.5rem;');
|
|
|
|
code.appendChild(copyButton);
|
|
|
|
if (parsedInvite[3] !== undefined) {
|
|
let sentTo = document.createElement('span');
|
|
sentTo.classList.add('text-muted');
|
|
sentTo.setAttribute('style', 'margin-left: 0.4rem; font-style: italic, font-size: 0.75rem;');
|
|
if (!parsedInvite[3].includes('Failed to send to')) {
|
|
sentTo.textContent = "Sent to ";
|
|
}
|
|
sentTo.textContent += parsedInvite[3];
|
|
|
|
code.appendChild(sentTo);
|
|
}
|
|
|
|
let deleteButton = document.createElement('button');
|
|
deleteButton.onclick = function() { deleteInvite(parsedInvite[0]); };
|
|
deleteButton.classList.add('btn', 'btn-outline-danger');
|
|
deleteButton.textContent = "Delete";
|
|
|
|
let block = document.createElement('div');
|
|
block.setAttribute('style', 'display: inline-block;');
|
|
block.appendChild(deleteButton);
|
|
let dropButton = document.createElement('i');
|
|
dropButton.classList.add('fa', 'fa-angle-down', 'collapsed', 'icon-button', 'not-rotated');
|
|
dropButton.setAttribute('style', 'padding: 1rem; margin: -1rem -1rem -1rem 0;');
|
|
dropButton.setAttribute('data-toggle', 'collapse');
|
|
dropButton.setAttribute('aria-expanded', 'false');
|
|
dropButton.setAttribute('data-target', '#' + CSS.escape(parsedInvite[0]) + '_collapse');
|
|
dropButton.onclick = function() {
|
|
if (this.classList.contains('rotated')) {
|
|
this.classList.remove('rotated');
|
|
this.classList.add('not-rotated');
|
|
} else {
|
|
this.classList.remove('not-rotated');
|
|
this.classList.add('rotated');
|
|
}
|
|
};
|
|
dropButton.setAttribute('style', 'margin-left: 1rem;');
|
|
block.appendChild(dropButton);
|
|
listRight.appendChild(block);
|
|
}
|
|
|
|
listItem.appendChild(listRight);
|
|
itemContainer.appendChild(listItem);
|
|
if (parsedInvite[2] == 0) {
|
|
let itemDropdown = document.createElement('div');
|
|
itemDropdown.id = parsedInvite[0] + '_collapse';
|
|
itemDropdown.classList.add('collapse');
|
|
|
|
let dropdownContent = document.createElement('div');
|
|
dropdownContent.classList.add('container', 'row', 'align-items-start', 'card-body');
|
|
|
|
let dropdownLeft = document.createElement('div');
|
|
dropdownLeft.classList.add('col');
|
|
|
|
let leftList = document.createElement('ul');
|
|
leftList.classList.add('list-group', 'list-group-flush');
|
|
|
|
if (typeof(parsedInvite[6]) != 'undefined') {
|
|
let createdDate = document.createElement('li');
|
|
createdDate.classList.add('list-group-item', 'py-1');
|
|
createdDate.textContent = `Created: ${parsedInvite[6]}`;
|
|
leftList.appendChild(createdDate);
|
|
}
|
|
|
|
let remainingUses = document.createElement('li');
|
|
remainingUses.classList.add('list-group-item', 'py-1');
|
|
remainingUses.id = parsedInvite[0] + '_remainingUses';
|
|
remainingUses.textContent = `Remaining uses: ${parsedInvite[4]}`;
|
|
leftList.appendChild(remainingUses);
|
|
|
|
dropdownLeft.appendChild(leftList);
|
|
dropdownContent.appendChild(dropdownLeft);
|
|
|
|
if (notifications_enabled) {
|
|
let dropdownMiddle = document.createElement('div');
|
|
dropdownMiddle.id = parsedInvite[0] + '_notifyButtons';
|
|
dropdownMiddle.classList.add('col');
|
|
|
|
let middleList = document.createElement('ul');
|
|
middleList.classList.add('list-group', 'list-group-flush');
|
|
middleList.textContent = 'Notify on:';
|
|
|
|
let notifyExpiry = document.createElement('li');
|
|
notifyExpiry.classList.add('list-group-item', 'py-1', 'form-check');
|
|
notifyExpiry.innerHTML = `
|
|
<input class="form-check-input" type="checkbox" value="" id="${parsedInvite[0]}_notifyExpiry">
|
|
<label class="form-check-label" for="${parsedInvite[0]}_notifyExpiry">Expiry</label>
|
|
`;
|
|
if (typeof(parsedInvite[7]) == 'boolean') {
|
|
notifyExpiry.getElementsByTagName('input')[0].checked = parsedInvite[7];
|
|
}
|
|
|
|
notifyExpiry.getElementsByTagName('input')[0].onclick = function() {
|
|
let req = new XMLHttpRequest();
|
|
var thisEl = this;
|
|
let send = {};
|
|
let code = thisEl.id.replace('_notifyExpiry', '');
|
|
send[code] = {};
|
|
send[code]['notify-expiry'] = thisEl.checked;
|
|
req.open("POST", "/setNotify", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4 && this.status != 200) {
|
|
thisEl.checked = !thisEl.checked;
|
|
}
|
|
};
|
|
req.send(JSON.stringify(send));
|
|
};
|
|
middleList.appendChild(notifyExpiry);
|
|
|
|
let notifyCreation = document.createElement('li');
|
|
notifyCreation.classList.add('list-group-item', 'py-1', 'form-check');
|
|
notifyCreation.innerHTML = `
|
|
<input class="form-check-input" type="checkbox" value="" id="${parsedInvite[0]}_notifyCreation">
|
|
<label class="form-check-label" for="${parsedInvite[0]}_notifyCreation">User creation</label>
|
|
`;
|
|
if (typeof(parsedInvite[8]) == 'boolean') {
|
|
notifyCreation.getElementsByTagName('input')[0].checked = parsedInvite[8];
|
|
}
|
|
notifyCreation.getElementsByTagName('input')[0].onclick = function() {
|
|
let req = new XMLHttpRequest();
|
|
var thisEl = this;
|
|
let send = {};
|
|
let code = thisEl.id.replace('_notifyCreation', '');
|
|
send[code] = {};
|
|
send[code]['notify-creation'] = thisEl.checked;
|
|
req.open("POST", "/setNotify", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4 && this.status != 200) {
|
|
thisEl.checked = !thisEl.checked;
|
|
}
|
|
};
|
|
req.send(JSON.stringify(send));
|
|
};
|
|
middleList.appendChild(notifyCreation);
|
|
|
|
dropdownMiddle.appendChild(middleList);
|
|
dropdownContent.appendChild(dropdownMiddle);
|
|
}
|
|
|
|
|
|
let dropdownRight = document.createElement('div');
|
|
dropdownRight.id = parsedInvite[0] + '_usersCreated';
|
|
dropdownRight.classList.add('col');
|
|
if (parsedInvite[5].length != 0) {
|
|
let userList = document.createElement('ul');
|
|
userList.classList.add('list-group', 'list-group-flush');
|
|
userList.innerHTML = '<li class="list-group-item py-1">Users created:</li>';
|
|
for (let user of parsedInvite[5]) {
|
|
let li = document.createElement('li');
|
|
li.classList.add('list-group-item', 'py-1', 'disabled');
|
|
let username = document.createElement('div');
|
|
username.classList.add('d-flex', 'float-left');
|
|
username.textContent = user[0];
|
|
li.appendChild(username);
|
|
let date = document.createElement('div');
|
|
date.classList.add('d-flex', 'float-right');
|
|
date.textContent = user[1];
|
|
li.appendChild(date);
|
|
userList.appendChild(li);
|
|
}
|
|
dropdownRight.appendChild(userList);
|
|
}
|
|
dropdownContent.appendChild(dropdownRight);
|
|
|
|
itemDropdown.appendChild(dropdownContent);
|
|
|
|
itemContainer.appendChild(itemDropdown);
|
|
}
|
|
links.appendChild(itemContainer);
|
|
}
|
|
|
|
function updateInvite(parsedInvite) {
|
|
let expiry = document.getElementById(parsedInvite[0] + '_expiry');
|
|
expiry.textContent = parsedInvite[1];
|
|
|
|
let remainingUses = document.getElementById(parsedInvite[0] + '_remainingUses');
|
|
if (remainingUses) {
|
|
remainingUses.textContent = `Remaining uses: ${parsedInvite[4]}`;
|
|
}
|
|
|
|
if (parsedInvite[5].length != 0) {
|
|
let usersCreated = document.getElementById(parsedInvite[0] + '_usersCreated');
|
|
let dropdownRight = document.createElement('div');
|
|
dropdownRight.id = parsedInvite[0] + '_usersCreated';
|
|
dropdownRight.classList.add('col');
|
|
let userList = document.createElement('ul');
|
|
userList.classList.add('list-group', 'list-group-flush');
|
|
userList.innerHTML = '<li class="list-group-item py-1">Users created:</li>';
|
|
for (let user of parsedInvite[5]) {
|
|
let li = document.createElement('li');
|
|
li.classList.add('list-group-item', 'py-1', 'disabled');
|
|
let username = document.createElement('div');
|
|
username.classList.add('d-flex', 'float-left');
|
|
username.textContent = user[0];
|
|
li.appendChild(username);
|
|
let date = document.createElement('div');
|
|
date.classList.add('d-flex', 'float-right');
|
|
date.textContent = user[1];
|
|
li.appendChild(date);
|
|
userList.appendChild(li);
|
|
}
|
|
dropdownRight.appendChild(userList);
|
|
usersCreated.replaceWith(dropdownRight);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// delete from list on page
|
|
function removeInvite(code) {
|
|
let item = document.getElementById(code);
|
|
item.parentNode.removeChild(item);
|
|
}
|
|
|
|
function generateInvites(empty = false) {
|
|
if (empty === false) {
|
|
let req = new XMLHttpRequest();
|
|
req.open("GET", "/getInvites", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.responseType = 'json';
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
var data = this.response;
|
|
if (data['invites'] == null || data['invites'].length == 0) {
|
|
document.getElementById('invites').textContent = '';
|
|
addItem(parseInvite([], true));
|
|
} else {
|
|
for (let invite of data['invites']) {
|
|
let match = false;
|
|
let items = document.getElementById('invites').children;
|
|
for (let item of items) {
|
|
if (item.id == invite['code']) {
|
|
match = true;
|
|
updateInvite(parseInvite(invite));
|
|
}
|
|
}
|
|
if (match == false) {
|
|
addItem(parseInvite(invite));
|
|
}
|
|
}
|
|
let items = document.getElementById('invites').children;
|
|
for (let item of items) {
|
|
var exists = false;
|
|
for (let invite of data['invites']) {
|
|
if (item.id == invite['code']) {
|
|
exists = true;
|
|
}
|
|
}
|
|
if (exists == false) {
|
|
removeInvite(item.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
req.send();
|
|
} else if (empty === true) {
|
|
document.getElementById('invites').textContent = '';
|
|
addItem(parseInvite([], true));
|
|
}
|
|
}
|
|
|
|
// actually delete invite
|
|
function deleteInvite(code) {
|
|
let send = JSON.stringify({ "code": code });
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", "/deleteInvite", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
generateInvites();
|
|
}
|
|
};
|
|
req.send(send);
|
|
}
|
|
|
|
// Add numbers to select element
|
|
function addOptions(length, selectElement) {
|
|
for (let v = 0; v <= length; v++) {
|
|
let opt = document.createElement('option');
|
|
opt.textContent = v;
|
|
opt.value = v;
|
|
selectElement.appendChild(opt);
|
|
}
|
|
}
|
|
|
|
function toClipboard(str) {
|
|
const el = document.createElement('textarea');
|
|
el.value = str;
|
|
el.setAttribute('readOnly', '');
|
|
el.style.position = 'absolute';
|
|
el.style.left = '-9999px';
|
|
document.body.appendChild(el);
|
|
const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
|
|
el.select();
|
|
document.execCommand('copy');
|
|
document.body.removeChild(el);
|
|
if (selected) {
|
|
document.getSelection().removeAllRanges();
|
|
document.getSelection().addRange(selected);
|
|
}
|
|
}
|
|
|
|
function fixCheckboxes() {
|
|
let send_to_address = [document.getElementById('send_to_address'), document.getElementById('send_to_address_enabled')]
|
|
if (send_to_address[0] != null) {
|
|
send_to_address[0].disabled = !send_to_address[1].checked;
|
|
}
|
|
let multiUseEnabled = document.getElementById('multiUseEnabled');
|
|
let multiUseCount = document.getElementById('multiUseCount');
|
|
let noUseLimit = document.getElementById('noUseLimit');
|
|
multiUseCount.disabled = !multiUseEnabled.checked;
|
|
noUseLimit.checked = false;
|
|
noUseLimit.disabled = !multiUseEnabled.checked;
|
|
}
|
|
|
|
fixCheckboxes();
|
|
|
|
document.getElementById('inviteForm').onsubmit = function() {
|
|
let button = document.getElementById('generateSubmit');
|
|
button.disabled = true;
|
|
button.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
send_object = serializeForm('inviteForm');
|
|
if (!send_object['multiple-uses'] || send_object['no-limit']) {
|
|
delete send_object['remaining-uses'];
|
|
}
|
|
if (document.getElementById('send_to_address') != null) {
|
|
if (send_object['send_to_address_enabled']) {
|
|
send_object['email'] = send_object['send_to_address'];
|
|
delete send_object['send_to_address'];
|
|
delete send_object['send_to_address_enabled'];
|
|
}
|
|
}
|
|
let send = JSON.stringify(send_object);
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", "/generateInvite", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
button.textContent = 'Generate';
|
|
button.disabled = false;
|
|
generateInvites();
|
|
}
|
|
};
|
|
req.send(send);
|
|
return false;
|
|
};
|
|
|
|
function tryLogin(username, password, modal, button, callback) {
|
|
let req = new XMLHttpRequest();
|
|
req.responseType = 'json';
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status != 200) {
|
|
let errormsg = req.response["error"];
|
|
if (errormsg == "") {
|
|
errormsg = "Unknown error"
|
|
}
|
|
if (modal) {
|
|
button.disabled = false;
|
|
button.textContent = errormsg;
|
|
if (!button.classList.contains('btn-danger')) {
|
|
button.classList.add('btn-danger');
|
|
button.classList.remove('btn-primary');
|
|
}
|
|
setTimeout(function () {
|
|
if (button.classList.contains('btn-danger')) {
|
|
button.classList.add('btn-primary');
|
|
button.classList.remove('btn-danger');
|
|
button.textContent = 'Login';
|
|
}
|
|
}, 4000)
|
|
} else {
|
|
loginModal.show();
|
|
}
|
|
} else {
|
|
const data = this.response;
|
|
window.token = data['token'];
|
|
generateInvites();
|
|
const interval = setInterval(function() { generateInvites(); }, 60 * 1000);
|
|
let day = document.getElementById('days');
|
|
addOptions(30, day);
|
|
day.selected = "0";
|
|
let hour = document.getElementById('hours');
|
|
addOptions(24, hour);
|
|
hour.selected = "0";
|
|
let minutes = document.getElementById('minutes');
|
|
addOptions(59, minutes);
|
|
minutes.selected = "30";
|
|
checkDuration();
|
|
if (modal) {
|
|
loginModal.hide();
|
|
}
|
|
document.getElementById('logoutButton').setAttribute('style', '');
|
|
}
|
|
if (typeof callback === "function") {
|
|
callback(this.status);
|
|
}
|
|
}
|
|
};
|
|
req.open("GET", "/getToken", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));
|
|
req.send();
|
|
}
|
|
|
|
document.getElementById('loginForm').onsubmit = function() {
|
|
window.token = "";
|
|
let details = serializeForm('loginForm');
|
|
// let errorArea = document.getElementById('loginErrorArea');
|
|
// errorArea.textContent = '';
|
|
let button = document.getElementById('loginSubmit');
|
|
if (button.classList.contains('btn-danger')) {
|
|
button.classList.add('btn-primary');
|
|
button.classList.remove('btn-danger');
|
|
}
|
|
button.disabled = true;
|
|
button.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
tryLogin(username = details['username'], password = details['password'], modal = true, button = button)
|
|
return false;
|
|
};
|
|
|
|
document.getElementById('openAbout').onclick = function() {
|
|
settingsModal.hide();
|
|
aboutModal.show();
|
|
};
|
|
|
|
document.getElementById('openDefaultsWizard').onclick = function() {
|
|
this.disabled = true
|
|
this.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
let req = new XMLHttpRequest();
|
|
req.responseType = 'json';
|
|
req.open("GET", "/getUsers", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status == 200) {
|
|
let users = req.response['users'];
|
|
let radioList = document.getElementById('defaultUserRadios');
|
|
radioList.textContent = '';
|
|
let first = true;
|
|
for (user of users) {
|
|
let radio = document.createElement('div');
|
|
radio.classList.add('radio');
|
|
let checked = 'checked';
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
checked = '';
|
|
}
|
|
radio.innerHTML =
|
|
`<label><input type="radio" name="defaultRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
|
radioList.appendChild(radio);
|
|
}
|
|
let button = document.getElementById('openDefaultsWizard');
|
|
button.disabled = false;
|
|
button.innerHTML = 'New User Defaults <i class="fa fa-user settingIcon"></i>';
|
|
let submitButton = document.getElementById('storeDefaults');
|
|
submitButton.disabled = false;
|
|
submitButton.textContent = 'Submit';
|
|
if (submitButton.classList.contains('btn-success')) {
|
|
submitButton.classList.remove('btn-success');
|
|
submitButton.classList.add('btn-primary');
|
|
} else if (submitButton.classList.contains('btn-danger')) {
|
|
submitButton.classList.remove('btn-danger');
|
|
submitButton.classList.add('btn-primary');
|
|
}
|
|
settingsModal.hide();
|
|
document.getElementById('defaultsTitle').textContent = `New user defaults`;
|
|
document.getElementById('userDefaultsDescription').textContent = `
|
|
Create an account and configure it to your liking, then choose it from below to store the settings as a template for all new users.`;
|
|
document.getElementById('storeHomescreenLabel').textContent = `Store homescreen layout`;
|
|
document.getElementById('defaultsSource').value = 'fromUser';
|
|
document.getElementById('defaultsSourceSection').classList.add('unfocused');
|
|
document.getElementById('storeDefaults').onclick = function() {
|
|
storeDefaults('all');
|
|
};
|
|
const list = document.getElementById('defaultUserRadios');
|
|
if (list.classList.contains('unfocused')) {
|
|
list.classList.remove('unfocused');
|
|
}
|
|
userDefaultsModal.show();
|
|
}
|
|
}
|
|
};
|
|
req.send();
|
|
};
|
|
|
|
function storeDefaults(users) {
|
|
this.disabled = true;
|
|
this.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
let button = document.getElementById('storeDefaults');
|
|
let radios = document.getElementsByName('defaultRadios');
|
|
let id = '';
|
|
for (let radio of radios) {
|
|
if (radio.checked) {
|
|
id = radio.id.replace('default_', '');
|
|
break;
|
|
}
|
|
}
|
|
let route = '/setDefaults';
|
|
let data = {
|
|
'from': 'user',
|
|
'id': id,
|
|
'homescreen': false
|
|
};
|
|
if (document.getElementById('defaultsSource').value == 'userTemplate') {
|
|
data['from'] = 'template';
|
|
}
|
|
if (users != 'all') {
|
|
data['apply_to'] = users;
|
|
route = '/applySettings';
|
|
}
|
|
if (document.getElementById('storeDefaultHomescreen').checked) {
|
|
data['homescreen'] = true;
|
|
}
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", route, true);
|
|
req.responseType = 'json';
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status == 200 || this.status == 204) {
|
|
button.textContent = "Success";
|
|
if (button.classList.contains('btn-danger')) {
|
|
button.classList.remove('btn-danger');
|
|
} else if (button.classList.contains('btn-primary')) {
|
|
button.classList.remove('btn-primary');
|
|
}
|
|
button.classList.add('btn-success');
|
|
button.disabled = false;
|
|
setTimeout(function() {
|
|
let button = document.getElementById('storeDefaults');
|
|
button.textContent = "Submit";
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-primary');
|
|
button.disabled = false;
|
|
userDefaultsModal.hide();
|
|
}, 1000);
|
|
} else {
|
|
if ("error" in req.response) {
|
|
button.textContent = req.response["error"];
|
|
} else if (("policy" in req.response) || ("homescreen" in req.response)) {
|
|
button.textContent = "Failed (Check JS Console)";
|
|
} else {
|
|
button.textContent = "Failed";
|
|
}
|
|
button.classList.remove('btn-primary');
|
|
button.classList.add('btn-danger');
|
|
setTimeout(function() {
|
|
let button = document.getElementById('storeDefaults');
|
|
button.textContent = "Submit";
|
|
button.classList.remove('btn-danger');
|
|
button.classList.add('btn-primary');
|
|
button.disabled = false;
|
|
}, 1000);
|
|
}
|
|
}
|
|
};
|
|
req.send(JSON.stringify(data));
|
|
};
|
|
|
|
var ombiDefaultsModal = '';
|
|
if (ombiEnabled) {
|
|
ombiDefaultsModal = createModal('ombiDefaults');
|
|
document.getElementById('openOmbiDefaults').onclick = function() {
|
|
this.disabled = true;
|
|
this.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
let req = new XMLHttpRequest();
|
|
req.responseType = 'json';
|
|
req.open("GET", "/getOmbiUsers", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status == 200) {
|
|
let users = req.response['users'];
|
|
let radioList = document.getElementById('ombiUserRadios');
|
|
radioList.textContent = '';
|
|
let first = true;
|
|
// name and id
|
|
for (user of users) {
|
|
let radio = document.createElement('div');
|
|
radio.classList.add('radio');
|
|
let checked = 'checked';
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
checked = '';
|
|
}
|
|
radio.innerHTML =
|
|
`<label><input type="radio" name="ombiRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
|
radioList.appendChild(radio);
|
|
}
|
|
let button = document.getElementById('openOmbiDefaults');
|
|
button.disabled = false;
|
|
button.innerHTML = 'Ombi User Defaults <i class="fa fa-chain-broken settingIcon"></i>';
|
|
let submitButton = document.getElementById('storeOmbiDefaults');
|
|
submitButton.disabled = false;
|
|
submitButton.textContent = 'Submit';
|
|
if (submitButton.classList.contains('btn-success')) {
|
|
submitButton.classList.remove('btn-success');
|
|
submitButton.classList.add('btn-primary');
|
|
} else if (submitButton.classList.contains('btn-danger')) {
|
|
submitButton.classList.remove('btn-danger');
|
|
submitButton.classList.add('btn-primary');
|
|
}
|
|
settingsModal.hide();
|
|
ombiDefaultsModal.show();
|
|
}
|
|
}
|
|
};
|
|
req.send();
|
|
};
|
|
document.getElementById('storeOmbiDefaults').onclick = function() {
|
|
this.disabled = true;
|
|
this.innerHTML =
|
|
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
|
|
'Loading...';
|
|
let button = document.getElementById('storeOmbiDefaults');
|
|
let radios = document.getElementsByName('ombiRadios');
|
|
for (let radio of radios) {
|
|
if (radio.checked) {
|
|
let data = {
|
|
'id': radio.id.slice(8),
|
|
};
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", "/setOmbiDefaults", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status == 200 || this.status == 204) {
|
|
button.textContent = "Success";
|
|
if (button.classList.contains('btn-danger')) {
|
|
button.classList.remove('btn-danger');
|
|
} else if (button.classList.contains('btn-primary')) {
|
|
button.classList.remove('btn-primary');
|
|
}
|
|
button.classList.add('btn-success');
|
|
button.disabled = false;
|
|
setTimeout(function() { ombiDefaultsModal.hide(); }, 1000);
|
|
} else {
|
|
button.textContent = "Failed";
|
|
button.classList.remove('btn-primary');
|
|
button.classList.add('btn-danger');
|
|
setTimeout(function() {
|
|
let button = document.getElementById('storeOmbiDefaults');
|
|
button.textContent = "Submit";
|
|
button.classList.remove('btn-danger');
|
|
button.classList.add('btn-primary');
|
|
button.disabled = false;
|
|
}, 1000);
|
|
}
|
|
}
|
|
};
|
|
req.send(JSON.stringify(data));
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
generateInvites(empty = true);
|
|
|
|
tryLogin("", "", false, callback = function(code){
|
|
console.log(code);
|
|
if (code != 200) {
|
|
loginModal.show();
|
|
}
|
|
});
|
|
|
|
document.getElementById('logoutButton').onclick = function () {
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", "/logout", true);
|
|
req.responseType = 'json';
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4 && this.status == 200) {
|
|
window.token = '';
|
|
location.reload();
|
|
return false;
|
|
}
|
|
};
|
|
req.send();
|
|
}
|
|
|
|
var config = {};
|
|
var modifiedConfig = {};
|
|
|
|
document.getElementById('openSettings').onclick = function () {
|
|
let req = new XMLHttpRequest();
|
|
req.open("GET", "/getConfig", true);
|
|
req.responseType = 'json';
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4 && this.status == 200) {
|
|
let settingsList = document.getElementById('settingsList');
|
|
settingsList.textContent = '';
|
|
config = this.response;
|
|
for (let section of config["order"]) {
|
|
let sectionCollapse = document.createElement('div');
|
|
sectionCollapse.classList.add('collapse');
|
|
sectionCollapse.id = section;
|
|
|
|
let sectionTitle = config[section]['meta']['name'];
|
|
let sectionDescription = config[section]['meta']['description'];
|
|
let entryListID = section + '_entryList';
|
|
let sectionFooter = section + '_footer';
|
|
|
|
let innerCollapse = `
|
|
<div class="card card-body">
|
|
<small class="text-muted">${sectionDescription}</small>
|
|
<div class="${entryListID}">
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
sectionCollapse.innerHTML = innerCollapse;
|
|
|
|
for (var entry of config[section]["order"]) {
|
|
if (entry != 'meta') {
|
|
let entryName = config[section][entry]['name'];
|
|
let required = false;
|
|
if (config[section][entry]['required']) {
|
|
entryName += ' <sup class="text-danger">*</sup>';
|
|
required = true;
|
|
}
|
|
if (config[section][entry]['requires_restart']) {
|
|
entryName += ' <sup class="text-danger">R</sup>';
|
|
}
|
|
if (config[section][entry].hasOwnProperty('description')) {
|
|
let tooltip = `
|
|
<a class="text-muted" href="#" data-toggle="tooltip" data-placement="right" title="${config[section][entry]['description']}"><i class="fa fa-question-circle-o"></i></a>
|
|
`;
|
|
entryName += ' ';
|
|
entryName += tooltip;
|
|
};
|
|
let entryValue = config[section][entry]['value'];
|
|
let entryType = config[section][entry]['type'];
|
|
let entryGroup = document.createElement('div');
|
|
if (entryType == 'bool') {
|
|
entryGroup.classList.add('form-check');
|
|
if (entryValue.toString() == 'true') {
|
|
var checked = true;
|
|
} else {
|
|
var checked = false;
|
|
}
|
|
entryGroup.innerHTML = `
|
|
<input class="form-check-input" type="checkbox" value="" id="${section}_${entry}">
|
|
<label class="form-check-label" for="${section}_${entry}">${entryName}</label>
|
|
`;
|
|
entryGroup.getElementsByClassName('form-check-input')[0].required = required;
|
|
entryGroup.getElementsByClassName('form-check-input')[0].checked = checked;
|
|
entryGroup.getElementsByClassName('form-check-input')[0].onclick = function() {
|
|
var state = this.checked;
|
|
for (var sect of Object.keys(config)) {
|
|
for (var ent of Object.keys(config[sect])) {
|
|
if ((sect + '_' + config[sect][ent]['depends_true']) == this.id) {
|
|
document.getElementById(sect + '_' + ent).disabled = !state;
|
|
} else if ((sect + '_' + config[sect][ent]['depends_false']) == this.id) {
|
|
document.getElementById(sect + '_' + ent).disabled = state;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
} else if ((entryType == 'text') || (entryType == 'email') || (entryType == 'password') || (entryType == 'number')) {
|
|
entryGroup.classList.add('form-group');
|
|
entryGroup.innerHTML = `
|
|
<label for="${section}_${entry}">${entryName}</label>
|
|
<input type="${entryType}" class="form-control" id="${section}_${entry}" aria-describedby="${entry}" value="${entryValue}">
|
|
`;
|
|
entryGroup.getElementsByClassName('form-control')[0].required = required;
|
|
} else if (entryType == 'select') {
|
|
entryGroup.classList.add('form-group');
|
|
let entryOptions = config[section][entry]['options'];
|
|
let innerGroup = `
|
|
<label for="${section}_${entry}">${entryName}</label>
|
|
<select class="form-control" id="${section}_${entry}">
|
|
`;
|
|
for (let entryOption of entryOptions) {
|
|
if (entryOption == entryValue) {
|
|
var selected = 'selected';
|
|
} else {
|
|
var selected = '';
|
|
}
|
|
innerGroup += `
|
|
<option value="${entryOption}" ${selected}>${entryOption}</option>
|
|
`;
|
|
}
|
|
innerGroup += '</select>';
|
|
entryGroup.innerHTML = innerGroup;
|
|
entryGroup.getElementsByClassName('form-control')[0].required = required;
|
|
|
|
}
|
|
sectionCollapse.getElementsByClassName(entryListID)[0].appendChild(entryGroup);
|
|
}
|
|
}
|
|
let sectionButton = document.createElement('button');
|
|
sectionButton.setAttribute('type', 'button');
|
|
sectionButton.classList.add('list-group-item', 'list-group-item-action');
|
|
sectionButton.appendChild(document.createTextNode(sectionTitle));
|
|
sectionButton.id = section + '_button';
|
|
sectionButton.setAttribute('data-toggle', 'collapse');
|
|
sectionButton.setAttribute('data-target', '#' + section);
|
|
settingsList.appendChild(sectionButton);
|
|
settingsList.appendChild(sectionCollapse);
|
|
}
|
|
}
|
|
};
|
|
req.send();
|
|
settingsModal.show();
|
|
}
|
|
|
|
triggerTooltips();
|
|
|
|
function sendConfig(modalId, restart = false) {
|
|
let modal = document.getElementById(modalId);
|
|
modifiedConfig['restart-program'] = false;
|
|
if (restart) {
|
|
modifiedConfig['restart-program'] = true;
|
|
}
|
|
let send = JSON.stringify(modifiedConfig);
|
|
let req = new XMLHttpRequest();
|
|
req.open("POST", "/modifyConfig", true);
|
|
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
req.onreadystatechange = function() {
|
|
if (this.readyState == 4) {
|
|
if (this.status == 200 || this.status == 204) {
|
|
createModal(modalId, true).hide();
|
|
if (modalId != 'settingsMenu') {
|
|
settingsModal.hide();
|
|
}
|
|
} else if (restart) {
|
|
refreshModal.show();
|
|
}
|
|
}
|
|
};
|
|
req.send(send);
|
|
}
|
|
|
|
document.getElementById('settingsSave').onclick = function() {
|
|
modifiedConfig = {};
|
|
var restart_setting_changed = false;
|
|
var settings_changed = false;
|
|
|
|
for (let section of config["order"]) {
|
|
for (let entry of config[section]["order"]) {
|
|
if (entry != 'meta') {
|
|
let entryID = section + '_' + entry;
|
|
let el = document.getElementById(entryID);
|
|
if (el.type == 'checkbox') {
|
|
var value = el.checked.toString();
|
|
} else {
|
|
var value = el.value.toString();
|
|
}
|
|
if (value != config[section][entry]['value'].toString()) {
|
|
if (!modifiedConfig.hasOwnProperty(section)) {
|
|
modifiedConfig[section] = {};
|
|
}
|
|
modifiedConfig[section][entry] = value;
|
|
settings_changed = true;
|
|
if (config[section][entry]['requires_restart']) {
|
|
restart_setting_changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (restart_setting_changed) {
|
|
document.getElementById('applyRestarts').onclick = function(){ sendConfig('restartModal'); };
|
|
let restartButton = document.getElementById('applyAndRestart')
|
|
if (restartButton) {
|
|
restartButton.onclick = function(){ sendConfig('restartModal', restart=true); };
|
|
}
|
|
settingsModal.hide();
|
|
restartModal.show();
|
|
} else if (settings_changed) {
|
|
sendConfig('settingsMenu');
|
|
} else {
|
|
settingsModal.hide();
|
|
}
|
|
}
|
|
|
|
// Diable 'Generate' button if days, hours, minutes are all zero
|
|
function checkDuration() {
|
|
let boxVals = [document.getElementById("days").value, document.getElementById("hours").value, document.getElementById("minutes").value];
|
|
let submit = document.getElementById("generateSubmit");
|
|
if (boxVals[0] != 0 || boxVals[1] != 0 || boxVals[2] != 0) {
|
|
submit.disabled = false;
|
|
} else if (boxVals[0] == 0 && boxVals[1] == 0 && boxVals[2] == 0) {
|
|
submit.disabled = true;
|
|
}
|
|
}
|
|
|
|
for (i of ["days", "hours", "minutes"]) {
|
|
document.getElementById(i).addEventListener("change", checkDuration);
|
|
}
|