function parseJson(str) {
let result;
try {
result = JSON.parse(str);
} catch (exc) {
result = null;
}
return result;
}
function getDevaluationFactorFromRange(formControl, range) {
let factor = 0;
range = parseJson(range);
if (range !== null) {
const controlValue = parseFloat(formControl.value);
Object
.entries(range)
.some(([devaluationKey, { min, max }]) => {
let isStopIteration = false;
if (
(controlValue >= parseFloat(min)) &&
(controlValue <= parseFloat(max))
) {
factor = parseFloat(devaluationKey);
isStopIteration = true;
}
return isStopIteration
});
}
return Number.isFinite(factor) ? factor : 0;
}
function getDevaluationFactor(formControl) {
const { dataset } = formControl;
let rawRange = dataset.devaluationRange ?? null;
let rawFactor = dataset.devaluationFactor ?? null;
let factor = (rawRange !== null)
? getDevaluationFactorFromRange(formControl, rawRange)
: 0;
factor = (
(factor === 0) && (rawFactor !== null) && parseFloat(rawFactor)
) || factor;
factor = Number.isFinite(factor) ? factor : 0;
if (factor !== 0) {
const { type } = formControl;
if ((type === 'radio') || (type === 'checkbox')) {
factor = formControl.checked ? factor : 0;
}
}
return factor;
}
function computeCurrentFee(rootNode, elmFee, baseFee) {
return Array
// array from `HTMLFormControlsCollection`
.from(rootNode.elements)
// calculate currrent fee from each form element's data
.reduce((currentFee, formControl) => {
return currentFee - (baseFee * getDevaluationFactor(formControl));
}, baseFee);
}
function updateCurrentValueAtBoundFeeContext(/*evt*/) {
const { rootNode, elmFee, baseFee } = this;
elmFee.value = computeCurrentFee(rootNode, elmFee, baseFee);
}
function displayCurrentValueAtBoundAgeContext(/*evt*/) {
const { elmRange, elmOutput } = this;
elmOutput.value = elmRange.value;
}
function initializeCurrentAgeDisplay(rootNode) {
const ageNode = rootNode.querySelector('[data-age-range]');
if (ageNode) {
const elmRange = ageNode.querySelector('input[type="range"]');
const elmOutput = ageNode.querySelector('output');
if (elmRange && elmOutput) {
const target = { elmRange, elmOutput };
const boundContextHandler =
displayCurrentValueAtBoundAgeContext.bind(target);
elmRange.addEventListener('input', boundContextHandler);
rootNode.addEventListener('reset', () =>
// decouple custom dom refresh from the system's one.
setTimeout(boundContextHandler, 0)
);
// display initial age value.
// // displayCurrentValueAtBoundAgeContext.call(target);
boundContextHandler();
}
}
}
function initializeMembershipFeeCalculator(rootNode) {
const DEFAULT_BASE_FEE = 10;
initializeCurrentAgeDisplay(rootNode);
const elmFeeValue = rootNode.querySelector('[data-fee-value]');
if (elmFeeValue) {
const baseFee = parseFloat(rootNode.dataset.baseFee);
const target = {
rootNode,
elmFee: elmFeeValue,
baseFee: Number.isFinite(baseFee) ? baseFee : DEFAULT_BASE_FEE,
};
const boundContextHandler =
updateCurrentValueAtBoundFeeContext.bind(target);
rootNode.addEventListener('input', boundContextHandler);
rootNode.addEventListener('reset', () =>
// decouple custom dom refresh from the system's one.
setTimeout(boundContextHandler, 0)
);
// compute initial fee value.
// // updateCurrentValueAtBoundFeeContext.call(target);
boundContextHandler();
rootNode.addEventListener('submit', evt => {
evt.preventDefault();
return false;
});
}
}
function main() {
document
.querySelectorAll('form[data-membership-fee-calculator]')
.forEach(initializeMembershipFeeCalculator);
}
main();
body, form {
margin: 0;
padding: 0;
}
form {
float: left;
width: 50%;
margin-top: -2px;
}
fieldset {
position: relative;
margin: 0 0 2px 0;
padding: 0 10px 2px 10px;
}
fieldset p {
margin: 1px 0 2px 0;
}
fieldset output {
color: #333;
font-weight: bolder;
}
label {
display: inline-block;
}
input[type="range"] {
width: 70%;
}
[data-age-range] output {
display: inline-block;
overflow: hidden;
max-width: 25%;
max-height: 1.2em;
position: relative;
top: 1px;
}
[type="reset"] {
position: absolute;
right: 4px;
top: -4px;
}
<form data-membership-fee-calculator data-base-fee="10">
<fieldset data-age-range>
<legend>
Age
</legend>
<input
type="range"
name="age" id="age"
value="50" min="1" max="100"
data-devaluation-range='{"0.1":{"min":60,"max":80}}'
/>
<output for="age">### not yet computed ###</output>
</fieldset>
<fieldset>
<p>
Do you have any long-term medical conditions
that can affect daily life?
</p>
<label>
<input
type="radio"
name="status"
value="yes"
data-devaluation-factor="0.4"
/>
<span class="label-copy">
Yes
</span>
</label>
<label>
<input type="radio" name="status" value="no" />
<span class="label-copy">
No
</span>
</label>
</fieldset>
<fieldset>
<p>
Are you currently employed?
</p>
<label>
<input
type="radio"
name="empstatus"
value="yes"
data-devaluation-factor="0.3"
/>
<span class="label-copy">
Yes
</span>
</label>
<label>
<input type="radio" name="empstatus" value="no" />
<span class="label-copy">
No
</span>
</label>
</fieldset>
<fieldset>
<legend>
Membership Fee
</legend>
<label>
<span class="label-copy">
Total Fee:
</span>
<output data-fee-value>### not yet computed ###</output>
</label>
<button type="reset">Restore base fee</button>
</fieldset>
</form>
<form data-membership-fee-calculator data-base-fee="20">
<fieldset data-age-range>
<legend>
Age
</legend>
<input
type="range"
name="age" id="age"
value="21" min="1" max="100"
data-devaluation-range=
'{"0.05":{"min":60,"max":69},"0.1":{"min":70,"max":79},"0.2":{"min":80,"max":120}}'
/>
<output for="age">### not yet computed ###</output>
</fieldset>
<fieldset>
<p>
Do you have any long-term medical conditions
that can affect daily life?
</p>
<label>
<input
type="radio"
name="status"
value="yes"
data-devaluation-factor="0.3"
/>
<span class="label-copy">
Yes
</span>
</label>
<label>
<input type="radio" name="status" value="no" />
<span class="label-copy">
No
</span>
</label>
</fieldset>
<fieldset>
<p>
Are you currently employed?
</p>
<label>
<input
type="radio"
name="empstatus"
value="yes"
data-devaluation-factor="0.3"
/>
<span class="label-copy">
Yes
</span>
</label>
<label>
<input type="radio" name="empstatus" value="no" />
<span class="label-copy">
No
</span>
</label>
</fieldset>
<fieldset>
<legend>
Membership Fee
</legend>
<label>
<span class="label-copy">
Total Fee:
</span>
<output data-fee-value>### not yet computed ###</output>
</label>
<button type="reset">Restore base fee</button>
</fieldset>
</form>