mirror of
https://github.com/Luzifer/vault-otp-ui.git
synced 2024-11-09 16:50:05 +00:00
189 lines
6 KiB
JavaScript
189 lines
6 KiB
JavaScript
// Generated by CoffeeScript 2.3.1
|
|
(function() {
|
|
var clipboard, createAlert, createOTPItem, currentTimeout, delay, fetchCodes, fetchInProgress, filterChange, initializeApplication, iterationCurrent, iterationNext, preFetch, refreshTimerProgress, serverConnectionError, tick, timeLeft, updateCodes, updatePreFetch;
|
|
|
|
currentTimeout = 0;
|
|
|
|
clipboard = null;
|
|
|
|
preFetch = null;
|
|
|
|
fetchInProgress = false;
|
|
|
|
serverConnectionError = false;
|
|
|
|
iterationCurrent = 'current';
|
|
|
|
iterationNext = 'next';
|
|
|
|
// document-ready function to start Javascript processing
|
|
$(function() {
|
|
if ($('body').hasClass('state-signedin')) {
|
|
return initializeApplication();
|
|
}
|
|
});
|
|
|
|
// createOTPItem generates code entries from the JSON objects passed from the backend
|
|
createOTPItem = function(item) {
|
|
var otpItem, tpl;
|
|
tpl = $('#tpl-otp-item').html();
|
|
otpItem = $(tpl);
|
|
otpItem.find('.badge').text(item.code.replace(/^([0-9]{3})([0-9]{3})$/, '$1 $2').replace(/^([0-9]{2})([0-9]{3})([0-9]{3})$/, '$1 $2 $3'));
|
|
otpItem.find('.title').text(item.name);
|
|
otpItem.find('i.fa').addClass(`fa-${item.icon}`);
|
|
return otpItem.appendTo($('#keylist'));
|
|
};
|
|
|
|
// createAlert adds a colored message at the top of the list
|
|
// type = success / info / warning / danger
|
|
createAlert = function(type, keyword, message, timeout) {
|
|
var alrt, tpl;
|
|
tpl = $('#tpl-message').html();
|
|
alrt = $(tpl);
|
|
alrt.find('.alert').addClass(`alert-${type}`);
|
|
alrt.find('.alert').find('.keyword').text(keyword);
|
|
alrt.find('.alert').find('.message').text(message);
|
|
alrt.appendTo($('#messagecontainer'));
|
|
if (timeout > 0) {
|
|
return delay(timeout, function() {
|
|
return alrt.remove();
|
|
});
|
|
}
|
|
};
|
|
|
|
// delay is a convenience wrapper to swap parameters of setTimeout
|
|
delay = function(delayMSecs, fkt) {
|
|
return window.setTimeout(fkt, delayMSecs);
|
|
};
|
|
|
|
// fetchCodes contacts the backend to receive JSON containing current codes
|
|
fetchCodes = function(iteration) {
|
|
var data, successFunc;
|
|
if (fetchInProgress) {
|
|
return;
|
|
}
|
|
fetchInProgress = true;
|
|
if (iteration === iterationCurrent) {
|
|
successFunc = updateCodes;
|
|
} else {
|
|
successFunc = updatePreFetch;
|
|
}
|
|
if (iteration === iterationCurrent && preFetch !== null) {
|
|
data = preFetch;
|
|
preFetch = null;
|
|
successFunc(data);
|
|
return;
|
|
}
|
|
return $.ajax({
|
|
url: `codes.json?it=${iteration}`,
|
|
success: successFunc,
|
|
dataType: 'json',
|
|
error: function() {
|
|
fetchInProgress = false;
|
|
createAlert('danger', 'Oops.', 'Server could not be contacted. Maybe you (or the server) are offline? Reload to try again.', 0);
|
|
return serverConnectionError = true;
|
|
},
|
|
statusCode: {
|
|
401: function() {
|
|
return window.location.reload();
|
|
},
|
|
500: function() {
|
|
fetchInProgress = false;
|
|
createAlert('danger', 'Oops.', 'The server responded with an internal error. Reload to try again.', 0);
|
|
return serverConnectionError = true;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// filterChange is called when changing the filter field and matches the
|
|
// titles of all shown entries. Those not matching the given regular expression
|
|
// will be hidden. The filterChange function is also called after a successful
|
|
// refresh of the shown codes to re-apply
|
|
filterChange = function() {
|
|
var filter;
|
|
filter = $('#filter').val().toLowerCase();
|
|
return $('.otp-item').each(function(idx, el) {
|
|
if ($(el).find('.title').text().toLowerCase().match(filter) === null) {
|
|
return $(el).hide();
|
|
} else {
|
|
return $(el).show();
|
|
}
|
|
});
|
|
};
|
|
|
|
// initializeApplication initializes some basic events and starts the first
|
|
// polling for codes
|
|
initializeApplication = function() {
|
|
$('#keylist').empty();
|
|
$('#filter').bind('keyup', filterChange);
|
|
tick(500, refreshTimerProgress);
|
|
return fetchCodes(iterationCurrent);
|
|
};
|
|
|
|
// refreshTimerProgress updates the top progressbar to display the
|
|
// remaining time until the one-time-passwords changes
|
|
refreshTimerProgress = function() {
|
|
var secondsLeft;
|
|
secondsLeft = timeLeft();
|
|
$('#timer').css('width', `${secondsLeft / 30 * 100}%`);
|
|
if (secondsLeft < 10 && preFetch === null && !serverConnectionError) {
|
|
// Do a pre-fetch to provide a seamless experience
|
|
return fetchCodes(iterationNext);
|
|
}
|
|
};
|
|
|
|
// tick is a convenience wrapper to swap parameters of setInterval
|
|
tick = function(delay, fkt) {
|
|
return window.setInterval(fkt, delay);
|
|
};
|
|
|
|
// timeLeft calculates the remaining time until codes get invalid
|
|
timeLeft = function() {
|
|
var now;
|
|
now = new Date().getTime();
|
|
return (currentTimeout - now) / 1000;
|
|
};
|
|
|
|
// updateCodes is being called when the backend delivered codes. The codes
|
|
// are then rendered and the clipboard methods are re-bound. Afterwards the
|
|
// next fetchCodes call is timed to that moment when the codes are getting
|
|
// invalid
|
|
updateCodes = function(data) {
|
|
var i, len, ref, token;
|
|
currentTimeout = new Date(data.next_wrap).getTime();
|
|
if (clipboard) {
|
|
clipboard.destroy();
|
|
}
|
|
$('#initLoader').hide();
|
|
$('#keylist').empty();
|
|
ref = data.tokens;
|
|
for (i = 0, len = ref.length; i < len; i++) {
|
|
token = ref[i];
|
|
createOTPItem(token);
|
|
}
|
|
clipboard = new Clipboard('.otp-item', {
|
|
text: function(trigger) {
|
|
return $(trigger).find('.badge').text().replace(/ /g, '');
|
|
}
|
|
});
|
|
clipboard.on('success', function(e) {
|
|
createAlert('success', 'Success:', 'Code copied to clipboard', 1000);
|
|
return e.blur();
|
|
});
|
|
clipboard.on('error', function(e) {
|
|
return createAlert('danger', 'Oops.', 'Copy to clipboard failed', 2000);
|
|
});
|
|
filterChange();
|
|
delay(timeLeft() * 1000, function() {
|
|
return fetchCodes(iterationCurrent);
|
|
});
|
|
return fetchInProgress = false;
|
|
};
|
|
|
|
updatePreFetch = function(data) {
|
|
preFetch = data;
|
|
return fetchInProgress = false;
|
|
};
|
|
|
|
}).call(this);
|