/**
* When attached to an eventListener of an element, opens or closes element in full screen.
*/
const fullScreenHTML = '';
const notFullScreenHTML = '×';
function toggleFullscreen(realm,screen) {
if (realm.innerHTML == fullScreenHTML){
if (screen.requestFullscreen) {
screen.requestFullscreen();
} else if (screen.webkitRequestFullscreen) { /* Safari */
screen.webkitRequestFullscreen();
} else if (screen.msRequestFullscreen) { /* IE11 */
screen.msRequestFullscreen();
} else if (document.mozRequestFullScreen) { /* Firefox 9-63 */
screen.mozRequestFullScreen();
}
}else{
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) { /* Safari */
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) { /* IE11 */
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) { /* Firefox 9-63 */
document.mozCancelFullScreen();
}
}
}
/**
* Draws out HTML framework and adds functionality to it for a visual and interactive calculator tool.
* @author Ole Brede, Terje Rudi.
*/
async function startPoengKalkulator(){
document.currentScript.insertAdjacentHTML('afterend', `
Poengkalkulator for master- og videreutdanning
`)
const pointCalculator = document.querySelector('#poengKalkulator')
pointCalculator.appendChild(document.currentScript)
pointCalculator.querySelector('summary').insertAdjacentHTML('afterend', `
`)
//Handles changing of fullscreen
if ((document.fullscreenEnabled || document.msFullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled) && (document.webkitFullScreenKeyboardInputAllowed === undefined || document.webkitFullScreenKeyboardInputAllowed === true)) {
function handleFullscreenchange() {
const realm = pointCalculator.querySelector('.menuBtn')
if (document.fullscreenElement || document.msFullscreenElement || document.webkitFullscreenElement || document.webkitIsFullScreen || document.mozFullScreenElement) {
realm.innerHTML = notFullScreenHTML;
realm.parentNode.parentNode.setAttribute('open','open');
} else {
realm.innerHTML = fullScreenHTML;
}
}
pointCalculator.addEventListener('fullscreenchange', () => {
handleFullscreenchange()
})
pointCalculator.addEventListener("webkitfullscreenchange", () => {
handleFullscreenchange()
})
pointCalculator.addEventListener("msfullscreenchange", () => {
handleFullscreenchange()
})
pointCalculator.addEventListener("mozfullscreenchange", () => {
handleFullscreenchange()
});
pointCalculator.querySelector('summary').insertAdjacentHTML('beforeend', `
`)
}
//Handles gradeCalculator
let gradePointsValue = 0
{
const form = document.querySelector('.gradeCalculatorTable')
/**
* Adds a clone of row below row
* @param {object} row htmldomobject
*/
function addGradeInputRow(row) {
if (row.parentElement.querySelectorAll('.gradeInputsRow')[row.parentElement.querySelectorAll('.gradeInputsRow').length - 1] !== row) {return}
clone = row.cloneNode(true)
for (let index = 0; index < clone.querySelectorAll('INPUT').length; index++) {
const input = clone.querySelectorAll('INPUT')[index];
input.value = ''
handleGradeInputsRowInputs(input)
}
for (let index = 0; index < clone.querySelectorAll('OUTPUT').length; index++) {
const element = clone.querySelectorAll('OUTPUT')[index];
element.value = ''
}
row.insertAdjacentElement('afterend', clone)
return clone
}
/**
* calculates individual row and shows results
* @param {object} row htmldomobject
*/
function showCalculationsRow(row) {
const gradeInput = row.querySelector('.gradeInput')
const pointInput = row.querySelector('.pointInput')
const pointValue = row.querySelector('.pointValue')
if (gradeInput.checkValidity() && pointInput.checkValidity() && gradeInput.value !== '' && pointInput.value !== '') {
pointValue.value = (pointInput.value * ((gradeInput.value.toUpperCase().charCodeAt(0) / -1) + 70)).toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 1})
addGradeInputRow(row)
} else {
pointValue.value = ''
}
showCalculationsOverall()
}
/**
* calculates all rows and shows results
*/
function showCalculationsOverall() {
const sumPoints = form.querySelector('.sumPoints > output')
const sumNumxPoints = form.querySelector('.sumNumxPoints > output')
const gradeAvg = form.parentElement.querySelector('.gradeAvg > output')
const gradePoints = pointCalculator.querySelector('.gradePoints')
let sumPointsValue = 0
let sumNumxPointsValue = 0
for (let index = 0; index < form.querySelectorAll('.gradeInputsRow').length; index++) {
const row = form.querySelectorAll('.gradeInputsRow')[index];
const gradeInput = row.querySelector('.gradeInput')
const pointInput = row.querySelector('.pointInput')
const pointValue = row.querySelector('.pointValue')
if (pointInput.value !== '' && pointValue.value !== '') {
sumPointsValue += parseFloat(pointInput.value)
sumNumxPointsValue += pointInput.value * ((gradeInput.value.toUpperCase().charCodeAt(0) / -1) + 70)
}
}
if (sumPointsValue !== 0 || sumNumxPointsValue !== 0) {
sumPoints.value = sumPointsValue.toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 1})
sumNumxPoints.value = sumNumxPointsValue.toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 1})
gradeAvg.value = (sumNumxPointsValue / sumPointsValue).toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 2})
gradePointsValue = (sumNumxPointsValue / sumPointsValue * 10)
gradePoints.value = gradePointsValue.toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 2})
} else {
sumPoints.value = ''
sumNumxPoints.value = ''
gradeAvg.value = ''
gradePointsValue = 0
gradePoints.value = '0'
}
updateCompCalculator()
}
/**
* removes excess input rows
*/
function removeEmptyGradeInput() {
const activeElementClass = document.activeElement.classList.contains('gradeInput') ? 'gradeInput' : document.activeElement.classList.contains('pointInput') ? 'pointInput' : undefined
for (let index = form.querySelectorAll('.gradeInputsRow').length - 2; index >= 0; index--) {
const row = form.querySelectorAll('.gradeInputsRow')[index]
const gradeInput = row.querySelector('.gradeInput')
const pointInput = row.querySelector('.pointInput')
const lastRow = form.querySelectorAll('.gradeInputsRow')[form.querySelectorAll('.gradeInputsRow').length - 1]
const lastGradeInput = lastRow.querySelector('.gradeInput')
const lastPointInput = lastRow.querySelector('.pointInput')
if (lastGradeInput.value !== '' || lastPointInput.value !== '') {return}
if (gradeInput.value === '' && pointInput.value === '') {
if (document.activeElement.parentElement.parentElement === row) {
lastRow.querySelector(`.${activeElementClass}`).select()
}
row.remove()
} else if (gradeInput.value === '' || pointInput.value === '') {
if (document.activeElement.parentElement.parentElement === lastRow) {
row.querySelector(`.${activeElementClass}`).select()
}
lastRow.remove()
return
} else {
return
}
}
}
/**
* Handles all the events for elements with the gradeInputsRow class
* @param {object} element htmldomobject
*/
function handleGradeInputsRowInputs(input) {
input.addEventListener('input', () => {
let row = input.parentElement.parentElement
if (input.classList.contains('gradeInput')) {
if (parseInt(input.value) <= 5 && parseInt(input.value) >= 0) {
input.value = String.fromCharCode((input.value / -1) + 70)
}
if (input.checkValidity() && input.value !== '') {
input.value = input.value.toUpperCase()
row.querySelector('.pointInput').select()
}
}
showCalculationsRow(row)
removeEmptyGradeInput()
})
reportValidityBlur(input)
enterToNextInput(input, form)
}
//Add events to already existing inputs
for (let index = 0; index < form.querySelectorAll('.gradeInputsRow input').length; index++) {
const input = form.querySelectorAll('.gradeInputsRow input')[index];
handleGradeInputsRowInputs(input)
}
}
//Handle eduCalculator
{
const form = document.querySelector('.eduCalculatorTable')
const eduPointInput = form.querySelector('.eduPointInput')
eduPointInput.addEventListener('input', () => {
const eduPointValue = pointCalculator.querySelector('.eduPointValue')
eduPointValue.value = (eduPointInput.checkValidity() && eduPointInput.value !== '0') ? Math.min(Math.floor(eduPointInput.value / 30), 4) : '0'
updateCompCalculator()
})
}
//Handle practiceCalculator
{
const form = document.querySelector('.Praksispoeng')
let rowCount = 1
function addPraksisInputRow(row) {
if (row.parentElement.querySelectorAll('.praksisInputRow')[row.parentElement.querySelectorAll('.praksisInputRow').length - 1] !== row) {return}
clone = row.cloneNode(true)
for (let index = 0; index < clone.querySelectorAll('INPUT').length; index++) {
const input = clone.querySelectorAll('INPUT')[index];
input.value = ''
handlePraksisInputRowInputs(input)
}
if (clone.open) {
clone.open = false
}
rowCount++
clone.querySelector('summary').innerHTML = `Stillingsforhold ${rowCount}`
handlePraksisInputRowDetails(clone)
row.insertAdjacentElement('afterend', clone)
return clone
}
function monthDayCount(date) {
const count = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
//account for leap years
count[1] = (date.getFullYear() % 4 === 0 && (date.getFullYear() % 100 !== 0 || date.getFullYear() % 400 === 0)) ? 29 : count[1]
return count[date.getMonth()]
}
function monthDiff(date1, date2) {
if (date1 > date2) {return 0}
let months = (date2.getFullYear() - date1.getFullYear()) * 12
months -= date1.getMonth()
months += date2.getMonth()
if ((date1.getDate() === date2.getDate()) || ((date1.getDate() / monthDayCount(date1) === 1) && (date2.getDate() / monthDayCount(date2) === 1))) {return months}
if (date1.getDate() > date2.getDate()) {
months -= (date1.getDate() - date2.getDate()) / 31
} else {
months -= ((31 - date2.getDate() + date1.getDate()) / 31) - 1
}
return months
}
//Potentional alternative way of calculating fractional months
/*
function monthDiff(date1, date2) {
if (date1 > date2) {return}
let months = (date2.getFullYear() - date1.getFullYear()) * 12
months -= date1.getMonth()
months += date2.getMonth()
if (date1.getDate() === date2.getDate()) {return months}
if (date1.getDate() > date2.getDate()) {
date2.setMonth(date2.getMonth() - 1)
months -= (date1.getDate() - date2.getDate()) / monthDayCount(date2)
} else {
months += (date2.getDate() - date1.getDate()) / monthDayCount(date2)
}
return months
}
*/
function CalculateRow(row) {
const beginDateInput = row.querySelector('.beginDateInput')
const endDateInput = row.querySelector('.endDateInput')
const percentInput = row.querySelector('.percentInput')
const extraInput = row.querySelector('.extraInput')
let monthsValue = 0
if (beginDateInput.checkValidity() && endDateInput.checkValidity() && percentInput.checkValidity() && beginDateInput.value !== '' && endDateInput.value !== '' && percentInput.value !== '') {
const monthsValueRaw = monthDiff(beginDateInput.valueAsDate, endDateInput.valueAsDate)
const monthsValueAdjusted = monthsValueRaw * percentInput.value / 100
monthsValue = monthsValueAdjusted
if (extraInput.checkValidity() && extraInput.value !== '') {
const extraMonths = extraInput.value / 150
if (extraMonths + monthsValueAdjusted > monthsValueRaw) {
monthsValue = monthsValueRaw
} else {
monthsValue = monthsValue + extraInput.value / 150
}
}
addPraksisInputRow(row)
}
return monthsValue
}
function showCalculationsOverall() {
const sumMonths = form.querySelector('.sumMonths')
const obligatedYears = form.querySelector('.obligatedYears')
const praksisPoints = pointCalculator.querySelector('.praksisPoints')
let sumMonthsValue = 0
for (let index = 0; index < form.querySelectorAll('.praksisInputRow').length; index++) {
const row = form.querySelectorAll('.praksisInputRow')[index];
const monthsValue = CalculateRow(row)
sumMonthsValue += monthsValue
}
sumMonths.value = sumMonthsValue.toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 2})
const monthsSubtract = obligatedYears.checkValidity() && obligatedYears.value !== '' ? obligatedYears.value * 12 : 0
if (sumMonthsValue !== 0 && sumMonthsValue - monthsSubtract > 0 && monthsSubtract > 0) {
praksisPoints.value = Math.min((Math.floor((sumMonthsValue - monthsSubtract) / 12) * 2), 6).toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 0})
} else {
praksisPoints.value = '0'
}
if (sumMonthsValue === 0) {
sumMonths.value = ''
}
updateCompCalculator()
}
function handlePraksisInputRowInputs(input) {
input.addEventListener('input', () => {
showCalculationsOverall()
})
reportValidityBlur(input)
enterToNextInput(input, form)
}
{
const obligatedYears = form.querySelector('.obligatedYears')
obligatedYears.addEventListener('input', () => {
showCalculationsOverall()
})
reportValidityBlur(obligatedYears)
enterToNextInput(obligatedYears, form)
}
function handlePraksisInputRowDetails(details) {
details.addEventListener('toggle', () => {
if (details.open) {
for (let index = 0; index < form.querySelectorAll('.praksisInputRow').length; index++) {
const otherdetails = form.querySelectorAll('.praksisInputRow')[index];
if (details !== otherdetails && otherdetails.open) {
otherdetails.open = false
}
}
}
})
}
//Add events to already existing inputs
for (let index = 0; index < form.querySelectorAll('.praksisInputRow input').length; index++) {
const input = form.querySelectorAll('.praksisInputRow input')[index];
handlePraksisInputRowInputs(input)
}
//Add events to already existing details
for (let index = 0; index < form.querySelectorAll('.praksisInputRow').length; index++) {
const details = form.querySelectorAll('.praksisInputRow')[index];
handlePraksisInputRowDetails(details)
}
}
/**
* report the validity of the element on blur
* @param {object} element htmldomobject
*/
function reportValidityBlur(element) {
element.addEventListener('blur', () => {
element.reportValidity()
})
}
/**
* If enter is pressed then selects next input in form
* If shift + enter is pressed then selects previous input in form
* @param {object} element htmldomobject
*/
function enterToNextInput(element, table) {
element.addEventListener('keypress', (event) => {
if (event.keyCode === 13) {
const inputs = Array.from(table.querySelectorAll('INPUT'))
const next = event.shiftKey ? -1 : 1
const input = inputs[inputs.indexOf(element) + next]
if (input) {
input.select()
}
}
})
}
function updateCompCalculator() {
const compTable = document.querySelector('.compCalculatorTable')
const eduPointValue = parseInt(document.querySelector('.eduPointValue').value)
const praksisPoints = parseInt(document.querySelector('.praksisPoints').value)
const compPoint = compTable.querySelector('.compPoint')
compPoint.value = (gradePointsValue + eduPointValue + praksisPoints).toLocaleString('no-NO', {minimumFractionDigits: 0, maximumFractionDigits: 2})
}
}
startPoengKalkulator()