138 lines
5.2 KiB
JavaScript
138 lines
5.2 KiB
JavaScript
/* Copyright (C) 2012-2013 Denver Gingerich,
|
|
** Copyright (C) 2013-2014, 2020 Bradley M. Kuhn,
|
|
** Copyright (C) 2016, 2020 Brett Smith.
|
|
** License: GPLv3-or-later
|
|
** Find a copy of GPL at https://sfconservancy.org/GPLv3
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var NEW_AMOUNT_EVENT = 'conservancy:newamount';
|
|
|
|
var flipClass = function(elem, byeClass, hiClass) {
|
|
var classList = elem.classList;
|
|
classList.remove(byeClass);
|
|
classList.add(hiClass);
|
|
}
|
|
|
|
var buildAmountData = function(amountInput) {
|
|
var amountData = {
|
|
minAmount: parseFloat(amountInput.min),
|
|
newAmount: parseFloat(amountInput.value),
|
|
}
|
|
if (amountInput.dataset.oldAmount !== undefined) {
|
|
amountData.oldAmount = parseFloat(amountInput.dataset.oldAmount);
|
|
}
|
|
return amountData;
|
|
}
|
|
|
|
var supportTypeSelector = function(supportTypeHash) {
|
|
return $(supportTypeHash + "Selector");
|
|
};
|
|
|
|
/* We've sometimes published links that say #renew instead of #renewal.
|
|
Rewrite that to work as intended. */
|
|
if (window.location.hash === "#renew") {
|
|
window.location.hash = "#renewal";
|
|
}
|
|
|
|
var formCorrectionNeeded = qs('#form-correction-needed');
|
|
|
|
function init_sustainer_form_validation () {
|
|
// Forms start in "invalid" form, with the errors shown, so that
|
|
// non-Javascript users see the errors by default and know what they must
|
|
// enter. Now we hide those for JavaScript users:
|
|
formCorrectionNeeded.classList.add('hidden');
|
|
|
|
qsa('form.supporter-form').forEach(function(form) {
|
|
var $amountInput = $('input[type=number]', form).first();
|
|
var amountError = qs('.supporter-form-input .form-error', form);
|
|
var $amountError = $('.form-error', $amountInput.parents('.supporter-form-input'));
|
|
|
|
$amountError.on(NEW_AMOUNT_EVENT, function(event, amountData) {
|
|
var isValid = amountData.newAmount >= amountData.minAmount;
|
|
if (amountData.oldAmount === undefined) {
|
|
if (isValid) {
|
|
amountError.classList.add('hidden');
|
|
} else {
|
|
flipClass($amountInput[0], 'valid', 'invalid');
|
|
amountError.classList.remove('hidden');
|
|
}
|
|
} else if (isValid) {
|
|
flipClass($amountInput[0], 'invalid', 'valid');
|
|
hide(amountError);
|
|
} else if (amountData.oldAmount >= amountData.minAmount) {
|
|
flipClass($amountInput[0], 'valid', 'invalid');
|
|
show(amountError);
|
|
}
|
|
});
|
|
|
|
$amountInput.on('input', function(event) {
|
|
event.target.classList.remove('invalid');
|
|
}).on('focusout', function(event) {
|
|
var amountInput = event.target;
|
|
var amountData = buildAmountData(amountInput);
|
|
$amountError.trigger(NEW_AMOUNT_EVENT, amountData);
|
|
amountInput.dataset.oldAmount = amountData.newAmount;
|
|
}).trigger('focusout');
|
|
|
|
form.addEventListener('submit', function(event) {
|
|
var amountData = buildAmountData($amountInput[0]);
|
|
if (amountData.newAmount >= amountData.minAmount) {
|
|
formCorrectionNeeded.classList.add('hidden');
|
|
} else {
|
|
formCorrectionNeeded.classList.remove('hidden')
|
|
.css("font-weight", "bold").css("font-size", "150%");
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function init_sustainer_type_switching () {
|
|
var selectSupportType = function(event) {
|
|
var $selectedLink = $(event.target);
|
|
$(".supporter-type-selector a").removeClass("supporter-type-selector-selected");
|
|
$selectedLink.addClass("supporter-type-selector-selected");
|
|
$(".supporter-type-selection").hide();
|
|
$(event.target.hash).show();
|
|
formCorrectionNeeded.classList.add('hidden');
|
|
return false;
|
|
};
|
|
$(".supporter-type-selector a").bind("click", selectSupportType);
|
|
|
|
var selectSupportTypeFromHash = function() {
|
|
return supportTypeSelector(window.location.hash).click();
|
|
};
|
|
$(window).bind("hashchange", selectSupportTypeFromHash);
|
|
if (selectSupportTypeFromHash().length === 0) {
|
|
supportTypeSelector("#annual").click();
|
|
}
|
|
};
|
|
|
|
function init_expanders () {
|
|
// To avoid hitting visitors with a wall of text, we initially hide the
|
|
// "Year in Review" and similar text inside expandable <details>
|
|
// sections. If the URL fragment references one of these sections, we open
|
|
// that section.
|
|
let details = qs('details' + window.location.hash) // eg. #WritingAndSpeaking
|
|
if (window.location.hash && details) {
|
|
details.open = true;
|
|
details.scrollIntoView();
|
|
}
|
|
// Exable convenient "expand all" link to expand/hide all sections at once.
|
|
qsa('.expander').forEach(function(expander) {
|
|
expander.innerHTML = expander.dataset['expandLinkText'];
|
|
expander.addEventListener('click', function() {
|
|
let details_elements = qsa('.expandable-section details');
|
|
let some_closed = Array.from(details_elements).some(el => !el.open);
|
|
details_elements.forEach(function(el) {
|
|
el.open = some_closed;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
init_sustainer_form_validation();
|
|
init_sustainer_type_switching();
|
|
init_expanders();
|