Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vivalid

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vivalid - npm Package Compare versions

Comparing version 0.1.4 to 0.2.0

.jsbeautifyrc

783

dist/vivalid-bundle.js

@@ -6,6 +6,7 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.vivalid = f()}})(function(){var define,module,exports;return (require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){

validInputTagNames: ['input', 'textarea', 'select'],
keyStrokedInputTypes: ['text', 'email', 'password', 'search'],
keyStrokedInputTypes: ['text', 'email', 'password', 'search', 'hidden'],
ERROR: {
mandatorySuccessFailure: 'passing callbacks for onValidationSuccess and onValidationFailure is mandatory'
}
mandatorySuccessFailure: 'passing callbacks for onValidationSuccess and onValidationFailure is mandatory',
errorInCallback: 'callback failed: '
}
};

@@ -16,3 +17,3 @@

function toArray(arrayLike){
function toArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);

@@ -22,3 +23,3 @@ }

function ready(fn) {
if (document.readyState != 'loading'){
if (document.readyState != 'loading') {
fn();

@@ -30,10 +31,10 @@ } else {

function getElementsByTagNames(tagsArray,obj) {
function getElementsByTagNames(tagsArray, obj) {
if (!obj) obj = document;
var results= [];
var i=0;
for (;i<tagsArray.length;i++) {
var results = [];
var i = 0;
for (; i < tagsArray.length; i++) {
var tags = obj.getElementsByTagName(tagsArray[i]);
var j=0;
for (;j<tags.length;j++) {
var j = 0;
for (; j < tags.length; j++) {
results.push(tags[j]);

@@ -48,5 +49,5 @@ }

// Get closest match
for ( ; elem && elem !== document; elem = elem.parentNode ) {
for (; elem && elem !== document; elem = elem.parentNode) {
if (hasDataSet(elem,attr)) {
if (hasDataSet(elem, attr)) {
return elem;

@@ -61,5 +62,5 @@ }

return toArray(elem.getElementsByTagName('*'))
.filter(function(el){
if (hasDataSet(el,attr)) return true;
});
.filter(function(el) {
if (hasDataSet(el, attr)) return true;
});
}

@@ -75,3 +76,3 @@

function getDataSet_unsupported(node, attr) {
if (node.nodeType !== Node.ELEMENT_NODE ) return false;
if (node.nodeType !== Node.ELEMENT_NODE) return false;

@@ -82,3 +83,3 @@ return node.getAttribute('data-' + toDashed(attr));

function getDataSet(node, attr) {
if (node.nodeType !== Node.ELEMENT_NODE ) return false;
if (node.nodeType !== Node.ELEMENT_NODE) return false;

@@ -88,3 +89,3 @@ return node.dataset[attr];

function hasDataSet(node, attr){
function hasDataSet(node, attr) {
return (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('data-' + toDashed(attr)));

@@ -99,4 +100,26 @@ }

// from http://jaketrent.com/post/addremove-classes-raw-javascript/
// used instead of classList because of lacking browser support
function hasClass(el, className) {
if (el.classList)
return el.classList.contains(className)
else
return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
}
function addClass(el, className) {
if (el.classList)
el.classList.add(className)
else if (!hasClass(el, className)) el.className += " " + className
}
function removeClass(el, className) {
if (el.classList)
el.classList.remove(className)
else if (hasClass(el, className)) {
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
el.className = el.className.replace(reg, ' ')
}
}
module.exports = {

@@ -109,7 +132,6 @@ getDataSet: isDataSetSupport ? getDataSet : getDataSet_unsupported,

ready: ready,
toArray: toArray
toArray: toArray,
addClass: addClass,
removeClass: removeClass
};
},{}],3:[function(require,module,exports){

@@ -124,2 +146,3 @@ var Input = require('./input');

var callbacks = {}; // custom user defined callbacks
var groupNameToVivalidGroup = {};

@@ -134,3 +157,3 @@ /**

*/
function addCallback(name,fn) {
function addCallback(name, fn) {
if (typeof fn !== 'function') throw 'error while trying to add a custom callback: argument must be a function';

@@ -148,13 +171,14 @@ if (callbacks[name]) throw 'error while trying to add a custom callback: ' + name + ' already exists';

*/
function initGroup(groupElem){
function initGroup(groupElem) {
$$.ready(registerGroupFromDataAttribtues);
function registerGroupFromDataAttribtues(){
function registerGroupFromDataAttribtues() {
inputElems = $$.getElementsByTagNames(validInputTagNames,groupElem)
.filter(function(el){
return $$.hasDataSet(el,'vivalidTuples');
});
inputElems = $$.getElementsByTagNames(validInputTagNames, groupElem)
.filter(function(el) {
return $$.hasDataSet(el, 'vivalidTuples');
});
createGroupFromDataAttribtues(groupElem,inputElems);
var vivalidGroup = createGroupFromDataAttribtues(groupElem, inputElems);
addToGroupNameDictionairy(groupElem, vivalidGroup);

@@ -174,3 +198,3 @@ }

function registerAllFromDataAttribtues(){
function registerAllFromDataAttribtues() {

@@ -184,17 +208,17 @@ var _nextGroupId = 1;

$$.getElementsByTagNames(validInputTagNames)
.filter(function(el){
return $$.hasDataSet(el,'vivalidTuples');
})
.forEach(function(el){
addGroupInputs($$.getClosestParentByAttribute(el,'vivalidGroup'),el);
});
.filter(function(el) {
return $$.hasDataSet(el, 'vivalidTuples');
})
.forEach(function(el) {
addGroupInputs($$.getClosestParentByAttribute(el, 'vivalidGroup'), el);
});
for (var groupId in groupIdToInputs){
createGroupFromDataAttribtues(groupIdToGroup[groupId], groupIdToInputs[groupId]);
for (var groupId in groupIdToInputs) {
var vivalidGroup = createGroupFromDataAttribtues(groupIdToGroup[groupId], groupIdToInputs[groupId]);
addToGroupNameDictionairy(groupIdToGroup[groupId], vivalidGroup);
}
function addGroupInputs(group, input) {
function addGroupInputs(group,input){
if (!group){
if (!group) {
throw 'an input validation is missing a group, input id: ' + input.id;

@@ -205,3 +229,3 @@ }

if(!groupIdToInputs[group._groupId]){
if (!groupIdToInputs[group._groupId]) {
groupIdToInputs[group._groupId] = [];

@@ -219,55 +243,94 @@ groupIdToGroup[group._groupId] = group;

/**
* Allow's an application to reset the validations state and event listeners of a group
* @memberof! vivalid.htmlInterface
* @function
* @example vivalid.htmlInterface.resetGroup('contactGroup');
* @param {string} groupName
*/
function resetGroup(groupName) {
var vivalidGroup = groupNameToVivalidGroup[groupName];
if (vivalidGroup) {
vivalidGroup.reset();
} else {
console.log('could not find group named ' + groupName);
}
}
/**
* @private
*/
function createGroupFromDataAttribtues(groupElem,inputElems){
function addToGroupNameDictionairy(groupElem, vivalidGroup) {
groupName = $$.getDataSet(groupElem, 'vivalidGroup');
groupNameToVivalidGroup[groupName] = vivalidGroup;
}
function createGroupFromDataAttribtues(groupElem, inputElems) {
var inputs = inputElems.map(vivalidInputFromElem);
var onValidation = [null,null];
var pendingUi = [null,null];
var onValidation = [null, null];
var pendingUi = [null, null];
var groupStatesChanged;
var groupPendingChanged;
var onBeforeValidation;
var onAfterValidation;
if ($$.hasDataSet(groupElem,'vivalidOnValidation')){
onValidation = JSON.parse($$.getDataSet(groupElem,'vivalidOnValidation'));
if ($$.hasDataSet(groupElem, 'vivalidOnValidation')) {
onValidation = JSON.parse($$.getDataSet(groupElem, 'vivalidOnValidation'));
if (!Array.isArray(onValidation) || onValidation.length !== 2) throw 'data-vivalid-on-validation value should be an array of size 2';
}
if ($$.hasDataSet(groupElem,'vivalidPendingUi')){
pendingUi = JSON.parse($$.getDataSet(groupElem,'vivalidPendingUi'));
if ($$.hasDataSet(groupElem, 'vivalidPendingUi')) {
pendingUi = JSON.parse($$.getDataSet(groupElem, 'vivalidPendingUi'));
if (!Array.isArray(pendingUi) || pendingUi.length !== 2) throw 'data-vivalid-pending-ui value should be an array of size 2';
}
if ($$.hasDataSet(groupElem,'vivalidStatesChanged')){
groupStatesChanged = $$.getDataSet(groupElem,'vivalidStatesChanged');
if ($$.hasDataSet(groupElem, 'vivalidStatesChanged')) {
groupStatesChanged = $$.getDataSet(groupElem, 'vivalidStatesChanged');
}
if ($$.hasDataSet(groupElem,'vivalidPendingChanged')){
groupPendingChanged = $$.getDataSet(groupElem,'vivalidPendingChanged');
if ($$.hasDataSet(groupElem, 'vivalidPendingChanged')) {
groupPendingChanged = $$.getDataSet(groupElem, 'vivalidPendingChanged');
}
new InputGroup(inputs,
$$.getChildrenByAttribute(groupElem,'vivalidSubmit'),
callbacks[onValidation[0]],
callbacks[onValidation[1]],
callbacks[pendingUi[0]],
callbacks[pendingUi[1]],
groupStatesChanged,
groupPendingChanged);
if ($$.hasDataSet(groupElem, 'vivalidBeforeValidation')) {
onBeforeValidation = $$.getDataSet(groupElem, 'vivalidBeforeValidation');
}
if ($$.hasDataSet(groupElem, 'vivalidAfterValidation')) {
onAfterValidation = $$.getDataSet(groupElem, 'vivalidAfterValidation');
}
return new InputGroup(inputs,
$$.getChildrenByAttribute(groupElem, 'vivalidSubmit'),
callbacks[onValidation[0]],
callbacks[onValidation[1]],
callbacks[pendingUi[0]],
callbacks[pendingUi[1]],
groupStatesChanged,
groupPendingChanged,
callbacks[onBeforeValidation],
callbacks[onAfterValidation],
$$.getChildrenByAttribute(groupElem, 'vivalidReset')
);
}
/**
* @private
*/
function vivalidInputFromElem(el){
var tuplesArray = JSON.parse($$.getDataSet(el,'vivalidTuples'));
function vivalidInputFromElem(el) {
var tuplesArray = JSON.parse($$.getDataSet(el, 'vivalidTuples'));
var onInputValidationResult;
if ($$.hasDataSet(el,'vivalidResult')){
onInputValidationResult = $$.getDataSet(el,'vivalidResult');
if ($$.hasDataSet(el, 'vivalidResult')) {
onInputValidationResult = $$.getDataSet(el, 'vivalidResult');
}
return new Input(el,tuplesArray,callbacks[onInputValidationResult]);
var isBlurOnly = $$.hasDataSet(el, 'vivalidBlurOnly');
return new Input(el, tuplesArray, callbacks[onInputValidationResult], isBlurOnly);
}
/**

@@ -281,6 +344,6 @@ * The interface to use when using data attributes to define Inputs And Groups.

initAll: initAll,
initGroup: initGroup
initGroup: initGroup,
resetGroup: resetGroup
};
},{"./constants":1,"./dom-helpers":2,"./input":5,"./input-group":4}],4:[function(require,module,exports){
},{"./constants":1,"./dom-helpers":2,"./input":6,"./input-group":4}],4:[function(require,module,exports){
var Input = require('./input');

@@ -303,126 +366,137 @@ var stateEnum = require('./state-enum');

* @param {function} [groupPendingChanged]
* @param {function} [onBeforeValidation] Signature of {@link _internal.onBeforeValidation onBeforeValidation}. A function to be called before triggering any of the input's validators
* @param {function} [onAfterValidation] Signature of {@link _internal.onAfterValidation onAfterValidation}. A function to be called after triggering all of the input's validators
* @param {HTMLElement[]} [resetElems] an array of elements that should trigger the group's validation _reset.
*/
function InputGroup(inputsArray,submitElems,onValidationSuccess,onValidationFailure,pendingUiStart,pendingUiStop, groupStatesChanged, groupPendingChanged){
function InputGroup(inputsArray, submitElems, onValidationSuccess, onValidationFailure, pendingUiStart, pendingUiStop, groupStatesChanged, groupPendingChanged, onBeforeValidation, onAfterValidation, resetElems) {
if(!onValidationSuccess || !onValidationFailure) throw ERROR.mandatorySuccessFailure;
if (!onValidationSuccess || !onValidationFailure) throw ERROR.mandatorySuccessFailure;
this.inputs = [];
this.inputElems = [];
this.submitElems = [];
this._inputs = [];
this._inputElems = [];
this._submitElems = [];
this._resetElems = [];
this._onValidationSuccess = onValidationSuccess;
this._onValidationFailure = onValidationFailure;
this._pendingUiStart = pendingUiStart;
this._pendingUiStop = pendingUiStop;
this._groupStatesChanged = groupStatesChanged;
this._onBeforeValidation = onBeforeValidation;
this._onAfterValidation = onAfterValidation;
this.onValidationSuccess = onValidationSuccess;
this.onValidationFailure = onValidationFailure;
this.pendingUiStart = pendingUiStart;
this.pendingUiStop = pendingUiStop;
this.groupStatesChanged = groupStatesChanged;
this._groupPendingChangedListeners = [];
this._groupPendingChangedListeners.push(
function(_isPending) {
if (!_isPending) {
if (this._isPendingUiStartRun) {
this.groupPendingChangedListeners = [];
this.groupPendingChangedListeners.push(
function(isPending){
if(!isPending){
if(this.isPendingUiStartRun){
this._pendingUiStop.call(this._pendingUiLastSubmitElem, this._inputElems, this._submitElems, this._resetElems);
this.pendingUiStop.call(this.pendingUiLastSubmitElem,this.inputElems,this.submitElems);
this._getOnSubmit.call(this).call(this._pendingUiLastSubmitElem);
this.getOnSubmit.call(this).call(this.pendingUiLastSubmitElem);
this.isPendingUiStartRun = false;
this.pendingUiLastSubmitElem = {};
this._isPendingUiStartRun = false;
this._pendingUiLastSubmitElem = {};
}
}
}.bind(this)
}.bind(this)
);
if (groupPendingChanged)
this.groupPendingChangedListeners.push(groupPendingChanged);
if (groupPendingChanged)
this._groupPendingChangedListeners.push(groupPendingChanged);
this.stateCounters = {};
this.stateCounters[stateEnum.invalid] = 0;
this.stateCounters[stateEnum.pending] = 0;
this.stateCounters[stateEnum.valid] = 0;
this._stateCounters = {};
this._stateCounters[stateEnum.invalid] = 0;
this._stateCounters[stateEnum.pending] = 0;
this._stateCounters[stateEnum.valid] = 0;
this.isPendingChangeTrueRun = false;
this._isPendingChangeTrueRun = false;
this.isPendingUiStartRun = false;
this._isPendingUiStartRun = false;
this.pendingUiLastSubmitElem = {};
this._pendingUiLastSubmitElem = {};
this.inputs = inputsArray.map(function(input){
this._inputs = inputsArray.map(function(input) {
input.setGroup(this);
return input;
},this);
}, this);
this.inputElems = inputsArray
.map(function(input){
return input.el;
});
this._inputElems = inputsArray
.map(function(input) {
return input.getDomElement();
});
this._stateCounters[stateEnum.valid] = inputsArray.length;
this.stateCounters[stateEnum.valid] = inputsArray.length;
this._submitElems = Array.prototype.slice.call(submitElems);
this.submitElems = Array.prototype.slice.call((submitElems));
this._submitElems.forEach(function(submit) {
submit.addEventListener('click', this._getOnSubmit.call(this));
}, this);
this.submitElems.forEach(function(submit){
submit.addEventListener('click',this.getOnSubmit.call(this));
},this);
if (resetElems) {
this._resetElems = Array.prototype.slice.call(resetElems);
this._resetElems.forEach(function(submit) {
submit.addEventListener('click', this.reset.bind(this));
}, this);
}
}
InputGroup.prototype = (function(){
InputGroup.prototype = (function() {
return {
isValid: isValid,
isPending: isPending,
getOnSubmit: getOnSubmit,
triggerInputsValidation: triggerInputsValidation,
updateGroupListeners: updateGroupListeners,
updateGroupStates: updateGroupStates,
reset: reset,
getOnBeforeValidation: getOnBeforeValidation,
getOnAfterValidation: getOnAfterValidation,
_isValid: _isValid,
_isPending: _isPending,
_getOnSubmit: _getOnSubmit,
_triggerInputsValidation: _triggerInputsValidation
};
function isValid(){
this.triggerInputsValidation();
function _isValid() {
this._triggerInputsValidation();
return (this.stateCounters[stateEnum.invalid] === 0 &&
this.stateCounters[stateEnum.pending] === 0);
return (this._stateCounters[stateEnum.invalid] === 0 &&
this._stateCounters[stateEnum.pending] === 0);
}
function isPending(){
this.triggerInputsValidation();
function _isPending() {
this._triggerInputsValidation();
return (this.stateCounters[stateEnum.invalid] === 0 &&
this.stateCounters[stateEnum.pending] > 0);
return (this._stateCounters[stateEnum.invalid] === 0 &&
this._stateCounters[stateEnum.pending] > 0);
}
function getOnSubmit() {
function _getOnSubmit() {
var self = this;
return function (e) {
if(e) e.preventDefault();
return function(e) {
if (e) e.preventDefault();
if(self.isPending()){
self.pendingUiStart.call(this,self.inputElems,self.submitElems);
self.isPendingUiStartRun = true;
self.pendingUiLastSubmitElem = this;
if (self._isPending()) {
self._pendingUiStart.call(this, self._inputElems, self._submitElems, self._resetElems);
self._isPendingUiStartRun = true;
self._pendingUiLastSubmitElem = this;
} else if (!self._isValid()) {
self._onValidationFailure.call(this,
self._stateCounters[stateEnum.invalid],
self._stateCounters[stateEnum.pending],
self._stateCounters[stateEnum.valid]);
} else {
self._onValidationSuccess.call(this);
}
else if(!self.isValid()){
self.onValidationFailure.call(this,
self.stateCounters[stateEnum.invalid],
self.stateCounters[stateEnum.pending],
self.stateCounters[stateEnum.valid]);
}
else {
self.onValidationSuccess.call(this);
}
if (DEBUG){
if (DEBUG) {
console.debug('cuurent states:');
console.debug("invalid: " + self.stateCounters[stateEnum.invalid]);
console.debug("pending: " + self.stateCounters[stateEnum.pending]);
console.debug("valid: " + self.stateCounters[stateEnum.valid]);
console.debug("invalid: " + self._stateCounters[stateEnum.invalid]);
console.debug("pending: " + self._stateCounters[stateEnum.pending]);
console.debug("valid: " + self._stateCounters[stateEnum.valid]);
}

@@ -434,4 +508,4 @@

function triggerInputsValidation(){
this.inputs.forEach(function(input){
function _triggerInputsValidation() {
this._inputs.forEach(function(input) {
input.triggerValidation();

@@ -441,29 +515,47 @@ });

function updateGroupStates(fromInputState, toInputState){
function getOnBeforeValidation(){
return this._onBeforeValidation;
}
function getOnAfterValidation(){
return this._onAfterValidation;
}
function updateGroupStates(fromInputState, toInputState) {
if (fromInputState.stateEnum === toInputState.stateEnum) return;
this.stateCounters[fromInputState.stateEnum]--;
this.stateCounters[toInputState.stateEnum]++;
this._stateCounters[fromInputState.stateEnum]--;
this._stateCounters[toInputState.stateEnum]++;
}
function updateGroupListeners(){
if (this.groupStatesChanged) this.groupStatesChanged();
function updateGroupListeners() {
if (this._groupStatesChanged) this._groupStatesChanged();
// run both internal and user groupPendingChange functions
this.groupPendingChangedListeners.forEach(function(listener){
this._groupPendingChangedListeners.forEach(function(listener) {
if (!this.isPendingChangeTrueRun && this.stateCounters[stateEnum.invalid] === 0 && this.stateCounters[stateEnum.pending] > 0)
{
listener(true);
this.isPendingChangeTrueRun = true;
}
else if (this.isPendingChangeTrueRun && this.stateCounters[stateEnum.pending] === 0)
{
listener(false);
this.isPendingChangeTrueRun = false;
}
},this);
if (!this._isPendingChangeTrueRun && this._stateCounters[stateEnum.invalid] === 0 && this._stateCounters[stateEnum.pending] > 0) {
listener(true);
this._isPendingChangeTrueRun = true;
} else if (this._isPendingChangeTrueRun && this._stateCounters[stateEnum.pending] === 0) {
listener(false);
this._isPendingChangeTrueRun = false;
}
}, this);
}
function reset(e) {
if (e && e.preventDefault) e.preventDefault();
this._inputs.forEach(function(input) {
input.reset();
});
this._stateCounters[stateEnum.invalid] = 0;
this._stateCounters[stateEnum.pending] = 0;
this._stateCounters[stateEnum.valid] = this._inputs.length;
}
})();

@@ -503,2 +595,3 @@

* @param {HTMLElement[]} submitElems the group's submit elements
* @param {HTMLElement[]} resetElems the group's _reset elements
*/

@@ -512,9 +605,40 @@

* @param {HTMLElement[]} submitElems the group's submit elements
* @param {HTMLElement[]} resetElems the group's _reset elements
*/
},{"./constants":1,"./input":5,"./state-enum":6}],5:[function(require,module,exports){
/** A function to be called before triggering any of the input's validators
* @name onBeforeValidation
* @function
* @memberof! _internal
* @param {HTMLElement} el the input's DOM object.
*/
/** A function to be called after triggering all of the input's validators
* @name onAfterValidation
* @function
* @memberof! _internal
* @param {HTMLElement} el the input's DOM object.
*/
},{"./constants":1,"./input":6,"./state-enum":7}],5:[function(require,module,exports){
var ValidationState = require('./validation-state');
var stateEnum = require('./state-enum');
function InputState() {
this.isNoneChecked = false;
this.validationState = new ValidationState('', stateEnum.valid);
this.validationCycle = 0;
this.isChanged = false;
this.activeEventType = '';
}
module.exports = InputState;
},{"./state-enum":7,"./validation-state":8}],6:[function(require,module,exports){
var validatorRepo = require('./validator-repo');
var stateEnum = require('./state-enum');
var ValidationState = require('./validation-state');
var InputState = require('./input-state');
var constants = require('./constants');
var $$ = require('./dom-helpers');

@@ -532,42 +656,33 @@ var validInputTagNames = constants.validInputTagNames;

* @param {function} [onInputValidationResult] Signature of {@link _internal.onInputValidationResult onInputValidationResult}. A function to handle an input state or message change. If not passed, {@link _internal.defaultOnInputValidationResult defaultOnInputValidationResult} will be used.
* @param {boolean} isBlurOnly if true, doesn't not trigger validation on 'input' or 'change' events.
*/
function Input(el, validatorsNameOptionsTuples, onInputValidationResult){
function Input(el, validatorsNameOptionsTuples, onInputValidationResult, isBlurOnly) {
if (validInputTagNames.indexOf(el.nodeName.toLowerCase()) === -1){
if (validInputTagNames.indexOf(el.nodeName.toLowerCase()) === -1) {
throw 'only operates on the following html tags: ' + validInputTagNames.toString();
}
this.group = {};
this._el = el;
this._validatorsNameOptionsTuples = validatorsNameOptionsTuples;
this._onInputValidationResult = onInputValidationResult || defaultOnInputValidationResult;
this._isBlurOnly = isBlurOnly;
this._validators = buildValidators();
this._inputState = new InputState();
this._elName = el.nodeName.toLowerCase();
this._elType = el.type;
this._isKeyed = (this._elName === 'textarea' || keyStrokedInputTypes.indexOf(this._elType) > -1);
this._runValidatorsBounded = this._runValidators.bind(this);
this.el = el;
this.validators = buildValidators();
this.onInputValidationResult = onInputValidationResult || defaultOnInputValidationResult;
this.isNoneChecked = false;
this._initListeners();
this.validationState = new ValidationState('', stateEnum.valid);
this.validationCycle = 0;
this.isChanged = false;
this.elName = el.nodeName.toLowerCase();
this.elType = el.type;
this.isKeyed = (this.elName === 'textarea' || keyStrokedInputTypes.indexOf(this.elType) > -1);
this.activeEventType = '';
this._runValidatorsBounded = this.runValidators.bind(this);
this.initListeners();
function buildValidators(){
function buildValidators() {
var result = [];
validatorsNameOptionsTuples.forEach(function(validatorsNameOptionsTuple){
validatorsNameOptionsTuples.forEach(function(validatorsNameOptionsTuple) {
var validatorName = validatorsNameOptionsTuple[0];
var validatorOptions = validatorsNameOptionsTuple[1];
result.push(
{
name: validatorName,
run: validatorRepo.build(validatorName,validatorOptions)
}
);
result.push({
name: validatorName,
run: validatorRepo.build(validatorName, validatorOptions)
});

@@ -579,3 +694,2 @@ });

/** The default {@link _internal.onInputValidationResult onInputValidationResult} used when {@link vivalid.Input} is initiated without a 3rd parameter

@@ -586,3 +700,3 @@ * @name defaultOnInputValidationResult

*/
function defaultOnInputValidationResult(el,validationsResult,validatorName,stateEnum) {
function defaultOnInputValidationResult(el, validationsResult, validatorName, stateEnum) {

@@ -592,29 +706,28 @@ var errorDiv;

// for radio buttons and checkboxes: get the last element in group by name
if ((el.nodeName.toLowerCase() === 'input' && (el.type === 'radio' || el.type === 'checkbox' ))){
if ((el.nodeName.toLowerCase() === 'input' && (el.type === 'radio' || el.type === 'checkbox'))) {
var getAllByName = el.parentNode.querySelectorAll('input[name="'+el.name+'"]');
var getAllByName = el.parentNode.querySelectorAll('input[name="' + el.name + '"]');
el = getAllByName.item(getAllByName.length-1);
el = getAllByName.item(getAllByName.length - 1);
}
if(validationsResult.stateEnum === stateEnum.invalid){
if (validationsResult.stateEnum === stateEnum.invalid) {
errorDiv = getExistingErrorDiv(el);
if(errorDiv) {
if (errorDiv) {
errorDiv.textContent = validationsResult.message;
} else {
appendNewErrorDiv(el, validationsResult.message);
}
else {
appendNewErrorDiv(el,validationsResult.message);
}
el.style.borderStyle = "solid";
el.style.borderColor = "#ff0000";
}
else {
$$.addClass(el, "vivalid-error-input");
} else {
errorDiv = getExistingErrorDiv(el);
if(errorDiv) {
if (errorDiv) {
errorDiv.parentNode.removeChild(errorDiv);
el.style.borderStyle = "";
el.style.borderColor = "";
$$.removeClass(el, "vivalid-error-input");
}

@@ -624,4 +737,4 @@ }

function getExistingErrorDiv(el) {
if (el.nextSibling.className === "vivalid-error") {
return el.nextSibling;
if (el.nextElementSibling && el.nextElementSibling.className === "vivalid-error") {
return el.nextElementSibling;
}

@@ -631,9 +744,9 @@

function appendNewErrorDiv(el,message) {
errorDiv = document.createElement("DIV");
function appendNewErrorDiv(el, message) {
errorDiv = document.createElement("DIV");
errorDiv.className = "vivalid-error";
errorDiv.style.color = "#ff0000";
var t = document.createTextNode(validationsResult.message);
errorDiv.appendChild(t);
el.parentNode.insertBefore(errorDiv, el.nextSibling);
var t = document.createTextNode(validationsResult.message);
errorDiv.appendChild(t);
el.parentNode.insertBefore(errorDiv, el.nextElementSibling);
}

@@ -648,37 +761,36 @@

return {
reBindCheckedElement: reBindCheckedElement,
triggerValidation: triggerValidation,
runValidators: runValidators,
changeEventType: changeEventType,
initListeners: initListeners,
setGroup: setGroup,
addChangeListener: addChangeListener,
addEventType: addEventType,
removeActiveEventType: removeActiveEventType,
getUpdateInputValidationResultAsync: getUpdateInputValidationResultAsync,
updateInputValidationResult: updateInputValidationResult
reset: reset,
getDomElement: getDomElement,
_reBindCheckedElement: _reBindCheckedElement,
_runValidators: _runValidators,
_changeEventType: _changeEventType,
_initListeners: _initListeners,
_addChangeListener: _addChangeListener,
_addEventType: _addEventType,
_removeActiveEventType: _removeActiveEventType,
_getUpdateInputValidationResultAsync: _getUpdateInputValidationResultAsync,
_updateInputValidationResult: _updateInputValidationResult
};
// public
function _reBindCheckedElement() {
function reBindCheckedElement(){
// reBind only radio and checkbox buttons
if (!(this.el.nodeName.toLowerCase() === 'input' && (this.el.type === 'radio' || this.el.type === 'checkbox' ))){
if (!(this._el.nodeName.toLowerCase() === 'input' && (this._el.type === 'radio' || this._el.type === 'checkbox'))) {
return;
}
var checkedElement = document.querySelector('input[name="'+this.el.name+'"]:checked');
if (checkedElement){
this.el = checkedElement;
this.isNoneChecked = false;
var checkedElement = document.querySelector('input[name="' + this._el.name + '"]:checked');
if (checkedElement) {
this._el = checkedElement;
this._inputState.isNoneChecked = false;
} else {
this._inputState.isNoneChecked = true;
}
else{
this.isNoneChecked = true;
}
}
function triggerValidation(){
if (this.validationCycle === 0 || this.isChanged) {
function triggerValidation() {
if (this._inputState.validationCycle === 0 || this._inputState.isChanged) {
this._runValidatorsBounded();

@@ -688,45 +800,53 @@ }

function changeEventType(eventType) {
if (!this.isKeyed) return;
if (eventType === this.activeEventType) return;
this.removeActiveEventType();
this.addEventType(eventType);
function _changeEventType(eventType) {
if (!this._isKeyed) return;
if (eventType === this._inputState.activeEventType) return;
this._removeActiveEventType();
this._addEventType(eventType);
}
function setGroup(value) {
this.group = value;
this._group = value;
}
function initListeners() {
function getDomElement(){
return this._el;
}
this.addChangeListener();
if (this.isKeyed){
this.addEventType('blur');
function _initListeners() {
this._addChangeListener();
if (this._isKeyed) {
this._addEventType('blur');
} else {
this._addEventType('change');
}
else {
this.addEventType('change');
}
}
function runValidators(event, fromIndex){
function _runValidators(event, fromIndex) {
this.validationCycle++;
this.reBindCheckedElement();
this._inputState.validationCycle++;
this._reBindCheckedElement();
if (typeof this._group.getOnBeforeValidation() === 'function') {
this._group.getOnBeforeValidation()(this._el);
}
var validationsResult, validatorName;
var i = fromIndex || 0;
for (; i < this.validators.length; i++){
var validator = this.validators[i];
var elementValue = this.isNoneChecked ? '' : this.el.value;
for (; i < this._validators.length; i++) {
var validator = this._validators[i];
var elementValue = this._inputState.isNoneChecked ? '' : this._el.value;
// if async, then return a pending enum with empty message and call the callback with result once ready
var validatorResult = validator.run(elementValue, this.getUpdateInputValidationResultAsync(validator.name, i, this.validationCycle));
if (validatorResult.stateEnum !== stateEnum.valid)
{
validationsResult = validatorResult;
validatorName = validator.name;
this.changeEventType('input'); //TODO: call only once?
break;
var validatorResult = validator.run(elementValue, this._getUpdateInputValidationResultAsync(validator.name, i, this._inputState.validationCycle));
if (validatorResult.stateEnum !== stateEnum.valid) {
validationsResult = validatorResult;
validatorName = validator.name;
if (!this._isBlurOnly) {
this._changeEventType('input'); //TODO: call only once?
}
break;
}

@@ -736,64 +856,78 @@ }

validationsResult = validationsResult || new ValidationState('', stateEnum.valid);
this.updateInputValidationResult(validationsResult,validatorName);
this._updateInputValidationResult(validationsResult, validatorName);
// new...
this.isChanged = false; // TODO: move to top of function
this._inputState.isChanged = false; // TODO: move to top of function?
if (typeof this._group.getOnAfterValidation() === 'function') {
this._group.getOnAfterValidation()(this._el);
}
}
// private
function reset() {
this._removeActiveEventType();
this._initListeners();
this._inputState = new InputState();
this._onInputValidationResult(this._el, stateEnum.valid, '', stateEnum); // called with valid state to clear any previous errors UI
}
function addChangeListener(){
if (this.isKeyed){
this.el.addEventListener('input', function () { this.isChanged = true;}, false);
}
function _addChangeListener() {
else if (this.elName === 'input' && (this.elType === 'radio' || this.elType === 'checkbox')){
var self = this;
var groupElements = document.querySelectorAll('input[name="'+this.el.name+'"]');
if (this._isKeyed) {
if (this._isBlurOnly) {
return;
} else {
this._el.addEventListener('input', function() {
self._inputState.isChanged = true;
}, false);
}
} else if (this._elName === 'input' && (this._elType === 'radio' || this._elType === 'checkbox')) {
var i=0;
for (; i <groupElements.length ; i++) {
groupElements[i].addEventListener('change', function (){this.isChanged = true;}, false);
var groupElements = document.querySelectorAll('input[name="' + this._el.name + '"]');
var i = 0;
for (; i < groupElements.length; i++) {
groupElements[i].addEventListener('change', function() {
self._inputState.isChanged = true;
}, false);
}
} else if (this._elName === 'select') {
this._el.addEventListener('change', function() {
self._inputState.isChanged = true;
}, false);
}
else if(this.elName === 'select'){
this.el.addEventListener('change', function (){this.isChanged = true;}, false);
}
}
function addEventType(eventType) {
if (this.isKeyed){
this.el.addEventListener(eventType, this._runValidatorsBounded, false);
}
function _addEventType(eventType) {
if (this._isKeyed) {
this._el.addEventListener(eventType, this._runValidatorsBounded, false);
} else if (this._elName === 'input' && (this._elType === 'radio' || this._elType === 'checkbox')) {
else if (this.elName === 'input' && (this.elType === 'radio' || this.elType === 'checkbox')){
var groupElements = document.querySelectorAll('input[name="' + this._el.name + '"]');
var groupElements = document.querySelectorAll('input[name="'+this.el.name+'"]');
var i=0;
for (; i <groupElements.length ; i++) {
var i = 0;
for (; i < groupElements.length; i++) {
groupElements[i].addEventListener(eventType, this._runValidatorsBounded, false);
}
} else if (this._elName === 'select') {
this._el.addEventListener(eventType, this._runValidatorsBounded, false);
}
else if(this.elName === 'select'){
this.el.addEventListener(eventType, this._runValidatorsBounded, false);
}
this.activeEventType = eventType;
this._inputState.activeEventType = eventType;
}
function removeActiveEventType() {
this.el.removeEventListener(this.activeEventType, this._runValidatorsBounded, false);
function _removeActiveEventType() {
this._el.removeEventListener(this._inputState.activeEventType, this._runValidatorsBounded, false);
}
function getUpdateInputValidationResultAsync(validatorName, validatorIndex, asyncValidationCycle) {
function _getUpdateInputValidationResultAsync(validatorName, validatorIndex, asyncValidationCycle) {
var self = this;
return function(validatorResult){
return function(validatorResult) {
// guard against updating async validations from old cycles
if(asyncValidationCycle && asyncValidationCycle !== self.validationCycle){
if (asyncValidationCycle && asyncValidationCycle !== self._inputState.validationCycle) {
return;

@@ -803,9 +937,7 @@ }

// if pending turned to be valid, and there are more validation to run, run them:
if (validatorResult.stateEnum === stateEnum.valid && validatorIndex+1 < self.validators.length){
self._runValidatorsBounded(null,validatorIndex+1);
if (validatorResult.stateEnum === stateEnum.valid && validatorIndex + 1 < self._validators.length) {
self._runValidatorsBounded(null, validatorIndex + 1);
} else {
self._updateInputValidationResult(validatorResult, validatorName);
}
else {
self.updateInputValidationResult(validatorResult,validatorName);
}
};

@@ -815,9 +947,9 @@

function updateInputValidationResult(validationsResult,validatorName) {
function _updateInputValidationResult(validationsResult, validatorName) {
this.group.updateGroupStates(this.validationState, validationsResult); // filter equal state at caller
this.group.updateGroupListeners();
this._group.updateGroupStates(this._inputState.validationState, validationsResult); // filter equal state at caller
this._group.updateGroupListeners();
this.validationState = validationsResult;
this.onInputValidationResult(this.el,validationsResult,validatorName,stateEnum);
this._inputState.validationState = validationsResult;
this._onInputValidationResult(this._el, validationsResult, validatorName, stateEnum);

@@ -847,3 +979,3 @@ }

},{"./constants":1,"./state-enum":6,"./validation-state":7,"./validator-repo":8}],6:[function(require,module,exports){
},{"./constants":1,"./dom-helpers":2,"./input-state":5,"./state-enum":7,"./validation-state":8,"./validator-repo":9}],7:[function(require,module,exports){
/**

@@ -862,4 +994,3 @@ * An Enum with 3 states: invalid , pending , valid .

module.exports = stateEnum;
},{}],7:[function(require,module,exports){
},{}],8:[function(require,module,exports){
/**

@@ -873,3 +1004,3 @@ * {constructor} creates a new ValidationState object with a validation message and state.

*/
function ValidationState(message, stateEnum){
function ValidationState(message, stateEnum) {
this.message = message;

@@ -884,4 +1015,3 @@ this.stateEnum = stateEnum;

*/
},{}],8:[function(require,module,exports){
},{}],9:[function(require,module,exports){
var stateEnum = require('./state-enum');

@@ -900,5 +1030,5 @@ var ValidationState = require('./validation-state');

*/
function addBuilder(name, fn){
function addBuilder(name, fn) {
if (typeof fn !== 'function') throw 'error while trying to register a Validator: argument must be a function';
validatorBuildersRepository [name] = fn;
validatorBuildersRepository[name] = fn;
}

@@ -914,3 +1044,3 @@

*/
function build(validatorName,validatorOptions) {
function build(validatorName, validatorOptions) {

@@ -921,3 +1051,3 @@ if (typeof validatorBuildersRepository[validatorName] !== 'function') {

return validatorBuildersRepository[validatorName](ValidationState,stateEnum,validatorOptions);
return validatorBuildersRepository[validatorName](ValidationState, stateEnum, validatorOptions);
}

@@ -934,4 +1064,3 @@

};
},{"./state-enum":6,"./validation-state":7}],"vivalid":[function(require,module,exports){
},{"./state-enum":7,"./validation-state":8}],"vivalid":[function(require,module,exports){
'use strict';

@@ -957,3 +1086,3 @@

},{"./constants":1,"./html-interface":3,"./input":5,"./input-group":4,"./validator-repo":8}]},{},["vivalid"]))("vivalid")
},{"./constants":1,"./html-interface":3,"./input":6,"./input-group":4,"./validator-repo":9}]},{},["vivalid"]))("vivalid")
});

@@ -1,1 +0,1 @@

!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.vivalid=t()}}(function(){return(require=function t(e,i,n){function a(r,o){if(!i[r]){if(!e[r]){var u="function"==typeof require&&require;if(!o&&u)return u(r,!0);if(s)return s(r,!0);var d=new Error("Cannot find module '"+r+"'");throw d.code="MODULE_NOT_FOUND",d}var l=i[r]={exports:{}};e[r][0].call(l.exports,function(t){var i=e[r][1][t];return a(i?i:t)},l,l.exports,t,e,i,n)}return i[r].exports}for(var s="function"==typeof require&&require,r=0;r<n.length;r++)a(n[r]);return a}({1:[function(t,e,i){e.exports={VERSION:"0.1.0",DEBUG:!1,validInputTagNames:["input","textarea","select"],keyStrokedInputTypes:["text","email","password","search"],ERROR:{mandatorySuccessFailure:"passing callbacks for onValidationSuccess and onValidationFailure is mandatory"}}},{}],2:[function(t,e,i){function n(t){return Array.prototype.slice.call(t)}function a(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}function s(t,e){e||(e=document);for(var i=[],n=0;n<t.length;n++)for(var a=e.getElementsByTagName(t[n]),s=0;s<a.length;s++)i.push(a[s]);return i}function r(t,e){for(;t&&t!==document;t=t.parentNode)if(h(t,e))return t;return!1}function o(t,e){return n(t.getElementsByTagName("*")).filter(function(t){return h(t,e)?!0:void 0})}function u(){var t=document.createElement("div");return t.setAttribute("data-a-b","c"),!(!t.dataset||"c"!==t.dataset.aB)}function d(t,e){return t.nodeType!==Node.ELEMENT_NODE?!1:t.getAttribute("data-"+c(e))}function l(t,e){return t.nodeType!==Node.ELEMENT_NODE?!1:t.dataset[e]}function h(t,e){return t.nodeType===Node.ELEMENT_NODE&&t.hasAttribute("data-"+c(e))}function c(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}var p=u();e.exports={getDataSet:p?l:d,hasDataSet:h,getElementsByTagNames:s,getClosestParentByAttribute:r,getChildrenByAttribute:o,ready:a,toArray:n}},{}],3:[function(t,e,i){function n(t,e){if("function"!=typeof e)throw"error while trying to add a custom callback: argument must be a function";if(p[t])throw"error while trying to add a custom callback: "+t+" already exists";p[t]=e}function a(t){function e(){inputElems=l.getElementsByTagNames(c,t).filter(function(t){return l.hasDataSet(t,"vivalidTuples")}),r(t,inputElems)}l.ready(e)}function s(){function t(){function t(t,a){if(!t)throw"an input validation is missing a group, input id: "+a.id;t._groupId||(t._groupId=e++),i[t._groupId]||(i[t._groupId]=[],n[t._groupId]=t),i[t._groupId].push(a)}var e=1,i={},n={};l.getElementsByTagNames(c).filter(function(t){return l.hasDataSet(t,"vivalidTuples")}).forEach(function(e){t(l.getClosestParentByAttribute(e,"vivalidGroup"),e)});for(var a in i)r(n[a],i[a])}l.ready(t)}function r(t,e){var i,n,a=e.map(o),s=[null,null],r=[null,null];if(l.hasDataSet(t,"vivalidOnValidation")&&(s=JSON.parse(l.getDataSet(t,"vivalidOnValidation")),!Array.isArray(s)||2!==s.length))throw"data-vivalid-on-validation value should be an array of size 2";if(l.hasDataSet(t,"vivalidPendingUi")&&(r=JSON.parse(l.getDataSet(t,"vivalidPendingUi")),!Array.isArray(r)||2!==r.length))throw"data-vivalid-pending-ui value should be an array of size 2";l.hasDataSet(t,"vivalidStatesChanged")&&(i=l.getDataSet(t,"vivalidStatesChanged")),l.hasDataSet(t,"vivalidPendingChanged")&&(n=l.getDataSet(t,"vivalidPendingChanged")),new d(a,l.getChildrenByAttribute(t,"vivalidSubmit"),p[s[0]],p[s[1]],p[r[0]],p[r[1]],i,n)}function o(t){var e,i=JSON.parse(l.getDataSet(t,"vivalidTuples"));return l.hasDataSet(t,"vivalidResult")&&(e=l.getDataSet(t,"vivalidResult")),new u(t,i,p[e])}var u=t("./input"),d=t("./input-group"),l=t("./dom-helpers"),h=t("./constants"),c=h.validInputTagNames,p={};e.exports={addCallback:n,initAll:s,initGroup:a}},{"./constants":1,"./dom-helpers":2,"./input":5,"./input-group":4}],4:[function(t,e,i){function n(t,e,i,n,s,o,u,d){if(!i||!n)throw r.mandatorySuccessFailure;this.inputs=[],this.inputElems=[],this.submitElems=[],this.onValidationSuccess=i,this.onValidationFailure=n,this.pendingUiStart=s,this.pendingUiStop=o,this.groupStatesChanged=u,this.groupPendingChangedListeners=[],this.groupPendingChangedListeners.push(function(t){t||this.isPendingUiStartRun&&(this.pendingUiStop.call(this.pendingUiLastSubmitElem,this.inputElems,this.submitElems),this.getOnSubmit.call(this).call(this.pendingUiLastSubmitElem),this.isPendingUiStartRun=!1,this.pendingUiLastSubmitElem={})}.bind(this)),d&&this.groupPendingChangedListeners.push(d),this.stateCounters={},this.stateCounters[a.invalid]=0,this.stateCounters[a.pending]=0,this.stateCounters[a.valid]=0,this.isPendingChangeTrueRun=!1,this.isPendingUiStartRun=!1,this.pendingUiLastSubmitElem={},this.inputs=t.map(function(t){return t.setGroup(this),t},this),this.inputElems=t.map(function(t){return t.el}),this.stateCounters[a.valid]=t.length,this.submitElems=Array.prototype.slice.call(e),this.submitElems.forEach(function(t){t.addEventListener("click",this.getOnSubmit.call(this))},this)}var a=(t("./input"),t("./state-enum")),s=t("./constants").DEBUG,r=t("./constants").ERROR;n.prototype=function(){function t(){return this.triggerInputsValidation(),0===this.stateCounters[a.invalid]&&0===this.stateCounters[a.pending]}function e(){return this.triggerInputsValidation(),0===this.stateCounters[a.invalid]&&this.stateCounters[a.pending]>0}function i(){var t=this;return function(e){e&&e.preventDefault(),t.isPending()?(t.pendingUiStart.call(this,t.inputElems,t.submitElems),t.isPendingUiStartRun=!0,t.pendingUiLastSubmitElem=this):t.isValid()?t.onValidationSuccess.call(this):t.onValidationFailure.call(this,t.stateCounters[a.invalid],t.stateCounters[a.pending],t.stateCounters[a.valid]),s&&(console.debug("cuurent states:"),console.debug("invalid: "+t.stateCounters[a.invalid]),console.debug("pending: "+t.stateCounters[a.pending]),console.debug("valid: "+t.stateCounters[a.valid]))}}function n(){this.inputs.forEach(function(t){t.triggerValidation()})}function r(t,e){t.stateEnum!==e.stateEnum&&(this.stateCounters[t.stateEnum]--,this.stateCounters[e.stateEnum]++)}function o(){this.groupStatesChanged&&this.groupStatesChanged(),this.groupPendingChangedListeners.forEach(function(t){!this.isPendingChangeTrueRun&&0===this.stateCounters[a.invalid]&&this.stateCounters[a.pending]>0?(t(!0),this.isPendingChangeTrueRun=!0):this.isPendingChangeTrueRun&&0===this.stateCounters[a.pending]&&(t(!1),this.isPendingChangeTrueRun=!1)},this)}return{isValid:t,isPending:e,getOnSubmit:i,triggerInputsValidation:n,updateGroupListeners:o,updateGroupStates:r}}(),e.exports=n},{"./constants":1,"./input":5,"./state-enum":6}],5:[function(t,e,i){function n(t,e,i){function n(){var t=[];return e.forEach(function(e){var i=e[0],n=e[1];t.push({name:i,run:a.build(i,n)})}),t}function o(t,e,i,n){function a(t){return"vivalid-error"===t.nextSibling.className?t.nextSibling:void 0}function s(t,i){r=document.createElement("DIV"),r.className="vivalid-error",r.style.color="#ff0000";var n=document.createTextNode(e.message);r.appendChild(n),t.parentNode.insertBefore(r,t.nextSibling)}var r;if("input"===t.nodeName.toLowerCase()&&("radio"===t.type||"checkbox"===t.type)){var o=t.parentNode.querySelectorAll('input[name="'+t.name+'"]');t=o.item(o.length-1)}e.stateEnum===n.invalid?(r=a(t),r?r.textContent=e.message:s(t,e.message),t.style.borderStyle="solid",t.style.borderColor="#ff0000"):(r=a(t),r&&(r.parentNode.removeChild(r),t.style.borderStyle="",t.style.borderColor=""))}if(-1===u.indexOf(t.nodeName.toLowerCase()))throw"only operates on the following html tags: "+u.toString();this.group={},this.el=t,this.validators=n(),this.onInputValidationResult=i||o,this.isNoneChecked=!1,this.validationState=new r("",s.valid),this.validationCycle=0,this.isChanged=!1,this.elName=t.nodeName.toLowerCase(),this.elType=t.type,this.isKeyed="textarea"===this.elName||d.indexOf(this.elType)>-1,this.activeEventType="",this._runValidatorsBounded=this.runValidators.bind(this),this.initListeners()}var a=t("./validator-repo"),s=t("./state-enum"),r=t("./validation-state"),o=t("./constants"),u=o.validInputTagNames,d=o.keyStrokedInputTypes;n.prototype=function(){function t(){if("input"===this.el.nodeName.toLowerCase()&&("radio"===this.el.type||"checkbox"===this.el.type)){var t=document.querySelector('input[name="'+this.el.name+'"]:checked');t?(this.el=t,this.isNoneChecked=!1):this.isNoneChecked=!0}}function e(){(0===this.validationCycle||this.isChanged)&&this._runValidatorsBounded()}function i(t){this.isKeyed&&t!==this.activeEventType&&(this.removeActiveEventType(),this.addEventType(t))}function n(t){this.group=t}function a(){this.addChangeListener(),this.isKeyed?this.addEventType("blur"):this.addEventType("change")}function o(t,e){this.validationCycle++,this.reBindCheckedElement();for(var i,n,a=e||0;a<this.validators.length;a++){var o=this.validators[a],u=this.isNoneChecked?"":this.el.value,d=o.run(u,this.getUpdateInputValidationResultAsync(o.name,a,this.validationCycle));if(d.stateEnum!==s.valid){i=d,n=o.name,this.changeEventType("input");break}}i=i||new r("",s.valid),this.updateInputValidationResult(i,n),this.isChanged=!1}function u(){if(this.isKeyed)this.el.addEventListener("input",function(){this.isChanged=!0},!1);else if("input"!==this.elName||"radio"!==this.elType&&"checkbox"!==this.elType)"select"===this.elName&&this.el.addEventListener("change",function(){this.isChanged=!0},!1);else for(var t=document.querySelectorAll('input[name="'+this.el.name+'"]'),e=0;e<t.length;e++)t[e].addEventListener("change",function(){this.isChanged=!0},!1)}function d(t){if(this.isKeyed)this.el.addEventListener(t,this._runValidatorsBounded,!1);else if("input"!==this.elName||"radio"!==this.elType&&"checkbox"!==this.elType)"select"===this.elName&&this.el.addEventListener(t,this._runValidatorsBounded,!1);else for(var e=document.querySelectorAll('input[name="'+this.el.name+'"]'),i=0;i<e.length;i++)e[i].addEventListener(t,this._runValidatorsBounded,!1);this.activeEventType=t}function l(){this.el.removeEventListener(this.activeEventType,this._runValidatorsBounded,!1)}function h(t,e,i){var n=this;return function(a){i&&i!==n.validationCycle||(a.stateEnum===s.valid&&e+1<n.validators.length?n._runValidatorsBounded(null,e+1):n.updateInputValidationResult(a,t))}}function c(t,e){this.group.updateGroupStates(this.validationState,t),this.group.updateGroupListeners(),this.validationState=t,this.onInputValidationResult(this.el,t,e,s)}return{reBindCheckedElement:t,triggerValidation:e,runValidators:o,changeEventType:i,initListeners:a,setGroup:n,addChangeListener:u,addEventType:d,removeActiveEventType:l,getUpdateInputValidationResultAsync:h,updateInputValidationResult:c}}(),e.exports=n},{"./constants":1,"./state-enum":6,"./validation-state":7,"./validator-repo":8}],6:[function(t,e,i){var n={invalid:1,pending:2,valid:3};e.exports=n},{}],7:[function(t,e,i){function n(t,e){this.message=t,this.stateEnum=e}e.exports=n},{}],8:[function(t,e,i){function n(t,e){if("function"!=typeof e)throw"error while trying to register a Validator: argument must be a function";o[t]=e}function a(t,e){if("function"!=typeof o[t])throw t+" does not exists. use addValidatorBuilder to add a new validation rule";return o[t](r,s,e)}var s=t("./state-enum"),r=t("./validation-state"),o={};e.exports={addBuilder:n,build:a}},{"./state-enum":6,"./validation-state":7}],vivalid:[function(t,e,i){"use strict";var n=t("./input"),a=t("./input-group"),s=t("./validator-repo"),r=t("./html-interface"),o=t("./constants");e.exports={VERSION:o.VERSION,Input:n,InputGroup:a,validatorRepo:s,htmlInterface:r,_ERROR:o.ERROR}},{"./constants":1,"./html-interface":3,"./input":5,"./input-group":4,"./validator-repo":8}]},{},["vivalid"]))("vivalid")});
!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.vivalid=t()}}(function(){return(require=function t(e,i,n){function a(r,o){if(!i[r]){if(!e[r]){var u="function"==typeof require&&require;if(!o&&u)return u(r,!0);if(s)return s(r,!0);var l=new Error("Cannot find module '"+r+"'");throw l.code="MODULE_NOT_FOUND",l}var d=i[r]={exports:{}};e[r][0].call(d.exports,function(t){var i=e[r][1][t];return a(i?i:t)},d,d.exports,t,e,i,n)}return i[r].exports}for(var s="function"==typeof require&&require,r=0;r<n.length;r++)a(n[r]);return a}({1:[function(t,e,i){e.exports={VERSION:"0.1.0",DEBUG:!1,validInputTagNames:["input","textarea","select"],keyStrokedInputTypes:["text","email","password","search","hidden"],ERROR:{mandatorySuccessFailure:"passing callbacks for onValidationSuccess and onValidationFailure is mandatory",errorInCallback:"callback failed: "}}},{}],2:[function(t,e,i){function n(t){return Array.prototype.slice.call(t)}function a(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}function s(t,e){e||(e=document);for(var i=[],n=0;n<t.length;n++)for(var a=e.getElementsByTagName(t[n]),s=0;s<a.length;s++)i.push(a[s]);return i}function r(t,e){for(;t&&t!==document;t=t.parentNode)if(h(t,e))return t;return!1}function o(t,e){return n(t.getElementsByTagName("*")).filter(function(t){return h(t,e)?!0:void 0})}function u(){var t=document.createElement("div");return t.setAttribute("data-a-b","c"),!(!t.dataset||"c"!==t.dataset.aB)}function l(t,e){return t.nodeType!==Node.ELEMENT_NODE?!1:t.getAttribute("data-"+p(e))}function d(t,e){return t.nodeType!==Node.ELEMENT_NODE?!1:t.dataset[e]}function h(t,e){return t.nodeType===Node.ELEMENT_NODE&&t.hasAttribute("data-"+p(e))}function p(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}function c(t,e){return t.classList?t.classList.contains(e):!!t.className.match(new RegExp("(\\s|^)"+e+"(\\s|$)"))}function v(t,e){t.classList?t.classList.add(e):c(t,e)||(t.className+=" "+e)}function f(t,e){if(t.classList)t.classList.remove(e);else if(c(t,e)){var i=new RegExp("(\\s|^)"+e+"(\\s|$)");t.className=t.className.replace(i," ")}}var _=u();e.exports={getDataSet:_?d:l,hasDataSet:h,getElementsByTagNames:s,getClosestParentByAttribute:r,getChildrenByAttribute:o,ready:a,toArray:n,addClass:v,removeClass:f}},{}],3:[function(t,e,i){function n(t,e){if("function"!=typeof e)throw"error while trying to add a custom callback: argument must be a function";if(f[t])throw"error while trying to add a custom callback: "+t+" already exists";f[t]=e}function a(t){function e(){inputElems=p.getElementsByTagNames(v,t).filter(function(t){return p.hasDataSet(t,"vivalidTuples")});var e=u(t,inputElems);o(t,e)}p.ready(e)}function s(){function t(){function t(t,a){if(!t)throw"an input validation is missing a group, input id: "+a.id;t._groupId||(t._groupId=e++),i[t._groupId]||(i[t._groupId]=[],n[t._groupId]=t),i[t._groupId].push(a)}var e=1,i={},n={};p.getElementsByTagNames(v).filter(function(t){return p.hasDataSet(t,"vivalidTuples")}).forEach(function(e){t(p.getClosestParentByAttribute(e,"vivalidGroup"),e)});for(var a in i){var s=u(n[a],i[a]);o(n[a],s)}}p.ready(t)}function r(t){var e=_[t];e?e.reset():console.log("could not find group named "+t)}function o(t,e){groupName=p.getDataSet(t,"vivalidGroup"),_[groupName]=e}function u(t,e){var i,n,a,s,r=e.map(l),o=[null,null],u=[null,null];if(p.hasDataSet(t,"vivalidOnValidation")&&(o=JSON.parse(p.getDataSet(t,"vivalidOnValidation")),!Array.isArray(o)||2!==o.length))throw"data-vivalid-on-validation value should be an array of size 2";if(p.hasDataSet(t,"vivalidPendingUi")&&(u=JSON.parse(p.getDataSet(t,"vivalidPendingUi")),!Array.isArray(u)||2!==u.length))throw"data-vivalid-pending-ui value should be an array of size 2";return p.hasDataSet(t,"vivalidStatesChanged")&&(i=p.getDataSet(t,"vivalidStatesChanged")),p.hasDataSet(t,"vivalidPendingChanged")&&(n=p.getDataSet(t,"vivalidPendingChanged")),p.hasDataSet(t,"vivalidBeforeValidation")&&(a=p.getDataSet(t,"vivalidBeforeValidation")),p.hasDataSet(t,"vivalidAfterValidation")&&(s=p.getDataSet(t,"vivalidAfterValidation")),new h(r,p.getChildrenByAttribute(t,"vivalidSubmit"),f[o[0]],f[o[1]],f[u[0]],f[u[1]],i,n,f[a],f[s],p.getChildrenByAttribute(t,"vivalidReset"))}function l(t){var e,i=JSON.parse(p.getDataSet(t,"vivalidTuples"));p.hasDataSet(t,"vivalidResult")&&(e=p.getDataSet(t,"vivalidResult"));var n=p.hasDataSet(t,"vivalidBlurOnly");return new d(t,i,f[e],n)}var d=t("./input"),h=t("./input-group"),p=t("./dom-helpers"),c=t("./constants"),v=c.validInputTagNames,f={},_={};e.exports={addCallback:n,initAll:s,initGroup:a,resetGroup:r}},{"./constants":1,"./dom-helpers":2,"./input":6,"./input-group":4}],4:[function(t,e,i){function n(t,e,i,n,s,o,u,l,d,h,p){if(!i||!n)throw r.mandatorySuccessFailure;this._inputs=[],this._inputElems=[],this._submitElems=[],this._resetElems=[],this._onValidationSuccess=i,this._onValidationFailure=n,this._pendingUiStart=s,this._pendingUiStop=o,this._groupStatesChanged=u,this._onBeforeValidation=d,this._onAfterValidation=h,this._groupPendingChangedListeners=[],this._groupPendingChangedListeners.push(function(t){t||this._isPendingUiStartRun&&(this._pendingUiStop.call(this._pendingUiLastSubmitElem,this._inputElems,this._submitElems,this._resetElems),this._getOnSubmit.call(this).call(this._pendingUiLastSubmitElem),this._isPendingUiStartRun=!1,this._pendingUiLastSubmitElem={})}.bind(this)),l&&this._groupPendingChangedListeners.push(l),this._stateCounters={},this._stateCounters[a.invalid]=0,this._stateCounters[a.pending]=0,this._stateCounters[a.valid]=0,this._isPendingChangeTrueRun=!1,this._isPendingUiStartRun=!1,this._pendingUiLastSubmitElem={},this._inputs=t.map(function(t){return t.setGroup(this),t},this),this._inputElems=t.map(function(t){return t.getDomElement()}),this._stateCounters[a.valid]=t.length,this._submitElems=Array.prototype.slice.call(e),this._submitElems.forEach(function(t){t.addEventListener("click",this._getOnSubmit.call(this))},this),p&&(this._resetElems=Array.prototype.slice.call(p),this._resetElems.forEach(function(t){t.addEventListener("click",this.reset.bind(this))},this))}var a=(t("./input"),t("./state-enum")),s=t("./constants").DEBUG,r=t("./constants").ERROR;n.prototype=function(){function t(){return this._triggerInputsValidation(),0===this._stateCounters[a.invalid]&&0===this._stateCounters[a.pending]}function e(){return this._triggerInputsValidation(),0===this._stateCounters[a.invalid]&&this._stateCounters[a.pending]>0}function i(){var t=this;return function(e){e&&e.preventDefault(),t._isPending()?(t._pendingUiStart.call(this,t._inputElems,t._submitElems,t._resetElems),t._isPendingUiStartRun=!0,t._pendingUiLastSubmitElem=this):t._isValid()?t._onValidationSuccess.call(this):t._onValidationFailure.call(this,t._stateCounters[a.invalid],t._stateCounters[a.pending],t._stateCounters[a.valid]),s&&(console.debug("cuurent states:"),console.debug("invalid: "+t._stateCounters[a.invalid]),console.debug("pending: "+t._stateCounters[a.pending]),console.debug("valid: "+t._stateCounters[a.valid]))}}function n(){this._inputs.forEach(function(t){t.triggerValidation()})}function r(){return this._onBeforeValidation}function o(){return this._onAfterValidation}function u(t,e){t.stateEnum!==e.stateEnum&&(this._stateCounters[t.stateEnum]--,this._stateCounters[e.stateEnum]++)}function l(){this._groupStatesChanged&&this._groupStatesChanged(),this._groupPendingChangedListeners.forEach(function(t){!this._isPendingChangeTrueRun&&0===this._stateCounters[a.invalid]&&this._stateCounters[a.pending]>0?(t(!0),this._isPendingChangeTrueRun=!0):this._isPendingChangeTrueRun&&0===this._stateCounters[a.pending]&&(t(!1),this._isPendingChangeTrueRun=!1)},this)}function d(t){t&&t.preventDefault&&t.preventDefault(),this._inputs.forEach(function(t){t.reset()}),this._stateCounters[a.invalid]=0,this._stateCounters[a.pending]=0,this._stateCounters[a.valid]=this._inputs.length}return{updateGroupListeners:l,updateGroupStates:u,reset:d,getOnBeforeValidation:r,getOnAfterValidation:o,_isValid:t,_isPending:e,_getOnSubmit:i,_triggerInputsValidation:n}}(),e.exports=n},{"./constants":1,"./input":6,"./state-enum":7}],5:[function(t,e,i){function n(){this.isNoneChecked=!1,this.validationState=new a("",s.valid),this.validationCycle=0,this.isChanged=!1,this.activeEventType=""}var a=t("./validation-state"),s=t("./state-enum");e.exports=n},{"./state-enum":7,"./validation-state":8}],6:[function(t,e,i){function n(t,e,i,n){function s(){var t=[];return e.forEach(function(e){var i=e[0],n=e[1];t.push({name:i,run:a.build(i,n)})}),t}function r(t,e,i,n){function a(t){return t.nextElementSibling&&"vivalid-error"===t.nextElementSibling.className?t.nextElementSibling:void 0}function s(t,i){r=document.createElement("DIV"),r.className="vivalid-error",r.style.color="#ff0000";var n=document.createTextNode(e.message);r.appendChild(n),t.parentNode.insertBefore(r,t.nextElementSibling)}var r;if("input"===t.nodeName.toLowerCase()&&("radio"===t.type||"checkbox"===t.type)){var o=t.parentNode.querySelectorAll('input[name="'+t.name+'"]');t=o.item(o.length-1)}e.stateEnum===n.invalid?(r=a(t),r?r.textContent=e.message:s(t,e.message),t.style.borderStyle="solid",t.style.borderColor="#ff0000",l.addClass(t,"vivalid-error-input")):(r=a(t),r&&(r.parentNode.removeChild(r),t.style.borderStyle="",t.style.borderColor="",l.removeClass(t,"vivalid-error-input")))}if(-1===d.indexOf(t.nodeName.toLowerCase()))throw"only operates on the following html tags: "+d.toString();this._el=t,this._validatorsNameOptionsTuples=e,this._onInputValidationResult=i||r,this._isBlurOnly=n,this._validators=s(),this._inputState=new o,this._elName=t.nodeName.toLowerCase(),this._elType=t.type,this._isKeyed="textarea"===this._elName||h.indexOf(this._elType)>-1,this._runValidatorsBounded=this._runValidators.bind(this),this._initListeners()}var a=t("./validator-repo"),s=t("./state-enum"),r=t("./validation-state"),o=t("./input-state"),u=t("./constants"),l=t("./dom-helpers"),d=u.validInputTagNames,h=u.keyStrokedInputTypes;n.prototype=function(){function t(){if("input"===this._el.nodeName.toLowerCase()&&("radio"===this._el.type||"checkbox"===this._el.type)){var t=document.querySelector('input[name="'+this._el.name+'"]:checked');t?(this._el=t,this._inputState.isNoneChecked=!1):this._inputState.isNoneChecked=!0}}function e(){(0===this._inputState.validationCycle||this._inputState.isChanged)&&this._runValidatorsBounded()}function i(t){this._isKeyed&&t!==this._inputState.activeEventType&&(this._removeActiveEventType(),this._addEventType(t))}function n(t){this._group=t}function a(){return this._el}function u(){this._addChangeListener(),this._isKeyed?this._addEventType("blur"):this._addEventType("change")}function l(t,e){this._inputState.validationCycle++,this._reBindCheckedElement(),"function"==typeof this._group.getOnBeforeValidation()&&this._group.getOnBeforeValidation()(this._el);for(var i,n,a=e||0;a<this._validators.length;a++){var o=this._validators[a],u=this._inputState.isNoneChecked?"":this._el.value,l=o.run(u,this._getUpdateInputValidationResultAsync(o.name,a,this._inputState.validationCycle));if(l.stateEnum!==s.valid){i=l,n=o.name,this._isBlurOnly||this._changeEventType("input");break}}i=i||new r("",s.valid),this._updateInputValidationResult(i,n),this._inputState.isChanged=!1,"function"==typeof this._group.getOnAfterValidation()&&this._group.getOnAfterValidation()(this._el)}function d(){this._removeActiveEventType(),this._initListeners(),this._inputState=new o,this._onInputValidationResult(this._el,s.valid,"",s)}function h(){var t=this;if(this._isKeyed){if(this._isBlurOnly)return;this._el.addEventListener("input",function(){t._inputState.isChanged=!0},!1)}else if("input"!==this._elName||"radio"!==this._elType&&"checkbox"!==this._elType)"select"===this._elName&&this._el.addEventListener("change",function(){t._inputState.isChanged=!0},!1);else for(var e=document.querySelectorAll('input[name="'+this._el.name+'"]'),i=0;i<e.length;i++)e[i].addEventListener("change",function(){t._inputState.isChanged=!0},!1)}function p(t){if(this._isKeyed)this._el.addEventListener(t,this._runValidatorsBounded,!1);else if("input"!==this._elName||"radio"!==this._elType&&"checkbox"!==this._elType)"select"===this._elName&&this._el.addEventListener(t,this._runValidatorsBounded,!1);else for(var e=document.querySelectorAll('input[name="'+this._el.name+'"]'),i=0;i<e.length;i++)e[i].addEventListener(t,this._runValidatorsBounded,!1);this._inputState.activeEventType=t}function c(){this._el.removeEventListener(this._inputState.activeEventType,this._runValidatorsBounded,!1)}function v(t,e,i){var n=this;return function(a){i&&i!==n._inputState.validationCycle||(a.stateEnum===s.valid&&e+1<n._validators.length?n._runValidatorsBounded(null,e+1):n._updateInputValidationResult(a,t))}}function f(t,e){this._group.updateGroupStates(this._inputState.validationState,t),this._group.updateGroupListeners(),this._inputState.validationState=t,this._onInputValidationResult(this._el,t,e,s)}return{triggerValidation:e,setGroup:n,reset:d,getDomElement:a,_reBindCheckedElement:t,_runValidators:l,_changeEventType:i,_initListeners:u,_addChangeListener:h,_addEventType:p,_removeActiveEventType:c,_getUpdateInputValidationResultAsync:v,_updateInputValidationResult:f}}(),e.exports=n},{"./constants":1,"./dom-helpers":2,"./input-state":5,"./state-enum":7,"./validation-state":8,"./validator-repo":9}],7:[function(t,e,i){var n={invalid:1,pending:2,valid:3};e.exports=n},{}],8:[function(t,e,i){function n(t,e){this.message=t,this.stateEnum=e}e.exports=n},{}],9:[function(t,e,i){function n(t,e){if("function"!=typeof e)throw"error while trying to register a Validator: argument must be a function";o[t]=e}function a(t,e){if("function"!=typeof o[t])throw t+" does not exists. use addValidatorBuilder to add a new validation rule";return o[t](r,s,e)}var s=t("./state-enum"),r=t("./validation-state"),o={};e.exports={addBuilder:n,build:a}},{"./state-enum":7,"./validation-state":8}],vivalid:[function(t,e,i){"use strict";var n=t("./input"),a=t("./input-group"),s=t("./validator-repo"),r=t("./html-interface"),o=t("./constants");e.exports={VERSION:o.VERSION,Input:n,InputGroup:a,validatorRepo:s,htmlInterface:r,_ERROR:o.ERROR}},{"./constants":1,"./html-interface":3,"./input":6,"./input-group":4,"./validator-repo":9}]},{},["vivalid"]))("vivalid")});

@@ -17,3 +17,3 @@ 'use strict';

files: [
'dist/*.js',
'dist/vivalid-bundle.js',
'test/*.js',

@@ -20,0 +20,0 @@ 'test/*.html'

@@ -5,6 +5,7 @@ module.exports = {

validInputTagNames: ['input', 'textarea', 'select'],
keyStrokedInputTypes: ['text', 'email', 'password', 'search'],
keyStrokedInputTypes: ['text', 'email', 'password', 'search', 'hidden'],
ERROR: {
mandatorySuccessFailure: 'passing callbacks for onValidationSuccess and onValidationFailure is mandatory'
}
mandatorySuccessFailure: 'passing callbacks for onValidationSuccess and onValidationFailure is mandatory',
errorInCallback: 'callback failed: '
}
};
var isDataSetSupport = testIsDataSetSupport();
function toArray(arrayLike){
function toArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);

@@ -8,3 +8,3 @@ }

function ready(fn) {
if (document.readyState != 'loading'){
if (document.readyState != 'loading') {
fn();

@@ -16,10 +16,10 @@ } else {

function getElementsByTagNames(tagsArray,obj) {
function getElementsByTagNames(tagsArray, obj) {
if (!obj) obj = document;
var results= [];
var i=0;
for (;i<tagsArray.length;i++) {
var results = [];
var i = 0;
for (; i < tagsArray.length; i++) {
var tags = obj.getElementsByTagName(tagsArray[i]);
var j=0;
for (;j<tags.length;j++) {
var j = 0;
for (; j < tags.length; j++) {
results.push(tags[j]);

@@ -34,5 +34,5 @@ }

// Get closest match
for ( ; elem && elem !== document; elem = elem.parentNode ) {
for (; elem && elem !== document; elem = elem.parentNode) {
if (hasDataSet(elem,attr)) {
if (hasDataSet(elem, attr)) {
return elem;

@@ -47,5 +47,5 @@ }

return toArray(elem.getElementsByTagName('*'))
.filter(function(el){
if (hasDataSet(el,attr)) return true;
});
.filter(function(el) {
if (hasDataSet(el, attr)) return true;
});
}

@@ -61,3 +61,3 @@

function getDataSet_unsupported(node, attr) {
if (node.nodeType !== Node.ELEMENT_NODE ) return false;
if (node.nodeType !== Node.ELEMENT_NODE) return false;

@@ -68,3 +68,3 @@ return node.getAttribute('data-' + toDashed(attr));

function getDataSet(node, attr) {
if (node.nodeType !== Node.ELEMENT_NODE ) return false;
if (node.nodeType !== Node.ELEMENT_NODE) return false;

@@ -74,3 +74,3 @@ return node.dataset[attr];

function hasDataSet(node, attr){
function hasDataSet(node, attr) {
return (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('data-' + toDashed(attr)));

@@ -85,4 +85,26 @@ }

// from http://jaketrent.com/post/addremove-classes-raw-javascript/
// used instead of classList because of lacking browser support
function hasClass(el, className) {
if (el.classList)
return el.classList.contains(className)
else
return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
}
function addClass(el, className) {
if (el.classList)
el.classList.add(className)
else if (!hasClass(el, className)) el.className += " " + className
}
function removeClass(el, className) {
if (el.classList)
el.classList.remove(className)
else if (hasClass(el, className)) {
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
el.className = el.className.replace(reg, ' ')
}
}
module.exports = {

@@ -95,5 +117,5 @@ getDataSet: isDataSetSupport ? getDataSet : getDataSet_unsupported,

ready: ready,
toArray: toArray
};
toArray: toArray,
addClass: addClass,
removeClass: removeClass
};

@@ -9,2 +9,3 @@ var Input = require('./input');

var callbacks = {}; // custom user defined callbacks
var groupNameToVivalidGroup = {};

@@ -19,3 +20,3 @@ /**

*/
function addCallback(name,fn) {
function addCallback(name, fn) {
if (typeof fn !== 'function') throw 'error while trying to add a custom callback: argument must be a function';

@@ -33,13 +34,14 @@ if (callbacks[name]) throw 'error while trying to add a custom callback: ' + name + ' already exists';

*/
function initGroup(groupElem){
function initGroup(groupElem) {
$$.ready(registerGroupFromDataAttribtues);
function registerGroupFromDataAttribtues(){
function registerGroupFromDataAttribtues() {
inputElems = $$.getElementsByTagNames(validInputTagNames,groupElem)
.filter(function(el){
return $$.hasDataSet(el,'vivalidTuples');
});
inputElems = $$.getElementsByTagNames(validInputTagNames, groupElem)
.filter(function(el) {
return $$.hasDataSet(el, 'vivalidTuples');
});
createGroupFromDataAttribtues(groupElem,inputElems);
var vivalidGroup = createGroupFromDataAttribtues(groupElem, inputElems);
addToGroupNameDictionairy(groupElem, vivalidGroup);

@@ -59,3 +61,3 @@ }

function registerAllFromDataAttribtues(){
function registerAllFromDataAttribtues() {

@@ -69,17 +71,17 @@ var _nextGroupId = 1;

$$.getElementsByTagNames(validInputTagNames)
.filter(function(el){
return $$.hasDataSet(el,'vivalidTuples');
})
.forEach(function(el){
addGroupInputs($$.getClosestParentByAttribute(el,'vivalidGroup'),el);
});
.filter(function(el) {
return $$.hasDataSet(el, 'vivalidTuples');
})
.forEach(function(el) {
addGroupInputs($$.getClosestParentByAttribute(el, 'vivalidGroup'), el);
});
for (var groupId in groupIdToInputs){
createGroupFromDataAttribtues(groupIdToGroup[groupId], groupIdToInputs[groupId]);
for (var groupId in groupIdToInputs) {
var vivalidGroup = createGroupFromDataAttribtues(groupIdToGroup[groupId], groupIdToInputs[groupId]);
addToGroupNameDictionairy(groupIdToGroup[groupId], vivalidGroup);
}
function addGroupInputs(group, input) {
function addGroupInputs(group,input){
if (!group){
if (!group) {
throw 'an input validation is missing a group, input id: ' + input.id;

@@ -90,3 +92,3 @@ }

if(!groupIdToInputs[group._groupId]){
if (!groupIdToInputs[group._groupId]) {
groupIdToInputs[group._groupId] = [];

@@ -104,55 +106,94 @@ groupIdToGroup[group._groupId] = group;

/**
* Allow's an application to reset the validations state and event listeners of a group
* @memberof! vivalid.htmlInterface
* @function
* @example vivalid.htmlInterface.resetGroup('contactGroup');
* @param {string} groupName
*/
function resetGroup(groupName) {
var vivalidGroup = groupNameToVivalidGroup[groupName];
if (vivalidGroup) {
vivalidGroup.reset();
} else {
console.log('could not find group named ' + groupName);
}
}
/**
* @private
*/
function createGroupFromDataAttribtues(groupElem,inputElems){
function addToGroupNameDictionairy(groupElem, vivalidGroup) {
groupName = $$.getDataSet(groupElem, 'vivalidGroup');
groupNameToVivalidGroup[groupName] = vivalidGroup;
}
function createGroupFromDataAttribtues(groupElem, inputElems) {
var inputs = inputElems.map(vivalidInputFromElem);
var onValidation = [null,null];
var pendingUi = [null,null];
var onValidation = [null, null];
var pendingUi = [null, null];
var groupStatesChanged;
var groupPendingChanged;
var onBeforeValidation;
var onAfterValidation;
if ($$.hasDataSet(groupElem,'vivalidOnValidation')){
onValidation = JSON.parse($$.getDataSet(groupElem,'vivalidOnValidation'));
if ($$.hasDataSet(groupElem, 'vivalidOnValidation')) {
onValidation = JSON.parse($$.getDataSet(groupElem, 'vivalidOnValidation'));
if (!Array.isArray(onValidation) || onValidation.length !== 2) throw 'data-vivalid-on-validation value should be an array of size 2';
}
if ($$.hasDataSet(groupElem,'vivalidPendingUi')){
pendingUi = JSON.parse($$.getDataSet(groupElem,'vivalidPendingUi'));
if ($$.hasDataSet(groupElem, 'vivalidPendingUi')) {
pendingUi = JSON.parse($$.getDataSet(groupElem, 'vivalidPendingUi'));
if (!Array.isArray(pendingUi) || pendingUi.length !== 2) throw 'data-vivalid-pending-ui value should be an array of size 2';
}
if ($$.hasDataSet(groupElem,'vivalidStatesChanged')){
groupStatesChanged = $$.getDataSet(groupElem,'vivalidStatesChanged');
if ($$.hasDataSet(groupElem, 'vivalidStatesChanged')) {
groupStatesChanged = $$.getDataSet(groupElem, 'vivalidStatesChanged');
}
if ($$.hasDataSet(groupElem,'vivalidPendingChanged')){
groupPendingChanged = $$.getDataSet(groupElem,'vivalidPendingChanged');
if ($$.hasDataSet(groupElem, 'vivalidPendingChanged')) {
groupPendingChanged = $$.getDataSet(groupElem, 'vivalidPendingChanged');
}
new InputGroup(inputs,
$$.getChildrenByAttribute(groupElem,'vivalidSubmit'),
callbacks[onValidation[0]],
callbacks[onValidation[1]],
callbacks[pendingUi[0]],
callbacks[pendingUi[1]],
groupStatesChanged,
groupPendingChanged);
if ($$.hasDataSet(groupElem, 'vivalidBeforeValidation')) {
onBeforeValidation = $$.getDataSet(groupElem, 'vivalidBeforeValidation');
}
if ($$.hasDataSet(groupElem, 'vivalidAfterValidation')) {
onAfterValidation = $$.getDataSet(groupElem, 'vivalidAfterValidation');
}
return new InputGroup(inputs,
$$.getChildrenByAttribute(groupElem, 'vivalidSubmit'),
callbacks[onValidation[0]],
callbacks[onValidation[1]],
callbacks[pendingUi[0]],
callbacks[pendingUi[1]],
groupStatesChanged,
groupPendingChanged,
callbacks[onBeforeValidation],
callbacks[onAfterValidation],
$$.getChildrenByAttribute(groupElem, 'vivalidReset')
);
}
/**
* @private
*/
function vivalidInputFromElem(el){
var tuplesArray = JSON.parse($$.getDataSet(el,'vivalidTuples'));
function vivalidInputFromElem(el) {
var tuplesArray = JSON.parse($$.getDataSet(el, 'vivalidTuples'));
var onInputValidationResult;
if ($$.hasDataSet(el,'vivalidResult')){
onInputValidationResult = $$.getDataSet(el,'vivalidResult');
if ($$.hasDataSet(el, 'vivalidResult')) {
onInputValidationResult = $$.getDataSet(el, 'vivalidResult');
}
return new Input(el,tuplesArray,callbacks[onInputValidationResult]);
var isBlurOnly = $$.hasDataSet(el, 'vivalidBlurOnly');
return new Input(el, tuplesArray, callbacks[onInputValidationResult], isBlurOnly);
}
/**

@@ -166,3 +207,4 @@ * The interface to use when using data attributes to define Inputs And Groups.

initAll: initAll,
initGroup: initGroup
};
initGroup: initGroup,
resetGroup: resetGroup
};

@@ -18,126 +18,137 @@ var Input = require('./input');

* @param {function} [groupPendingChanged]
* @param {function} [onBeforeValidation] Signature of {@link _internal.onBeforeValidation onBeforeValidation}. A function to be called before triggering any of the input's validators
* @param {function} [onAfterValidation] Signature of {@link _internal.onAfterValidation onAfterValidation}. A function to be called after triggering all of the input's validators
* @param {HTMLElement[]} [resetElems] an array of elements that should trigger the group's validation _reset.
*/
function InputGroup(inputsArray,submitElems,onValidationSuccess,onValidationFailure,pendingUiStart,pendingUiStop, groupStatesChanged, groupPendingChanged){
function InputGroup(inputsArray, submitElems, onValidationSuccess, onValidationFailure, pendingUiStart, pendingUiStop, groupStatesChanged, groupPendingChanged, onBeforeValidation, onAfterValidation, resetElems) {
if(!onValidationSuccess || !onValidationFailure) throw ERROR.mandatorySuccessFailure;
if (!onValidationSuccess || !onValidationFailure) throw ERROR.mandatorySuccessFailure;
this.inputs = [];
this.inputElems = [];
this.submitElems = [];
this._inputs = [];
this._inputElems = [];
this._submitElems = [];
this._resetElems = [];
this._onValidationSuccess = onValidationSuccess;
this._onValidationFailure = onValidationFailure;
this._pendingUiStart = pendingUiStart;
this._pendingUiStop = pendingUiStop;
this._groupStatesChanged = groupStatesChanged;
this._onBeforeValidation = onBeforeValidation;
this._onAfterValidation = onAfterValidation;
this.onValidationSuccess = onValidationSuccess;
this.onValidationFailure = onValidationFailure;
this.pendingUiStart = pendingUiStart;
this.pendingUiStop = pendingUiStop;
this.groupStatesChanged = groupStatesChanged;
this._groupPendingChangedListeners = [];
this._groupPendingChangedListeners.push(
function(_isPending) {
if (!_isPending) {
if (this._isPendingUiStartRun) {
this.groupPendingChangedListeners = [];
this.groupPendingChangedListeners.push(
function(isPending){
if(!isPending){
if(this.isPendingUiStartRun){
this._pendingUiStop.call(this._pendingUiLastSubmitElem, this._inputElems, this._submitElems, this._resetElems);
this.pendingUiStop.call(this.pendingUiLastSubmitElem,this.inputElems,this.submitElems);
this._getOnSubmit.call(this).call(this._pendingUiLastSubmitElem);
this.getOnSubmit.call(this).call(this.pendingUiLastSubmitElem);
this.isPendingUiStartRun = false;
this.pendingUiLastSubmitElem = {};
this._isPendingUiStartRun = false;
this._pendingUiLastSubmitElem = {};
}
}
}.bind(this)
}.bind(this)
);
if (groupPendingChanged)
this.groupPendingChangedListeners.push(groupPendingChanged);
if (groupPendingChanged)
this._groupPendingChangedListeners.push(groupPendingChanged);
this.stateCounters = {};
this.stateCounters[stateEnum.invalid] = 0;
this.stateCounters[stateEnum.pending] = 0;
this.stateCounters[stateEnum.valid] = 0;
this._stateCounters = {};
this._stateCounters[stateEnum.invalid] = 0;
this._stateCounters[stateEnum.pending] = 0;
this._stateCounters[stateEnum.valid] = 0;
this.isPendingChangeTrueRun = false;
this._isPendingChangeTrueRun = false;
this.isPendingUiStartRun = false;
this._isPendingUiStartRun = false;
this.pendingUiLastSubmitElem = {};
this._pendingUiLastSubmitElem = {};
this.inputs = inputsArray.map(function(input){
this._inputs = inputsArray.map(function(input) {
input.setGroup(this);
return input;
},this);
}, this);
this.inputElems = inputsArray
.map(function(input){
return input.el;
});
this._inputElems = inputsArray
.map(function(input) {
return input.getDomElement();
});
this._stateCounters[stateEnum.valid] = inputsArray.length;
this.stateCounters[stateEnum.valid] = inputsArray.length;
this._submitElems = Array.prototype.slice.call(submitElems);
this.submitElems = Array.prototype.slice.call((submitElems));
this._submitElems.forEach(function(submit) {
submit.addEventListener('click', this._getOnSubmit.call(this));
}, this);
this.submitElems.forEach(function(submit){
submit.addEventListener('click',this.getOnSubmit.call(this));
},this);
if (resetElems) {
this._resetElems = Array.prototype.slice.call(resetElems);
this._resetElems.forEach(function(submit) {
submit.addEventListener('click', this.reset.bind(this));
}, this);
}
}
InputGroup.prototype = (function(){
InputGroup.prototype = (function() {
return {
isValid: isValid,
isPending: isPending,
getOnSubmit: getOnSubmit,
triggerInputsValidation: triggerInputsValidation,
updateGroupListeners: updateGroupListeners,
updateGroupStates: updateGroupStates,
reset: reset,
getOnBeforeValidation: getOnBeforeValidation,
getOnAfterValidation: getOnAfterValidation,
_isValid: _isValid,
_isPending: _isPending,
_getOnSubmit: _getOnSubmit,
_triggerInputsValidation: _triggerInputsValidation
};
function isValid(){
this.triggerInputsValidation();
function _isValid() {
this._triggerInputsValidation();
return (this.stateCounters[stateEnum.invalid] === 0 &&
this.stateCounters[stateEnum.pending] === 0);
return (this._stateCounters[stateEnum.invalid] === 0 &&
this._stateCounters[stateEnum.pending] === 0);
}
function isPending(){
this.triggerInputsValidation();
function _isPending() {
this._triggerInputsValidation();
return (this.stateCounters[stateEnum.invalid] === 0 &&
this.stateCounters[stateEnum.pending] > 0);
return (this._stateCounters[stateEnum.invalid] === 0 &&
this._stateCounters[stateEnum.pending] > 0);
}
function getOnSubmit() {
function _getOnSubmit() {
var self = this;
return function (e) {
if(e) e.preventDefault();
return function(e) {
if (e) e.preventDefault();
if(self.isPending()){
self.pendingUiStart.call(this,self.inputElems,self.submitElems);
self.isPendingUiStartRun = true;
self.pendingUiLastSubmitElem = this;
if (self._isPending()) {
self._pendingUiStart.call(this, self._inputElems, self._submitElems, self._resetElems);
self._isPendingUiStartRun = true;
self._pendingUiLastSubmitElem = this;
} else if (!self._isValid()) {
self._onValidationFailure.call(this,
self._stateCounters[stateEnum.invalid],
self._stateCounters[stateEnum.pending],
self._stateCounters[stateEnum.valid]);
} else {
self._onValidationSuccess.call(this);
}
else if(!self.isValid()){
self.onValidationFailure.call(this,
self.stateCounters[stateEnum.invalid],
self.stateCounters[stateEnum.pending],
self.stateCounters[stateEnum.valid]);
}
else {
self.onValidationSuccess.call(this);
}
if (DEBUG){
if (DEBUG) {
console.debug('cuurent states:');
console.debug("invalid: " + self.stateCounters[stateEnum.invalid]);
console.debug("pending: " + self.stateCounters[stateEnum.pending]);
console.debug("valid: " + self.stateCounters[stateEnum.valid]);
console.debug("invalid: " + self._stateCounters[stateEnum.invalid]);
console.debug("pending: " + self._stateCounters[stateEnum.pending]);
console.debug("valid: " + self._stateCounters[stateEnum.valid]);
}

@@ -149,4 +160,4 @@

function triggerInputsValidation(){
this.inputs.forEach(function(input){
function _triggerInputsValidation() {
this._inputs.forEach(function(input) {
input.triggerValidation();

@@ -156,29 +167,47 @@ });

function updateGroupStates(fromInputState, toInputState){
function getOnBeforeValidation(){
return this._onBeforeValidation;
}
function getOnAfterValidation(){
return this._onAfterValidation;
}
function updateGroupStates(fromInputState, toInputState) {
if (fromInputState.stateEnum === toInputState.stateEnum) return;
this.stateCounters[fromInputState.stateEnum]--;
this.stateCounters[toInputState.stateEnum]++;
this._stateCounters[fromInputState.stateEnum]--;
this._stateCounters[toInputState.stateEnum]++;
}
function updateGroupListeners(){
if (this.groupStatesChanged) this.groupStatesChanged();
function updateGroupListeners() {
if (this._groupStatesChanged) this._groupStatesChanged();
// run both internal and user groupPendingChange functions
this.groupPendingChangedListeners.forEach(function(listener){
this._groupPendingChangedListeners.forEach(function(listener) {
if (!this.isPendingChangeTrueRun && this.stateCounters[stateEnum.invalid] === 0 && this.stateCounters[stateEnum.pending] > 0)
{
listener(true);
this.isPendingChangeTrueRun = true;
}
else if (this.isPendingChangeTrueRun && this.stateCounters[stateEnum.pending] === 0)
{
listener(false);
this.isPendingChangeTrueRun = false;
}
},this);
if (!this._isPendingChangeTrueRun && this._stateCounters[stateEnum.invalid] === 0 && this._stateCounters[stateEnum.pending] > 0) {
listener(true);
this._isPendingChangeTrueRun = true;
} else if (this._isPendingChangeTrueRun && this._stateCounters[stateEnum.pending] === 0) {
listener(false);
this._isPendingChangeTrueRun = false;
}
}, this);
}
function reset(e) {
if (e && e.preventDefault) e.preventDefault();
this._inputs.forEach(function(input) {
input.reset();
});
this._stateCounters[stateEnum.invalid] = 0;
this._stateCounters[stateEnum.pending] = 0;
this._stateCounters[stateEnum.valid] = this._inputs.length;
}
})();

@@ -218,2 +247,3 @@

* @param {HTMLElement[]} submitElems the group's submit elements
* @param {HTMLElement[]} resetElems the group's _reset elements
*/

@@ -227,2 +257,17 @@

* @param {HTMLElement[]} submitElems the group's submit elements
* @param {HTMLElement[]} resetElems the group's _reset elements
*/
/** A function to be called before triggering any of the input's validators
* @name onBeforeValidation
* @function
* @memberof! _internal
* @param {HTMLElement} el the input's DOM object.
*/
/** A function to be called after triggering all of the input's validators
* @name onAfterValidation
* @function
* @memberof! _internal
* @param {HTMLElement} el the input's DOM object.
*/
var validatorRepo = require('./validator-repo');
var stateEnum = require('./state-enum');
var ValidationState = require('./validation-state');
var InputState = require('./input-state');
var constants = require('./constants');
var $$ = require('./dom-helpers');

@@ -17,42 +19,33 @@ var validInputTagNames = constants.validInputTagNames;

* @param {function} [onInputValidationResult] Signature of {@link _internal.onInputValidationResult onInputValidationResult}. A function to handle an input state or message change. If not passed, {@link _internal.defaultOnInputValidationResult defaultOnInputValidationResult} will be used.
* @param {boolean} isBlurOnly if true, doesn't not trigger validation on 'input' or 'change' events.
*/
function Input(el, validatorsNameOptionsTuples, onInputValidationResult){
function Input(el, validatorsNameOptionsTuples, onInputValidationResult, isBlurOnly) {
if (validInputTagNames.indexOf(el.nodeName.toLowerCase()) === -1){
if (validInputTagNames.indexOf(el.nodeName.toLowerCase()) === -1) {
throw 'only operates on the following html tags: ' + validInputTagNames.toString();
}
this.group = {};
this._el = el;
this._validatorsNameOptionsTuples = validatorsNameOptionsTuples;
this._onInputValidationResult = onInputValidationResult || defaultOnInputValidationResult;
this._isBlurOnly = isBlurOnly;
this._validators = buildValidators();
this._inputState = new InputState();
this._elName = el.nodeName.toLowerCase();
this._elType = el.type;
this._isKeyed = (this._elName === 'textarea' || keyStrokedInputTypes.indexOf(this._elType) > -1);
this._runValidatorsBounded = this._runValidators.bind(this);
this.el = el;
this.validators = buildValidators();
this.onInputValidationResult = onInputValidationResult || defaultOnInputValidationResult;
this.isNoneChecked = false;
this._initListeners();
this.validationState = new ValidationState('', stateEnum.valid);
this.validationCycle = 0;
this.isChanged = false;
this.elName = el.nodeName.toLowerCase();
this.elType = el.type;
this.isKeyed = (this.elName === 'textarea' || keyStrokedInputTypes.indexOf(this.elType) > -1);
this.activeEventType = '';
this._runValidatorsBounded = this.runValidators.bind(this);
this.initListeners();
function buildValidators(){
function buildValidators() {
var result = [];
validatorsNameOptionsTuples.forEach(function(validatorsNameOptionsTuple){
validatorsNameOptionsTuples.forEach(function(validatorsNameOptionsTuple) {
var validatorName = validatorsNameOptionsTuple[0];
var validatorOptions = validatorsNameOptionsTuple[1];
result.push(
{
name: validatorName,
run: validatorRepo.build(validatorName,validatorOptions)
}
);
result.push({
name: validatorName,
run: validatorRepo.build(validatorName, validatorOptions)
});

@@ -64,3 +57,2 @@ });

/** The default {@link _internal.onInputValidationResult onInputValidationResult} used when {@link vivalid.Input} is initiated without a 3rd parameter

@@ -71,3 +63,3 @@ * @name defaultOnInputValidationResult

*/
function defaultOnInputValidationResult(el,validationsResult,validatorName,stateEnum) {
function defaultOnInputValidationResult(el, validationsResult, validatorName, stateEnum) {

@@ -77,29 +69,28 @@ var errorDiv;

// for radio buttons and checkboxes: get the last element in group by name
if ((el.nodeName.toLowerCase() === 'input' && (el.type === 'radio' || el.type === 'checkbox' ))){
if ((el.nodeName.toLowerCase() === 'input' && (el.type === 'radio' || el.type === 'checkbox'))) {
var getAllByName = el.parentNode.querySelectorAll('input[name="'+el.name+'"]');
var getAllByName = el.parentNode.querySelectorAll('input[name="' + el.name + '"]');
el = getAllByName.item(getAllByName.length-1);
el = getAllByName.item(getAllByName.length - 1);
}
if(validationsResult.stateEnum === stateEnum.invalid){
if (validationsResult.stateEnum === stateEnum.invalid) {
errorDiv = getExistingErrorDiv(el);
if(errorDiv) {
if (errorDiv) {
errorDiv.textContent = validationsResult.message;
} else {
appendNewErrorDiv(el, validationsResult.message);
}
else {
appendNewErrorDiv(el,validationsResult.message);
}
el.style.borderStyle = "solid";
el.style.borderColor = "#ff0000";
}
else {
$$.addClass(el, "vivalid-error-input");
} else {
errorDiv = getExistingErrorDiv(el);
if(errorDiv) {
if (errorDiv) {
errorDiv.parentNode.removeChild(errorDiv);
el.style.borderStyle = "";
el.style.borderColor = "";
$$.removeClass(el, "vivalid-error-input");
}

@@ -109,4 +100,4 @@ }

function getExistingErrorDiv(el) {
if (el.nextSibling.className === "vivalid-error") {
return el.nextSibling;
if (el.nextElementSibling && el.nextElementSibling.className === "vivalid-error") {
return el.nextElementSibling;
}

@@ -116,9 +107,9 @@

function appendNewErrorDiv(el,message) {
errorDiv = document.createElement("DIV");
function appendNewErrorDiv(el, message) {
errorDiv = document.createElement("DIV");
errorDiv.className = "vivalid-error";
errorDiv.style.color = "#ff0000";
var t = document.createTextNode(validationsResult.message);
errorDiv.appendChild(t);
el.parentNode.insertBefore(errorDiv, el.nextSibling);
var t = document.createTextNode(validationsResult.message);
errorDiv.appendChild(t);
el.parentNode.insertBefore(errorDiv, el.nextElementSibling);
}

@@ -133,37 +124,36 @@

return {
reBindCheckedElement: reBindCheckedElement,
triggerValidation: triggerValidation,
runValidators: runValidators,
changeEventType: changeEventType,
initListeners: initListeners,
setGroup: setGroup,
addChangeListener: addChangeListener,
addEventType: addEventType,
removeActiveEventType: removeActiveEventType,
getUpdateInputValidationResultAsync: getUpdateInputValidationResultAsync,
updateInputValidationResult: updateInputValidationResult
reset: reset,
getDomElement: getDomElement,
_reBindCheckedElement: _reBindCheckedElement,
_runValidators: _runValidators,
_changeEventType: _changeEventType,
_initListeners: _initListeners,
_addChangeListener: _addChangeListener,
_addEventType: _addEventType,
_removeActiveEventType: _removeActiveEventType,
_getUpdateInputValidationResultAsync: _getUpdateInputValidationResultAsync,
_updateInputValidationResult: _updateInputValidationResult
};
// public
function _reBindCheckedElement() {
function reBindCheckedElement(){
// reBind only radio and checkbox buttons
if (!(this.el.nodeName.toLowerCase() === 'input' && (this.el.type === 'radio' || this.el.type === 'checkbox' ))){
if (!(this._el.nodeName.toLowerCase() === 'input' && (this._el.type === 'radio' || this._el.type === 'checkbox'))) {
return;
}
var checkedElement = document.querySelector('input[name="'+this.el.name+'"]:checked');
if (checkedElement){
this.el = checkedElement;
this.isNoneChecked = false;
var checkedElement = document.querySelector('input[name="' + this._el.name + '"]:checked');
if (checkedElement) {
this._el = checkedElement;
this._inputState.isNoneChecked = false;
} else {
this._inputState.isNoneChecked = true;
}
else{
this.isNoneChecked = true;
}
}
function triggerValidation(){
if (this.validationCycle === 0 || this.isChanged) {
function triggerValidation() {
if (this._inputState.validationCycle === 0 || this._inputState.isChanged) {
this._runValidatorsBounded();

@@ -173,45 +163,53 @@ }

function changeEventType(eventType) {
if (!this.isKeyed) return;
if (eventType === this.activeEventType) return;
this.removeActiveEventType();
this.addEventType(eventType);
function _changeEventType(eventType) {
if (!this._isKeyed) return;
if (eventType === this._inputState.activeEventType) return;
this._removeActiveEventType();
this._addEventType(eventType);
}
function setGroup(value) {
this.group = value;
this._group = value;
}
function initListeners() {
function getDomElement(){
return this._el;
}
this.addChangeListener();
if (this.isKeyed){
this.addEventType('blur');
function _initListeners() {
this._addChangeListener();
if (this._isKeyed) {
this._addEventType('blur');
} else {
this._addEventType('change');
}
else {
this.addEventType('change');
}
}
function runValidators(event, fromIndex){
function _runValidators(event, fromIndex) {
this.validationCycle++;
this.reBindCheckedElement();
this._inputState.validationCycle++;
this._reBindCheckedElement();
if (typeof this._group.getOnBeforeValidation() === 'function') {
this._group.getOnBeforeValidation()(this._el);
}
var validationsResult, validatorName;
var i = fromIndex || 0;
for (; i < this.validators.length; i++){
var validator = this.validators[i];
var elementValue = this.isNoneChecked ? '' : this.el.value;
for (; i < this._validators.length; i++) {
var validator = this._validators[i];
var elementValue = this._inputState.isNoneChecked ? '' : this._el.value;
// if async, then return a pending enum with empty message and call the callback with result once ready
var validatorResult = validator.run(elementValue, this.getUpdateInputValidationResultAsync(validator.name, i, this.validationCycle));
if (validatorResult.stateEnum !== stateEnum.valid)
{
validationsResult = validatorResult;
validatorName = validator.name;
this.changeEventType('input'); //TODO: call only once?
break;
var validatorResult = validator.run(elementValue, this._getUpdateInputValidationResultAsync(validator.name, i, this._inputState.validationCycle));
if (validatorResult.stateEnum !== stateEnum.valid) {
validationsResult = validatorResult;
validatorName = validator.name;
if (!this._isBlurOnly) {
this._changeEventType('input'); //TODO: call only once?
}
break;
}

@@ -221,64 +219,78 @@ }

validationsResult = validationsResult || new ValidationState('', stateEnum.valid);
this.updateInputValidationResult(validationsResult,validatorName);
this._updateInputValidationResult(validationsResult, validatorName);
// new...
this.isChanged = false; // TODO: move to top of function
this._inputState.isChanged = false; // TODO: move to top of function?
if (typeof this._group.getOnAfterValidation() === 'function') {
this._group.getOnAfterValidation()(this._el);
}
}
// private
function reset() {
this._removeActiveEventType();
this._initListeners();
this._inputState = new InputState();
this._onInputValidationResult(this._el, stateEnum.valid, '', stateEnum); // called with valid state to clear any previous errors UI
}
function addChangeListener(){
if (this.isKeyed){
this.el.addEventListener('input', function () { this.isChanged = true;}, false);
}
function _addChangeListener() {
else if (this.elName === 'input' && (this.elType === 'radio' || this.elType === 'checkbox')){
var self = this;
var groupElements = document.querySelectorAll('input[name="'+this.el.name+'"]');
if (this._isKeyed) {
if (this._isBlurOnly) {
return;
} else {
this._el.addEventListener('input', function() {
self._inputState.isChanged = true;
}, false);
}
} else if (this._elName === 'input' && (this._elType === 'radio' || this._elType === 'checkbox')) {
var i=0;
for (; i <groupElements.length ; i++) {
groupElements[i].addEventListener('change', function (){this.isChanged = true;}, false);
var groupElements = document.querySelectorAll('input[name="' + this._el.name + '"]');
var i = 0;
for (; i < groupElements.length; i++) {
groupElements[i].addEventListener('change', function() {
self._inputState.isChanged = true;
}, false);
}
} else if (this._elName === 'select') {
this._el.addEventListener('change', function() {
self._inputState.isChanged = true;
}, false);
}
else if(this.elName === 'select'){
this.el.addEventListener('change', function (){this.isChanged = true;}, false);
}
}
function addEventType(eventType) {
if (this.isKeyed){
this.el.addEventListener(eventType, this._runValidatorsBounded, false);
}
function _addEventType(eventType) {
if (this._isKeyed) {
this._el.addEventListener(eventType, this._runValidatorsBounded, false);
} else if (this._elName === 'input' && (this._elType === 'radio' || this._elType === 'checkbox')) {
else if (this.elName === 'input' && (this.elType === 'radio' || this.elType === 'checkbox')){
var groupElements = document.querySelectorAll('input[name="' + this._el.name + '"]');
var groupElements = document.querySelectorAll('input[name="'+this.el.name+'"]');
var i=0;
for (; i <groupElements.length ; i++) {
var i = 0;
for (; i < groupElements.length; i++) {
groupElements[i].addEventListener(eventType, this._runValidatorsBounded, false);
}
} else if (this._elName === 'select') {
this._el.addEventListener(eventType, this._runValidatorsBounded, false);
}
else if(this.elName === 'select'){
this.el.addEventListener(eventType, this._runValidatorsBounded, false);
}
this.activeEventType = eventType;
this._inputState.activeEventType = eventType;
}
function removeActiveEventType() {
this.el.removeEventListener(this.activeEventType, this._runValidatorsBounded, false);
function _removeActiveEventType() {
this._el.removeEventListener(this._inputState.activeEventType, this._runValidatorsBounded, false);
}
function getUpdateInputValidationResultAsync(validatorName, validatorIndex, asyncValidationCycle) {
function _getUpdateInputValidationResultAsync(validatorName, validatorIndex, asyncValidationCycle) {
var self = this;
return function(validatorResult){
return function(validatorResult) {
// guard against updating async validations from old cycles
if(asyncValidationCycle && asyncValidationCycle !== self.validationCycle){
if (asyncValidationCycle && asyncValidationCycle !== self._inputState.validationCycle) {
return;

@@ -288,9 +300,7 @@ }

// if pending turned to be valid, and there are more validation to run, run them:
if (validatorResult.stateEnum === stateEnum.valid && validatorIndex+1 < self.validators.length){
self._runValidatorsBounded(null,validatorIndex+1);
if (validatorResult.stateEnum === stateEnum.valid && validatorIndex + 1 < self._validators.length) {
self._runValidatorsBounded(null, validatorIndex + 1);
} else {
self._updateInputValidationResult(validatorResult, validatorName);
}
else {
self.updateInputValidationResult(validatorResult,validatorName);
}
};

@@ -300,9 +310,9 @@

function updateInputValidationResult(validationsResult,validatorName) {
function _updateInputValidationResult(validationsResult, validatorName) {
this.group.updateGroupStates(this.validationState, validationsResult); // filter equal state at caller
this.group.updateGroupListeners();
this._group.updateGroupStates(this._inputState.validationState, validationsResult); // filter equal state at caller
this._group.updateGroupListeners();
this.validationState = validationsResult;
this.onInputValidationResult(this.el,validationsResult,validatorName,stateEnum);
this._inputState.validationState = validationsResult;
this._onInputValidationResult(this._el, validationsResult, validatorName, stateEnum);

@@ -309,0 +319,0 @@ }

@@ -13,2 +13,2 @@ /**

module.exports = stateEnum;
module.exports = stateEnum;

@@ -9,3 +9,3 @@ /**

*/
function ValidationState(message, stateEnum){
function ValidationState(message, stateEnum) {
this.message = message;

@@ -19,2 +19,2 @@ this.stateEnum = stateEnum;

* @namespace _internal
*/
*/

@@ -14,5 +14,5 @@ var stateEnum = require('./state-enum');

*/
function addBuilder(name, fn){
function addBuilder(name, fn) {
if (typeof fn !== 'function') throw 'error while trying to register a Validator: argument must be a function';
validatorBuildersRepository [name] = fn;
validatorBuildersRepository[name] = fn;
}

@@ -28,3 +28,3 @@

*/
function build(validatorName,validatorOptions) {
function build(validatorName, validatorOptions) {

@@ -35,3 +35,3 @@ if (typeof validatorBuildersRepository[validatorName] !== 'function') {

return validatorBuildersRepository[validatorName](ValidationState,stateEnum,validatorOptions);
return validatorBuildersRepository[validatorName](ValidationState, stateEnum, validatorOptions);
}

@@ -47,2 +47,2 @@

build: build
};
};
{
"name": "vivalid",
"version": "0.1.4",
"version": "0.2.0",
"description": "client side validations with async (client-server) support",

@@ -28,2 +28,3 @@ "main": "./lib/vivalid.js",

"karma-phantomjs-launcher": "^0.1.4",
"karma-sinon": "^1.0.4",
"mocha": "^2.1.0",

@@ -30,0 +31,0 @@ "vinyl-buffer": "^1.0.0",

@@ -13,11 +13,34 @@ # ViValid [![Build Status](https://travis-ci.org/pazams/vivalid.svg)](https://travis-ci.org/pazams/vivalid)

Sync rules return with `stateEnum.invalid` or `stateEnum.valid`
Asyc rules return with `stateEnum.pending`, and also call a callback with `stateEnum.invalid` or `stateEnum.valid` when ready.
Async rules return with `stateEnum.pending`, and also call a callback with `stateEnum.invalid` or `stateEnum.valid` when ready. Callbacks from previous cycles get filtered out, which helps when AJAX responses are out of order from multiple parallel requests.
Demo: https://embed.plnkr.co/daGXkk1RrdRxjFWhQgwX/
### Full UI control
Either edit the UI through a CSS rule for `.vivalid-error` , or gain complete control by passing a [callback](http://pazams.github.io/vivalid/documentation/-_internal.html#..onInputValidationResult) that will be called with a DOM element, validation message, and validation state.
### Data attributes interface
Use this library with full javascript interface, or the data attributes html interface (with js to only define callbacks).
Either edit the UI through a CSS rule for `.vivalid-error` and `.vivalid-error-input` classes , or gain complete control by passing a [callback](http://pazams.github.io/vivalid/documentation/-_internal.html#..onInputValidationResult) that will be called with a DOM element, validation message, and validation state.
Demo: https://embed.plnkr.co/JsA852mcYTTUHPoUf3F1/
### JS api or data attributes interface
Use this library with full [javascript api](http://pazams.github.io/vivalid/documentation/vivalid.html), or the data attributes html interface (with js to only define callbacks).
Data attribute | defined on | notes
-------------- | --------------| -----
data-vivalid-group | group | defines an input group. may be applied on (but not restricted to) `<form>` elements. the value may used with [htmlInterface api](http://www.pazams.com/vivalid/documentation/vivalid.htmlInterface.html) in resetGroup.
data-vivalid-on-validation | group | references success and failure callbacks.
data-vivalid-pending-ui | group | references start and stop pending ui callbacks. used only in groups which contain async validators.
data-vivalid-after-validation | group | references before and after validation callbacks. Defined on group level, but gets called on each input individually.
data-vivalid-tuples | input | an array of [validatorsNameOptionsTuple] (http://www.pazams.com/vivalid/documentation/-_internal.html#..validatorsNameOptionsTuple) in JSON format.
data-vivalid-blur-only | input | marks an input to be evaluated on `blur` event only, as opposed to default way: first evaluated on `blur` event, and after one event, evaluate on `input` event.
data-vivalid-submit | button | triggers the group validation. Taking further action such as submitting a form, should be defined on the group's validation success callback. `preventDefault()` is applied to the DOM event.
data-vivalid-reset | button | resets the state of the validation. this does not reset the form/group or clear its contents. This functionality, through the js api, is useful for SPA's. `preventDefault()` is applied to the DOM event.
### Separation of validator rules
No validator rules are included. Write your own, or also choose to include common ones from https://github.com/pazams/vivalid-rules-core
No validator rules are included. Choose to include common ones from https://github.com/pazams/vivalid-rules-core, or write your own.
Demo: https://embed.plnkr.co/Q6bTpj7PhqbQTBUZt166/
### Support for radio buttons and checkboxes
see [here](http://pazams.github.io/vivalid/documentation/vivalid.Input.html)
Demo: https://embed.plnkr.co/xtxe1YfsmxRR9hacZ3sn/

@@ -28,4 +51,4 @@

### Manual:
* save and include: https://raw.githubusercontent.com/pazams/vivalid/master/dist/vivalid-bundle.min.js
* (optional) https://raw.githubusercontent.com/pazams/vivalid-rules-core/master/dist/vivalid-rules-core-bundle.min.js
* save and include: https://cdn.rawgit.com/pazams/vivalid/0.2.0/dist/vivalid-bundle.min.js
* (optional) https://cdn.rawgit.com/pazams/vivalid-rules-core/0.2.0/dist/vivalid-rules-core-bundle.min.js

@@ -40,146 +63,3 @@ ### npm:

## JS interface
See js [documentation](http://pazams.github.io/vivalid/documentation/vivalid.html)
## Data attributes html interface
See js [documentation](http://pazams.github.io/vivalid/documentation/vivalid.html), plus a short example ([live here](http://pazams.github.io/vivalid/demos/1/)):
**index.html**
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://rawgit.com/pazams/vivalid/master/dist/vivalid-bundle.js"></script>
<script src="https://rawgit.com/pazams/vivalid-rules-core/master/dist/vivalid-rules-core-bundle.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<!-- inline css styles to make the exmaple work as a standalone without a css file -->
<h1>Html data attributes interface</h1>
<form id="MainForm" data-vivalid-group data-vivalid-on-validation='["onValidationSuccess", "onValidationFailure"]' data-vivalid-pending-ui='["pendingUiStart", "pendingUiStop"]'>
<div>
<input type="text" placeholder="First Name" data-vivalid-tuples='[["required",{}],["betweenlength",{"min": 4, "max": 10}]]' />
</div>
<div style="position: relative;">
<input type="text" placeholder="User Name (type 'bob' and press send)"
data-vivalid-tuples='[["required",{}],["exisitingUserBob",{}]]'
data-vivalid-result='onInputValidationResult'
/>
<div class="js-message" style="background-color: blue; color: white; display: none; position: absolute; z-index: 1; padding: 6px; left: 122px; top: 0;">
</div>
</div>
<div>
<input id="SendButton" type="button" value="send" data-vivalid-submit />
</div>
<div id="MessageLogs">
</div>
</form>
</body>
</html>
```
**script.js**
```javascript
var addValidatorBuilder = vivalid.validatorRepo.addBuilder;
var addCallback = vivalid.htmlInterface.addCallback;
var initAll = vivalid.htmlInterface.initAll;
function messageLog(message){
var log = document.getElementById('MessageLogs');
log.innerHTML = log.innerHTML +'<br/>' + message;
}
addValidatorBuilder('exisitingUserBob', function(ValidationState, stateEnum, options) {
return function(value, callback) {
var msg = 'user bob exists';
setTimeout(dummyServiceCall, 3000);
return new ValidationState('', stateEnum.pending);
function dummyServiceCall() {
if (value.indexOf('bob') !== -1) {
callback(new ValidationState(msg, stateEnum.invalid));
} else {
callback(new ValidationState('', stateEnum.valid));
}
}
};
});
addCallback('onValidationSuccess', function() {
messageLog('HOORAY!!!! input group is valid and form will submit');
});
addCallback('onValidationFailure', function(invalid, pending, valid) {
messageLog('input group is invalid!: ' + invalid + ' invalid, ' + pending + ' pending, and ' + valid + ' valid ');
});
addCallback('pendingUiStart', function(inputElems, submitElems) {
messageLog('pendingUiStart');
inputElems.forEach(function(input) {
input.disabled = true;
});
submitElems.forEach(function(submit) {
submit.disabled = true;
});
this.style.backgroundColor = 'green';
});
addCallback('pendingUiStop', function(inputElems, submitElems) {
messageLog('pendingUiStop');
inputElems.forEach(function(input) {
input.disabled = false;
});
submitElems.forEach(function(submit) {
submit.disabled = false;
});
this.style.backgroundColor = 'blue';
});
// show casing a callback for custom UI
addCallback('onInputValidationResult', function(el, validationsResult, validatorName, stateEnum) {
var msgEl = el.parentNode.querySelector('.js-message');
var displayEl = msgEl;
if (validationsResult.stateEnum === stateEnum.invalid) {
displayEl.style.display = 'block';
msgEl.innerHTML = validationsResult.message;
} else {
displayEl.style.display = 'none';
msgEl.innerHTML = '';
}
});
initAll();
```
## Contributers
## Contributors
read [this](https://github.com/pazams/vivalid/issues/1) before attempting to `gulp build`
## More demos
Coming soon

@@ -5,3 +5,3 @@ 'use strict';

var Name,Email,SendButton,Form;
var Name,Email,SendButton,ResetButton,Form;

@@ -30,2 +30,3 @@ var clickEvent = new MouseEvent('click', {

SendButton = document.getElementById('SendButton'+formNumber);
ResetButton = document.getElementById('ResetButton'+formNumber);
Form = document.getElementById('Form'+formNumber);

@@ -36,3 +37,3 @@ }

function isErrorDisplayed(el) {
return (el.nextSibling.className === "vivalid-error");
return (el.nextElementSibling && el.nextElementSibling.className === "vivalid-error");
}

@@ -44,2 +45,3 @@

var initGroup = vivalid.htmlInterface.initGroup;
var resetGroup = vivalid.htmlInterface.resetGroup;
var ERROR = vivalid._ERROR;

@@ -49,3 +51,2 @@

addValidatorBuilder('required',function(ValidationState,stateEnum,options){

@@ -85,3 +86,26 @@

addValidatorBuilder('exisitingUserBob', function(ValidationState, stateEnum, options) {
return function(value, callback) {
var msg = 'user bob exists';
setTimeout(dummyServiceCall, 0);
return new ValidationState('', stateEnum.pending);
function dummyServiceCall() {
if (value.indexOf('bob') !== -1) {
callback(new ValidationState(msg, stateEnum.invalid));
} else {
callback(new ValidationState('', stateEnum.valid));
}
}
};
});
addCallback('onValidationSuccess', function(){

@@ -91,16 +115,51 @@ });

addCallback('onValidationFailure', function(invalid,pending,valid){
if(typeof invalid !== 'number' || typeof pending !== 'number' || typeof valid !== 'number'){
throw ERROR+"onValidationFailure";
}
});
addCallback('pendingUiStart', function(inputElems,submitElems){
addCallback('pendingUiStart', function(inputElems,submitElems,resetElems){
try{
inputElems.concat(submitElems).concat(resetElems).forEach(function(input) {
input.disabled = true;
});
this.disabled = true;
}
catch(e){
throw ERROR+"pendingUiStart";
}
finally {
// done();
}
});
addCallback('pendingUiStop' ,function(inputElems,submitElems){
addCallback('pendingUiStop' ,function(inputElems,submitElems,resetElems){
try{
inputElems.concat(submitElems).concat(resetElems).forEach(function(input) {
input.disabled = true;
});
this.disabled = true;
}
catch(e){
throw ERROR+"pendingUiStop";
}
finally {
// done();
}
});
addCallback('onBeforeValidation' ,function(input){
});
addCallback('onAfterValidation' ,function(input){
});
});
describe('Async', function() {
describe('Sanity', function() {

@@ -169,7 +228,60 @@ // inject the HTML fixture for the tests

// TODO: the function returned from _getUpdateInputValidationResultAsync does not get called- since tests do not wait for done()
describe('Async', function() {
describe('Sync', function() {
// inject the HTML fixture for the tests
beforeEach(function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3
fixture.base = 'test';
fixture.load('integrationTest.fixture.html');
it('should not require passing pendingUi callbacks', function() {
selectFormElements(6);
initGroup(Form);
});
// remove the html fixture from the DOM
afterEach(function() {
fixture.cleanup();
});
// UX/UI
it('should call pendingUiStart and pendingUiStop, and executes them without errors', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Email.dispatchEvent(clickEvent);
Email.value = "bob@email.com";
SendButton.click();
// relays on done() inside these functions to test no errors
// TODO: use sinon...
expect(true).to.be.ok;
});
it('should call pendingUiStart and pendingUiStop, if pending resolved to be valid, and there are more validation to run, run them', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Email.dispatchEvent(clickEvent);
Email.value = "john@email.com";
SendButton.click();
// relays on done() inside these functions to test no errors
// TODO: use sinon...
expect(true).to.be.ok;
});
});
describe('optional & required callbacks', function() {
it('should not require passing pendingUi or onBefore/onAfterValidation callbacks', function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3

@@ -203,7 +315,218 @@ fixture.base = 'test';

});
/* use sinon to test these callbacks */
/*
describe('onBefore/onAfter Validation', function() {
it('should call onBeforeValidation', function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3
fixture.base = 'test';
fixture.load('integrationTest.fixture.html');
selectFormElements(5);
initGroup(Form);
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
// works, but improve to test the throws of initAll();
// instead of dummy test below
expect(true).to.be.ok;
fixture.cleanup();
});
it('should call onAfterValidation', function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3
fixture.base = 'test';
fixture.load('integrationTest.fixture.html');
selectFormElements(5);
initGroup(Form);
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
// works, but improve to test the throws of initAll();
// instead of dummy test below
expect(true).to.be.ok;
fixture.cleanup();
});
});
*/
describe('Blur-only inputs', function() {
// inject the HTML fixture for the tests
beforeEach(function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3
fixture.base = 'test';
fixture.load('integrationTest.fixture.html');
selectFormElements(4);
initGroup(Form);
});
// remove the html fixture from the DOM
afterEach(function() {
fixture.cleanup();
});
// UX/UI
it('Blur-only: should not display any errors before interacting with the inputs', function() {
expect(!isErrorDisplayed(Name) && !isErrorDisplayed(Email)).to.be.ok;
});
it('Blur-only: should display all invalid errors after submitting, even without interacting with the inputs', function() {
SendButton.click();
expect(isErrorDisplayed(Name) && isErrorDisplayed(Email)).to.be.ok;
});
it('Blur-only: should not display an error before first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
expect(!isErrorDisplayed(Name)).to.be.ok;
});
it('Blur-only: should display an error after first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
expect(isErrorDisplayed(Name)).to.be.ok;
});
it('Blur-only: should ***NOT*** respond to fixes as they are typed, after first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
Name.value = "John";
Name.dispatchEvent(inputEvent);
expect(isErrorDisplayed(Name)).to.be.ok;
});
});
describe('Group reset', function() {
resetMode('groupName');
resetMode('button');
function resetMode(mode){
// inject the HTML fixture for the tests
beforeEach(function() {
// Why this line? See: https://github.com/billtrik/karma-fixture/issues/3
fixture.base = 'test';
fixture.load('integrationTest.fixture.html');
selectFormElements(1);
initGroup(Form);
interactAndReset();
});
// remove the html fixture from the DOM
afterEach(function() {
fixture.cleanup();
});
function interactAndReset(){
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
Name.value = "John Doe2";
Name.dispatchEvent(inputEvent);
switch(mode){
case "groupName":
resetGroup('FirstGroup');
break;
case "button":
ResetButton.click();
break;
}
}
// UX/UI
it('after error displayed and reset- should not display any errors before interacting with the inputs', function() {
expect(!isErrorDisplayed(Name) && !isErrorDisplayed(Email)).to.be.ok;
});
it('after error displayed and reset- should display all invalid errors after submitting, even without interacting with the inputs', function() {
SendButton.click();
expect(isErrorDisplayed(Name) && isErrorDisplayed(Email)).to.be.ok;
});
it('after error displayed and reset- should not display an error before first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
expect(!isErrorDisplayed(Name)).to.be.ok;
});
it('after error displayed and reset- should display an error after first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
expect(isErrorDisplayed(Name)).to.be.ok;
});
it('after error displayed and reset- should respond to fixes as they are typed, after first blur (focus out)', function() {
Name.dispatchEvent(clickEvent);
Name.value = "John Doe";
Name.dispatchEvent(blurEvent);
Name.value = "John";
Name.dispatchEvent(inputEvent);
expect(!isErrorDisplayed(Name)).to.be.ok;
});
}
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc