@atlassian/aui
Version:
Atlassian User Interface Framework
425 lines (345 loc) • 14.5 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['module', 'exports', './jquery', './internal/amdify', './internal/deprecation', './internal/globalize', './internal/skate', './form-validation/validator-register', './form-notification', './form-validation/basic-validators'], factory);
} else if (typeof exports !== "undefined") {
factory(module, exports, require('./jquery'), require('./internal/amdify'), require('./internal/deprecation'), require('./internal/globalize'), require('./internal/skate'), require('./form-validation/validator-register'), require('./form-notification'), require('./form-validation/basic-validators'));
} else {
var mod = {
exports: {}
};
factory(mod, mod.exports, global.jquery, global.amdify, global.deprecation, global.globalize, global.skate, global.validatorRegister, global.formNotification, global.basicValidators);
global.formValidation = mod.exports;
}
})(this, function (module, exports, _jquery, _amdify, _deprecation, _globalize, _skate, _validatorRegister) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _amdify2 = _interopRequireDefault(_amdify);
var deprecate = _interopRequireWildcard(_deprecation);
var _globalize2 = _interopRequireDefault(_globalize);
var _skate2 = _interopRequireDefault(_skate);
var _validatorRegister2 = _interopRequireDefault(_validatorRegister);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//Attributes
var ATTRIBUTE_VALIDATION_OPTION_PREFIX = 'aui-validation-';
var ATTRIBUTE_NOTIFICATION_PREFIX = 'data-aui-notification-';
var ATTRIBUTE_FIELD_STATE = 'aui-validation-state';
var INVALID = 'invalid';
var VALID = 'valid';
var VALIDATING = 'validating';
var UNVALIDATED = 'unvalidated';
var ATTRIBUTE_VALIDATION_FIELD_COMPONENT = 'data-aui-validation-field';
//Classes
var CLASS_VALIDATION_INITIALISED = '_aui-form-validation-initialised';
//Events
var EVENT_FIELD_STATE_CHANGED = '_aui-internal-field-state-changed';
function isFieldInitialised($field) {
return $field.hasClass(CLASS_VALIDATION_INITIALISED);
}
function initValidation($field) {
if (!isFieldInitialised($field)) {
initialiseDisplayField($field);
prepareFieldMarkup($field);
bindFieldEvents($field);
changeFieldState($field, UNVALIDATED);
}
}
function initialiseDisplayField($field) {
getDisplayField($field).attr('data-aui-notification-field', '');
}
function prepareFieldMarkup($field) {
$field.addClass(CLASS_VALIDATION_INITIALISED);
}
function bindFieldEvents($field) {
bindStopTypingEvent($field);
bindValidationEvent($field);
}
function bindStopTypingEvent($field) {
var keyUpTimer;
var triggerStopTypingEvent = function triggerStopTypingEvent() {
$field.trigger('aui-stop-typing');
};
$field.on('keyup', function () {
clearTimeout(keyUpTimer);
keyUpTimer = setTimeout(triggerStopTypingEvent, 1500);
});
}
function bindValidationEvent($field) {
var validateWhen = getValidationOption($field, 'when');
var watchedFieldID = getValidationOption($field, 'watchfield');
var elementsToWatch = watchedFieldID ? $field.add('#' + watchedFieldID) : $field;
elementsToWatch.on(validateWhen, function startValidation() {
validationTriggeredHandler($field);
});
}
function validationTriggeredHandler($field) {
var noValidate = getValidationOption($field, 'novalidate');
if (noValidate) {
changeFieldState($field, VALID);
return;
}
return startValidating($field);
}
function getValidationOption($field, option) {
var defaults = {
'when': 'change'
};
var optionValue = $field.attr('data-' + ATTRIBUTE_VALIDATION_OPTION_PREFIX + option);
if (!optionValue) {
optionValue = defaults[option];
}
return optionValue;
}
function startValidating($field) {
clearFieldMessages($field);
var validatorsToRun = getActivatedValidators($field);
changeFieldState($field, VALIDATING);
var deferreds = runValidatorsAndGetDeferred($field, validatorsToRun);
var fieldValidators = _jquery2.default.when.apply(_jquery2.default, deferreds);
fieldValidators.done(function () {
changeFieldState($field, VALID);
});
return fieldValidators;
}
function clearFieldMessages($field) {
setFieldNotification(getDisplayField($field), 'none');
}
function getValidators() {
return _validatorRegister2.default.validators();
}
function getActivatedValidators($field) {
var callList = [];
getValidators().forEach(function (validator, index) {
var validatorTrigger = validator.validatorTrigger;
var runThisValidator = $field.is(validatorTrigger);
if (runThisValidator) {
callList.push(index);
}
});
return callList;
}
function runValidatorsAndGetDeferred($field, validatorsToRun) {
var allDeferreds = [];
validatorsToRun.forEach(function (validatorIndex) {
var validatorFunction = getValidators()[validatorIndex].validatorFunction;
var deferred = new _jquery2.default.Deferred();
var validatorContext = createValidatorContext($field, deferred);
validatorFunction(validatorContext);
allDeferreds.push(deferred);
});
return allDeferreds;
}
function createValidatorContext($field, validatorDeferred) {
var context = {
validate: function validate() {
validatorDeferred.resolve();
},
invalidate: function invalidate(message) {
changeFieldState($field, INVALID, message);
validatorDeferred.reject();
},
args: createArgumentAccessorFunction($field),
el: $field[0],
$el: $field
};
deprecate.prop(context, '$el', {
sinceVersion: '5.9.0',
removeInVersion: '8.0.0',
alternativeName: 'el',
extraInfo: 'See https://ecosystem.atlassian.net/browse/AUI-3263.'
});
return context;
}
function createArgumentAccessorFunction($field) {
return function (arg) {
return $field.attr('data-' + ATTRIBUTE_VALIDATION_OPTION_PREFIX + arg) || $field.attr(arg);
};
}
function changeFieldState($field, state, message) {
$field.attr('data-' + ATTRIBUTE_FIELD_STATE, state);
if (state === UNVALIDATED) {
return;
}
$field.trigger(_jquery2.default.Event(EVENT_FIELD_STATE_CHANGED));
var $displayField = getDisplayField($field);
var stateToNotificationTypeMap = {};
stateToNotificationTypeMap[VALIDATING] = 'wait';
stateToNotificationTypeMap[INVALID] = 'error';
stateToNotificationTypeMap[VALID] = 'success';
var notificationType = stateToNotificationTypeMap[state];
if (state === VALIDATING) {
showSpinnerIfSlow($field);
} else {
setFieldNotification($displayField, notificationType, message);
}
}
function showSpinnerIfSlow($field) {
setTimeout(function () {
var stillValidating = getFieldState($field) === VALIDATING;
if (stillValidating) {
setFieldNotification($field, 'wait');
}
}, 500);
}
function setFieldNotification($field, type, message) {
var spinnerWasVisible = isSpinnerVisible($field);
removeIconOnlyNotifications($field);
var skipShowingSuccessNotification = type === 'success' && !spinnerWasVisible;
if (skipShowingSuccessNotification) {
return;
}
if (type === 'none') {
removeFieldNotification($field, 'error');
} else {
var previousMessage = $field.attr(ATTRIBUTE_NOTIFICATION_PREFIX + type) || '[]';
var newMessage = message ? combineJSONMessages(message, previousMessage) : '';
$field.attr(ATTRIBUTE_NOTIFICATION_PREFIX + type, newMessage);
}
}
function removeIconOnlyNotifications($field) {
removeFieldNotification($field, 'wait');
removeFieldNotification($field, 'success');
}
function removeFieldNotification($field, type) {
$field.removeAttr(ATTRIBUTE_NOTIFICATION_PREFIX + type);
}
function isSpinnerVisible($field) {
return $field.is('[' + ATTRIBUTE_NOTIFICATION_PREFIX + 'wait]');
}
function combineJSONMessages(newString, previousString) {
var previousStackedMessageList = JSON.parse(previousString);
var newStackedMessageList = previousStackedMessageList.concat([newString]);
var newStackedMessage = JSON.stringify(newStackedMessageList);
return newStackedMessage;
}
function getDisplayField($field) {
var displayFieldID = getValidationOption($field, 'displayfield');
var notifyOnSelf = displayFieldID === undefined;
return notifyOnSelf ? $field : (0, _jquery2.default)('#' + displayFieldID);
}
function getFieldState($field) {
return $field.attr('data-' + ATTRIBUTE_FIELD_STATE);
}
/**
* Trigger validation on a field manually
* @param $field the field that validation should be triggered for
*/
function validateField($field) {
$field = (0, _jquery2.default)($field);
validationTriggeredHandler($field);
}
/**
* Form scrolling and submission prevent based on validation state
* -If the form is unvalidated, validate all fields
* -If the form is invalid, go to the first invalid element
* -If the form is validating, wait for them to validate and then try submitting again
* -If the form is valid, allow form submission
*/
(0, _jquery2.default)(document).on('submit', function (e) {
var form = e.target;
var $form = (0, _jquery2.default)(form);
var formState = getFormStateName($form);
if (formState === UNVALIDATED) {
delaySubmitUntilStateChange($form, e);
validateUnvalidatedFields($form);
} else if (formState === VALIDATING) {
delaySubmitUntilStateChange($form, e);
} else if (formState === INVALID) {
e.preventDefault();
selectFirstInvalid($form);
} else if (formState === VALID) {
var validSubmitEvent = _jquery2.default.Event('aui-valid-submit');
$form.trigger(validSubmitEvent);
var preventNormalSubmit = validSubmitEvent.isDefaultPrevented();
if (preventNormalSubmit) {
e.preventDefault(); //users can bind to aui-valid-submit for ajax forms
}
}
});
function delaySubmitUntilStateChange($form, event) {
event.preventDefault();
$form.one(EVENT_FIELD_STATE_CHANGED, function () {
$form.trigger('submit');
});
}
function getFormStateName($form) {
var $fieldCollection = $form.find('.' + CLASS_VALIDATION_INITIALISED);
var fieldStates = getFieldCollectionStateNames($fieldCollection);
var wholeFormState = mergeStates(fieldStates);
return wholeFormState;
}
function getFieldCollectionStateNames($fields) {
var states = _jquery2.default.map($fields, function (field) {
return getFieldState((0, _jquery2.default)(field));
});
return states;
}
function mergeStates(stateNames) {
var containsInvalidState = stateNames.indexOf(INVALID) !== -1;
var containsUnvalidatedState = stateNames.indexOf(UNVALIDATED) !== -1;
var containsValidatingState = stateNames.indexOf(VALIDATING) !== -1;
if (containsInvalidState) {
return INVALID;
} else if (containsUnvalidatedState) {
return UNVALIDATED;
} else if (containsValidatingState) {
return VALIDATING;
} else {
return VALID;
}
}
function validateUnvalidatedFields($form) {
var $unvalidatedElements = getFieldsInFormWithState($form, UNVALIDATED);
$unvalidatedElements.each(function (index, el) {
validator.validate((0, _jquery2.default)(el));
});
}
function selectFirstInvalid($form) {
var $firstInvalidField = getFieldsInFormWithState($form, INVALID).first();
$firstInvalidField.focus();
}
function getFieldsInFormWithState($form, state) {
var selector = '[data-' + ATTRIBUTE_FIELD_STATE + '=' + state + ']';
return $form.find(selector);
}
var validator = {
register: _validatorRegister2.default.register,
validate: validateField
};
(0, _skate2.default)(ATTRIBUTE_VALIDATION_FIELD_COMPONENT, {
attached: function attached(field) {
if (field.form) {
field.form.setAttribute('novalidate', 'novalidate');
}
var $field = (0, _jquery2.default)(field);
initValidation($field);
_skate2.default.init(field); //needed to kick off form notification skate initialisation
},
type: _skate2.default.type.ATTRIBUTE
});
(0, _amdify2.default)('aui/form-validation', validator);
(0, _globalize2.default)('formValidation', validator);
exports.default = validator;
module.exports = exports['default'];
});
//# sourceMappingURL=form-validation.js.map