Skip to content
Permalink
039588f4b1
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
368 lines (332 sloc) 13.1 KB
/*! H5F
* https://github.com/ryanseddon/H5F/
* Copyright (c) Ryan Seddon | Licensed MIT */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// Browser globals
root.H5F = factory();
}
}(this, function () {
var d = document,
field = d.createElement("input"),
emailPatt = /^[a-zA-Z0-9.!#$%&'*+-\/=?\^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
urlPatt = /[a-z][\-\.+a-z]*:\/\//i,
nodes = /^(input|select|textarea)$/i,
isSubmit, bypassSubmit, usrPatt, curEvt, args,
// Methods
setup, validation, validity, checkField, bypassChecks, checkValidity, setCustomValidity, support, pattern, placeholder, range, required, valueMissing, listen, unlisten, preventActions, getTarget, addClass, removeClass, isHostMethod, isSiblingChecked;
setup = function(form, settings) {
var isCollection = !form.nodeType || false;
var opts = {
validClass : "valid",
invalidClass : "error",
requiredClass : "required",
placeholderClass : "placeholder",
onSubmit : Function.prototype,
onInvalid : Function.prototype
};
if(typeof settings === "object") {
for (var i in opts) {
if(typeof settings[i] === "undefined") { settings[i] = opts[i]; }
}
}
args = settings || opts;
if(isCollection) {
for(var k=0,len=form.length;k<len;k++) {
validation(form[k]);
}
} else {
validation(form);
}
};
validation = function(form) {
var f = form.elements,
flen = f.length,
isRequired,
noValidate = !!(form.attributes["novalidate"]);
listen(form,"invalid",checkField,true);
listen(form,"blur",checkField,true);
listen(form,"input",checkField,true);
listen(form,"keyup",checkField,true);
listen(form,"focus",checkField,true);
listen(form,"change",checkField,true);
listen(form,"click",bypassChecks,true);
listen(form,"submit",function(e){
isSubmit = true;
if(!bypassSubmit && !noValidate && !form.checkValidity()) {
preventActions(e);
return;
}
args.onSubmit.call(form, e);
},false);
if(!support()) {
form.checkValidity = function() { return checkValidity(form); };
while(flen--) {
isRequired = !!(f[flen].attributes["required"]);
// Firefox includes fieldsets inside elements nodelist so we filter it out.
if(f[flen].nodeName.toLowerCase() !== "fieldset") {
validity(f[flen]); // Add validity object to field
}
}
}
};
validity = function(el) {
var elem = el,
missing = valueMissing(elem),
attrs = {
type: elem.getAttribute("type"),
pattern: elem.getAttribute("pattern"),
placeholder: elem.getAttribute("placeholder")
},
isType = /^(email|url)$/i,
evt = /^(input|keyup)$/i,
fType = ((isType.test(attrs.type)) ? attrs.type : ((attrs.pattern) ? attrs.pattern : false)),
patt = pattern(elem,fType),
step = range(elem,"step"),
min = range(elem,"min"),
max = range(elem,"max"),
customError = !( elem.validationMessage === "" || elem.validationMessage === undefined );
elem.checkValidity = function() { return checkValidity.call(this,elem); };
elem.setCustomValidity = function(msg) { setCustomValidity.call(elem,msg); };
elem.validity = {
valueMissing: missing,
patternMismatch: patt,
rangeUnderflow: min,
rangeOverflow: max,
stepMismatch: step,
customError: customError,
valid: (!missing && !patt && !step && !min && !max && !customError)
};
if(attrs.placeholder && !evt.test(curEvt)) { placeholder(elem); }
};
checkField = function(e) {
var el = getTarget(e) || e, // checkValidity method passes element not event
events = /^(input|keyup|focusin|focus|change)$/i,
ignoredTypes = /^(submit|image|button|reset)$/i,
specialTypes = /^(checkbox|radio)$/i,
checkForm = true;
if(nodes.test(el.nodeName) && !(ignoredTypes.test(el.type) || ignoredTypes.test(el.nodeName))) {
curEvt = e.type;
if(!support()) {
validity(el);
}
if(el.validity.valid && (el.value !== "" || specialTypes.test(el.type)) || (el.value !== el.getAttribute("placeholder") && el.validity.valid)) {
removeClass(el,[args.invalidClass,args.requiredClass]);
addClass(el,args.validClass);
} else if(!events.test(curEvt)) {
if(el.validity.valueMissing) {
removeClass(el,[args.invalidClass,args.validClass]);
addClass(el,args.requiredClass);
} else if(!el.validity.valid) {
removeClass(el,[args.validClass,args.requiredClass]);
addClass(el,args.invalidClass);
}
} else if(el.validity.valueMissing) {
removeClass(el,[args.requiredClass,args.invalidClass,args.validClass]);
}
if(curEvt === "input" && checkForm) {
// If input is triggered remove the keyup event
unlisten(el.form,"keyup",checkField,true);
checkForm = false;
}
}
};
checkValidity = function(el) {
var f, ff, isDisabled, isRequired, hasPattern, invalid = false;
if(el.nodeName.toLowerCase() === "form") {
f = el.elements;
for(var i = 0,len = f.length;i < len;i++) {
ff = f[i];
isDisabled = !!(ff.attributes["disabled"]);
isRequired = !!(ff.attributes["required"]);
hasPattern = !!(ff.attributes["pattern"]);
if(ff.nodeName.toLowerCase() !== "fieldset" && !isDisabled && (isRequired || hasPattern && isRequired)) {
checkField(ff);
if(!ff.validity.valid && !invalid) {
if(isSubmit) { // If it's not a submit event the field shouldn't be focused
ff.focus();
}
invalid = true;
args.onInvalid.call(el, ff);
}
}
}
return !invalid;
} else {
checkField(el);
return el.validity.valid;
}
};
setCustomValidity = function(msg) {
var el = this;
el.validationMessage = msg;
};
bypassChecks = function(e) {
// handle formnovalidate attribute
var el = getTarget(e);
if(el.attributes["formnovalidate"] && el.type === "submit") {
bypassSubmit = true;
}
};
support = function() {
return (isHostMethod(field,"validity") && isHostMethod(field,"checkValidity"));
};
// Create helper methods to emulate attributes in older browsers
pattern = function(el, type) {
if(type === "email") {
return !emailPatt.test(el.value);
} else if(type === "url") {
return !urlPatt.test(el.value);
} else if(!type) {
return false;
} else {
var placeholder = el.getAttribute("placeholder"),
val = el.value;
usrPatt = new RegExp('^(?:' + type + ')$');
if(val === placeholder) {
return false;
} else if(val === "") {
return false;
} else {
return !usrPatt.test(el.value);
}
}
};
placeholder = function(el) {
var attrs = { placeholder: el.getAttribute("placeholder") },
focus = /^(focus|focusin|submit)$/i,
node = /^(input|textarea)$/i,
ignoredType = /^password$/i,
isNative = !!("placeholder" in field);
if(!isNative && node.test(el.nodeName) && !ignoredType.test(el.type)) {
if(el.value === "" && !focus.test(curEvt)) {
el.value = attrs.placeholder;
listen(el.form,'submit', function () {
curEvt = 'submit';
placeholder(el);
}, true);
addClass(el,args.placeholderClass);
} else if(el.value === attrs.placeholder && focus.test(curEvt)) {
el.value = "";
removeClass(el,args.placeholderClass);
}
}
};
range = function(el, type) {
// Emulate min, max and step
var min = parseInt(el.getAttribute("min"),10) || 0,
max = parseInt(el.getAttribute("max"),10) || false,
step = parseInt(el.getAttribute("step"),10) || 1,
val = parseInt(el.value,10),
mismatch = (val-min)%step;
if(!valueMissing(el) && !isNaN(val)) {
if(type === "step") {
return (el.getAttribute("step")) ? (mismatch !== 0) : false;
} else if(type === "min") {
return (el.getAttribute("min")) ? (val < min) : false;
} else if(type === "max") {
return (el.getAttribute("max")) ? (val > max) : false;
}
} else if(el.getAttribute("type") === "number") {
return true;
} else {
return false;
}
};
required = function(el) {
var required = !!(el.attributes["required"]);
return (required) ? valueMissing(el) : false;
};
valueMissing = function(el) {
var placeholder = el.getAttribute("placeholder"),
specialTypes = /^(checkbox|radio)$/i,
isRequired = !!(el.attributes["required"]);
return !!(isRequired && (el.value === "" || el.value === placeholder || (specialTypes.test(el.type) && !isSiblingChecked(el))));
};
/* Util methods */
listen = function (node,type,fn,capture) {
if(isHostMethod(window,"addEventListener")) {
/* FF & Other Browsers */
node.addEventListener( type, fn, capture );
} else if(isHostMethod(window,"attachEvent") && typeof window.event !== "undefined") {
/* Internet Explorer way */
if(type === "blur") {
type = "focusout";
} else if(type === "focus") {
type = "focusin";
}
node.attachEvent( "on" + type, fn );
}
};
unlisten = function (node,type,fn,capture) {
if(isHostMethod(window,"removeEventListener")) {
/* FF & Other Browsers */
node.removeEventListener( type, fn, capture );
} else if(isHostMethod(window,"detachEvent") && typeof window.event !== "undefined") {
/* Internet Explorer way */
node.detachEvent( "on" + type, fn );
}
};
preventActions = function (evt) {
evt = evt || window.event;
if(evt.stopPropagation && evt.preventDefault) {
evt.stopPropagation();
evt.preventDefault();
} else {
evt.cancelBubble = true;
evt.returnValue = false;
}
};
getTarget = function (evt) {
evt = evt || window.event;
return evt.target || evt.srcElement;
};
addClass = function (e,c) {
var re;
if (!e.className) {
e.className = c;
}
else {
re = new RegExp('(^|\\s)' + c + '(\\s|$)');
if (!re.test(e.className)) { e.className += ' ' + c; }
}
};
removeClass = function (e,c) {
var re, m, arr = (typeof c === "object") ? c.length : 1, len = arr;
if (e.className) {
if (e.className === c) {
e.className = '';
} else {
while(arr--) {
re = new RegExp('(^|\\s)' + ((len > 1) ? c[arr] : c) + '(\\s|$)');
m = e.className.match(re);
if (m && m.length === 3) { e.className = e.className.replace(re, (m[1] && m[2])?' ':''); }
}
}
}
};
isHostMethod = function(o, m) {
var t = typeof o[m], reFeaturedMethod = new RegExp('^function|object$', 'i');
return !!((reFeaturedMethod.test(t) && o[m]) || t === 'unknown');
};
/* Checking if one of the radio siblings is checked */
isSiblingChecked = function(el) {
var siblings = document.getElementsByName(el.name);
for(var i=0; i<siblings.length; i++){
if(siblings[i].checked){
return true;
}
}
return false;
};
// Since all methods are only used internally no need to expose globally
return {
setup: setup
};
}));