H5F.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*! H5F
  2. * https://github.com/ryanseddon/H5F/
  3. * Copyright (c) Ryan Seddon | Licensed MIT */
  4. (function (root, factory) {
  5. if (typeof define === 'function' && define.amd) {
  6. // AMD. Register as an anonymous module.
  7. define(factory);
  8. } else if (typeof module === 'object' && module.exports) {
  9. // CommonJS
  10. module.exports = factory();
  11. } else {
  12. // Browser globals
  13. root.H5F = factory();
  14. }
  15. }(this, function () {
  16. var d = document,
  17. field = d.createElement("input"),
  18. emailPatt = /^[a-zA-Z0-9.!#$%&'*+-\/=?\^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
  19. urlPatt = /[a-z][\-\.+a-z]*:\/\//i,
  20. nodes = /^(input|select|textarea)$/i,
  21. isSubmit, bypassSubmit, usrPatt, curEvt, args,
  22. // Methods
  23. setup, validation, validity, checkField, bypassChecks, checkValidity, setCustomValidity, support, pattern, placeholder, range, required, valueMissing, listen, unlisten, preventActions, getTarget, addClass, removeClass, isHostMethod, isSiblingChecked;
  24. setup = function(form, settings) {
  25. var isCollection = !form.nodeType || false;
  26. var opts = {
  27. validClass : "valid",
  28. invalidClass : "error",
  29. requiredClass : "required",
  30. placeholderClass : "placeholder",
  31. onSubmit : Function.prototype,
  32. onInvalid : Function.prototype
  33. };
  34. if(typeof settings === "object") {
  35. for (var i in opts) {
  36. if(typeof settings[i] === "undefined") { settings[i] = opts[i]; }
  37. }
  38. }
  39. args = settings || opts;
  40. if(isCollection) {
  41. for(var k=0,len=form.length;k<len;k++) {
  42. validation(form[k]);
  43. }
  44. } else {
  45. validation(form);
  46. }
  47. };
  48. validation = function(form) {
  49. var f = form.elements,
  50. flen = f.length,
  51. isRequired,
  52. noValidate = !!(form.attributes["novalidate"]);
  53. listen(form,"invalid",checkField,true);
  54. listen(form,"blur",checkField,true);
  55. listen(form,"input",checkField,true);
  56. listen(form,"keyup",checkField,true);
  57. listen(form,"focus",checkField,true);
  58. listen(form,"change",checkField,true);
  59. listen(form,"click",bypassChecks,true);
  60. listen(form,"submit",function(e){
  61. isSubmit = true;
  62. if(!bypassSubmit && !noValidate && !form.checkValidity()) {
  63. preventActions(e);
  64. return;
  65. }
  66. args.onSubmit.call(form, e);
  67. },false);
  68. if(!support()) {
  69. form.checkValidity = function() { return checkValidity(form); };
  70. while(flen--) {
  71. isRequired = !!(f[flen].attributes["required"]);
  72. // Firefox includes fieldsets inside elements nodelist so we filter it out.
  73. if(f[flen].nodeName.toLowerCase() !== "fieldset") {
  74. validity(f[flen]); // Add validity object to field
  75. }
  76. }
  77. }
  78. };
  79. validity = function(el) {
  80. var elem = el,
  81. missing = valueMissing(elem),
  82. attrs = {
  83. type: elem.getAttribute("type"),
  84. pattern: elem.getAttribute("pattern"),
  85. placeholder: elem.getAttribute("placeholder")
  86. },
  87. isType = /^(email|url)$/i,
  88. evt = /^(input|keyup)$/i,
  89. fType = ((isType.test(attrs.type)) ? attrs.type : ((attrs.pattern) ? attrs.pattern : false)),
  90. patt = pattern(elem,fType),
  91. step = range(elem,"step"),
  92. min = range(elem,"min"),
  93. max = range(elem,"max"),
  94. customError = !( elem.validationMessage === "" || elem.validationMessage === undefined );
  95. elem.checkValidity = function() { return checkValidity.call(this,elem); };
  96. elem.setCustomValidity = function(msg) { setCustomValidity.call(elem,msg); };
  97. elem.validity = {
  98. valueMissing: missing,
  99. patternMismatch: patt,
  100. rangeUnderflow: min,
  101. rangeOverflow: max,
  102. stepMismatch: step,
  103. customError: customError,
  104. valid: (!missing && !patt && !step && !min && !max && !customError)
  105. };
  106. if(attrs.placeholder && !evt.test(curEvt)) { placeholder(elem); }
  107. };
  108. checkField = function(e) {
  109. var el = getTarget(e) || e, // checkValidity method passes element not event
  110. events = /^(input|keyup|focusin|focus|change)$/i,
  111. ignoredTypes = /^(submit|image|button|reset)$/i,
  112. specialTypes = /^(checkbox|radio)$/i,
  113. checkForm = true;
  114. if(nodes.test(el.nodeName) && !(ignoredTypes.test(el.type) || ignoredTypes.test(el.nodeName))) {
  115. curEvt = e.type;
  116. if(!support()) {
  117. validity(el);
  118. }
  119. if(el.validity.valid && (el.value !== "" || specialTypes.test(el.type)) || (el.value !== el.getAttribute("placeholder") && el.validity.valid)) {
  120. removeClass(el,[args.invalidClass,args.requiredClass]);
  121. addClass(el,args.validClass);
  122. } else if(!events.test(curEvt)) {
  123. if(el.validity.valueMissing) {
  124. removeClass(el,[args.invalidClass,args.validClass]);
  125. addClass(el,args.requiredClass);
  126. } else if(!el.validity.valid) {
  127. removeClass(el,[args.validClass,args.requiredClass]);
  128. addClass(el,args.invalidClass);
  129. }
  130. } else if(el.validity.valueMissing) {
  131. removeClass(el,[args.requiredClass,args.invalidClass,args.validClass]);
  132. }
  133. if(curEvt === "input" && checkForm) {
  134. // If input is triggered remove the keyup event
  135. unlisten(el.form,"keyup",checkField,true);
  136. checkForm = false;
  137. }
  138. }
  139. };
  140. checkValidity = function(el) {
  141. var f, ff, isDisabled, isRequired, hasPattern, invalid = false;
  142. if(el.nodeName.toLowerCase() === "form") {
  143. f = el.elements;
  144. for(var i = 0,len = f.length;i < len;i++) {
  145. ff = f[i];
  146. isDisabled = !!(ff.attributes["disabled"]);
  147. isRequired = !!(ff.attributes["required"]);
  148. hasPattern = !!(ff.attributes["pattern"]);
  149. if(ff.nodeName.toLowerCase() !== "fieldset" && !isDisabled && (isRequired || hasPattern && isRequired)) {
  150. checkField(ff);
  151. if(!ff.validity.valid && !invalid) {
  152. if(isSubmit) { // If it's not a submit event the field shouldn't be focused
  153. ff.focus();
  154. }
  155. invalid = true;
  156. args.onInvalid.call(el, ff);
  157. }
  158. }
  159. }
  160. return !invalid;
  161. } else {
  162. checkField(el);
  163. return el.validity.valid;
  164. }
  165. };
  166. setCustomValidity = function(msg) {
  167. var el = this;
  168. el.validationMessage = msg;
  169. };
  170. bypassChecks = function(e) {
  171. // handle formnovalidate attribute
  172. var el = getTarget(e);
  173. if(el.attributes["formnovalidate"] && el.type === "submit") {
  174. bypassSubmit = true;
  175. }
  176. };
  177. support = function() {
  178. return (isHostMethod(field,"validity") && isHostMethod(field,"checkValidity"));
  179. };
  180. // Create helper methods to emulate attributes in older browsers
  181. pattern = function(el, type) {
  182. if(type === "email") {
  183. return !emailPatt.test(el.value);
  184. } else if(type === "url") {
  185. return !urlPatt.test(el.value);
  186. } else if(!type) {
  187. return false;
  188. } else {
  189. var placeholder = el.getAttribute("placeholder"),
  190. val = el.value;
  191. usrPatt = new RegExp('^(?:' + type + ')$');
  192. if(val === placeholder) {
  193. return false;
  194. } else if(val === "") {
  195. return false;
  196. } else {
  197. return !usrPatt.test(el.value);
  198. }
  199. }
  200. };
  201. placeholder = function(el) {
  202. var attrs = { placeholder: el.getAttribute("placeholder") },
  203. focus = /^(focus|focusin|submit)$/i,
  204. node = /^(input|textarea)$/i,
  205. ignoredType = /^password$/i,
  206. isNative = !!("placeholder" in field);
  207. if(!isNative && node.test(el.nodeName) && !ignoredType.test(el.type)) {
  208. if(el.value === "" && !focus.test(curEvt)) {
  209. el.value = attrs.placeholder;
  210. listen(el.form,'submit', function () {
  211. curEvt = 'submit';
  212. placeholder(el);
  213. }, true);
  214. addClass(el,args.placeholderClass);
  215. } else if(el.value === attrs.placeholder && focus.test(curEvt)) {
  216. el.value = "";
  217. removeClass(el,args.placeholderClass);
  218. }
  219. }
  220. };
  221. range = function(el, type) {
  222. // Emulate min, max and step
  223. var min = parseInt(el.getAttribute("min"),10) || 0,
  224. max = parseInt(el.getAttribute("max"),10) || false,
  225. step = parseInt(el.getAttribute("step"),10) || 1,
  226. val = parseInt(el.value,10),
  227. mismatch = (val-min)%step;
  228. if(!valueMissing(el) && !isNaN(val)) {
  229. if(type === "step") {
  230. return (el.getAttribute("step")) ? (mismatch !== 0) : false;
  231. } else if(type === "min") {
  232. return (el.getAttribute("min")) ? (val < min) : false;
  233. } else if(type === "max") {
  234. return (el.getAttribute("max")) ? (val > max) : false;
  235. }
  236. } else if(el.getAttribute("type") === "number") {
  237. return true;
  238. } else {
  239. return false;
  240. }
  241. };
  242. required = function(el) {
  243. var required = !!(el.attributes["required"]);
  244. return (required) ? valueMissing(el) : false;
  245. };
  246. valueMissing = function(el) {
  247. var placeholder = el.getAttribute("placeholder"),
  248. specialTypes = /^(checkbox|radio)$/i,
  249. isRequired = !!(el.attributes["required"]);
  250. return !!(isRequired && (el.value === "" || el.value === placeholder || (specialTypes.test(el.type) && !isSiblingChecked(el))));
  251. };
  252. /* Util methods */
  253. listen = function (node,type,fn,capture) {
  254. if(isHostMethod(window,"addEventListener")) {
  255. /* FF & Other Browsers */
  256. node.addEventListener( type, fn, capture );
  257. } else if(isHostMethod(window,"attachEvent") && typeof window.event !== "undefined") {
  258. /* Internet Explorer way */
  259. if(type === "blur") {
  260. type = "focusout";
  261. } else if(type === "focus") {
  262. type = "focusin";
  263. }
  264. node.attachEvent( "on" + type, fn );
  265. }
  266. };
  267. unlisten = function (node,type,fn,capture) {
  268. if(isHostMethod(window,"removeEventListener")) {
  269. /* FF & Other Browsers */
  270. node.removeEventListener( type, fn, capture );
  271. } else if(isHostMethod(window,"detachEvent") && typeof window.event !== "undefined") {
  272. /* Internet Explorer way */
  273. node.detachEvent( "on" + type, fn );
  274. }
  275. };
  276. preventActions = function (evt) {
  277. evt = evt || window.event;
  278. if(evt.stopPropagation && evt.preventDefault) {
  279. evt.stopPropagation();
  280. evt.preventDefault();
  281. } else {
  282. evt.cancelBubble = true;
  283. evt.returnValue = false;
  284. }
  285. };
  286. getTarget = function (evt) {
  287. evt = evt || window.event;
  288. return evt.target || evt.srcElement;
  289. };
  290. addClass = function (e,c) {
  291. var re;
  292. if (!e.className) {
  293. e.className = c;
  294. }
  295. else {
  296. re = new RegExp('(^|\\s)' + c + '(\\s|$)');
  297. if (!re.test(e.className)) { e.className += ' ' + c; }
  298. }
  299. };
  300. removeClass = function (e,c) {
  301. var re, m, arr = (typeof c === "object") ? c.length : 1, len = arr;
  302. if (e.className) {
  303. if (e.className === c) {
  304. e.className = '';
  305. } else {
  306. while(arr--) {
  307. re = new RegExp('(^|\\s)' + ((len > 1) ? c[arr] : c) + '(\\s|$)');
  308. m = e.className.match(re);
  309. if (m && m.length === 3) { e.className = e.className.replace(re, (m[1] && m[2])?' ':''); }
  310. }
  311. }
  312. }
  313. };
  314. isHostMethod = function(o, m) {
  315. var t = typeof o[m], reFeaturedMethod = new RegExp('^function|object$', 'i');
  316. return !!((reFeaturedMethod.test(t) && o[m]) || t === 'unknown');
  317. };
  318. /* Checking if one of the radio siblings is checked */
  319. isSiblingChecked = function(el) {
  320. var siblings = document.getElementsByName(el.name);
  321. for(var i=0; i<siblings.length; i++){
  322. if(siblings[i].checked){
  323. return true;
  324. }
  325. }
  326. return false;
  327. };
  328. // Since all methods are only used internally no need to expose globally
  329. return {
  330. setup: setup
  331. };
  332. }));