vendor: 17,665 bytes, lines 1-435
1//Pristine https://github.com/sha256/Pristine 2(function (global, factory) { 3 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 4 typeof define === 'function' && define.amd ? define(factory) : 5 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Pristine = factory()); 6}(this, (function () { 7 'use strict'; 8 9 var lang = { 10 en: { 11 required: "Error: This field is required", 12 email: "Error: This field requires a valid e-mail address. Example: [email protected]", 13 number: "Error: This field requires a number", 14 integer: "Error: This field requires an integer value", 15 url: "Error: This field requires a valid website URL", 16 tel: "Error: This field requires a valid telephone number", 17 maxlength: "Error: This fields length must be < ${1}", 18 minlength: "Error: This fields length must be > ${1}", 19 min: "Error: Minimum value for this field is ${1}", 20 max: "Error: Maximum value for this field is ${1}", 21 pattern: "Error: Please match the requested format", 22 equals: "Error: The two fields do not match" 23 } 24 }; 25 26 function findAncestor(el, cls) { 27 while ((el = el.parentElement) && !el.classList.contains(cls)) { } 28 return el; 29 } 30 31 function tmpl(o) { 32 var _arguments = arguments; 33 34 return this.replace(/\${([^{}]*)}/g, function (a, b) { 35 return _arguments[b]; 36 }); 37 } 38 39 function groupedElemCount(input) { 40 return input.pristine.self.form.querySelectorAll('input[name="' + input.getAttribute('name') + '"]:checked').length; 41 } 42 43 function mergeConfig(obj1, obj2) { 44 for (var attr in obj2) { 45 if (!(attr in obj1)) { 46 obj1[attr] = obj2[attr]; 47 } 48 } 49 return obj1; 50 } 51 52 var defaultConfig = { 53 classTo: 'form-group', 54 errorClass: 'has-danger', 55 successClass: 'has-success', 56 errorTextParent: 'form-group', 57 errorTextTag: 'div', 58 errorTextClass: 'text-help' 59 }; 60 61 var PRISTINE_ERROR = 'pristine-error'; 62 var SELECTOR = "input:not([type^=hidden]):not([type^=submit]), select, textarea"; 63 var ALLOWED_ATTRIBUTES = ["required", "min", "max", 'minlength', 'maxlength', 'pattern']; 64 var EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 65 66 var MESSAGE_REGEX = /-message(?:-([a-z]{2}(?:_[A-Z]{2})?))?/; // matches, -message, -message-en, -message-en_US 67 var currentLocale = 'en'; 68 var validators = {}; 69 70 var _ = function _(name, validator) { 71 validator.name = name; 72 if (validator.priority === undefined) validator.priority = 1; 73 validators[name] = validator; 74 }; 75 76 _('text', { 77 fn: function fn(val) { 78 return true; 79 }, priority: 0 80 }); 81 _('required', { 82 fn: function fn(val) { 83 return this.type === 'radio' || this.type === 'checkbox' ? groupedElemCount(this) : val !== undefined && val !== ''; 84 }, priority: 99, halt: true 85 }); 86 _('email', { 87 fn: function fn(val) { 88 return !val || EMAIL_REGEX.test(val); 89 } 90 }); 91 _('number', { 92 fn: function fn(val) { 93 return !val || !isNaN(parseFloat(val)) || isNaN(parseFloat(val)); 94 }, priority: 2 95 }); 96 _('integer', { 97 fn: function fn(val) { 98 return !val || /^\d+$/.test(val); 99 } 100 }); 101 _('minlength', { 102 fn: function fn(val, length) { 103 return !val || val.length >= parseInt(length); 104 } 105 }); 106 _('maxlength', { 107 fn: function fn(val, length) { 108 return !val || val.length <= parseInt(length); 109 } 110 }); 111 _('min', { 112 fn: function fn(val, limit) { 113 return !val || (this.type === 'checkbox' ? groupedElemCount(this) >= parseInt(limit) : parseFloat(val) >= parseFloat(limit)); 114 } 115 }); 116 _('max', { 117 fn: function fn(val, limit) { 118 return !val || (this.type === 'checkbox' ? groupedElemCount(this) <= parseInt(limit) : parseFloat(val) <= parseFloat(limit)); 119 } 120 }); 121 _('pattern', { 122 fn: function fn(val, pattern) { 123 var m = pattern.match(new RegExp('^/(.*?)/([gimy]*)$')); return !val || new RegExp(m[1], m[2]).test(val); 124 } 125 }); 126 _('equals', { 127 fn: function fn(val, otherFieldSelector) { 128 var other = document.querySelector(otherFieldSelector); return other && (!val && !other.value || other.value === val); 129 } 130 }); 131 132 function Pristine(form, config, live) { 133 134 var self = this; 135 136 init(form, config, live); 137 138 function init(form, config, live) { 139 140 form.setAttribute("novalidate", "true"); 141 142 self.form = form; 143 self.config = mergeConfig(config || {}, defaultConfig); 144 self.live = !(live === false); 145 self.fields = Array.from(form.querySelectorAll(SELECTOR)).map(function (input) { 146 147 var fns = []; 148 var params = {}; 149 var messages = {}; 150 151 [].forEach.call(input.attributes, function (attr) { 152 if (/^data-pristine-/.test(attr.name)) { 153 var name = attr.name.substr(14); 154 var messageMatch = name.match(MESSAGE_REGEX); 155 if (messageMatch !== null) { 156 var locale = messageMatch[1] === undefined ? 'en' : messageMatch[1]; 157 if (!messages.hasOwnProperty(locale)) messages[locale] = {}; 158 messages[locale][name.slice(0, name.length - messageMatch[0].length)] = attr.value; 159 return; 160 } 161 if (name === 'type') name = attr.value; 162 _addValidatorToField(fns, params, name, attr.value); 163 } else if (~ALLOWED_ATTRIBUTES.indexOf(attr.name)) { 164 _addValidatorToField(fns, params, attr.name, attr.value); 165 } else if (attr.name === 'type') { 166 _addValidatorToField(fns, params, attr.value); 167 } 168 }); 169 170 fns.sort(function (a, b) { 171 return b.priority - a.priority; 172 }); 173 174 self.live && input.addEventListener(!~['radio', 'checkbox'].indexOf(input.getAttribute('type')) ? 'input' : 'change', function (e) { 175 self.validate(e.target); 176 }.bind(self)); 177 178 return input.pristine = { input: input, validators: fns, params: params, messages: messages, self: self }; 179 }.bind(self)); 180 } 181 182 function _addValidatorToField(fns, params, name, value) { 183 var validator = validators[name]; 184 if (validator) { 185 fns.push(validator); 186 if (value) { 187 var valueParams = name === "pattern" ? [value] : value.split(','); 188 valueParams.unshift(null); // placeholder for input's value 189 params[name] = valueParams; 190 } 191 } 192 } 193 194 /*** 195 * Checks whether the form/input elements are valid 196 * @param input => input element(s) or a jquery selector, null for full form validation 197 * @param silent => do not show error messages, just return true/false 198 * @returns {boolean} return true when valid false otherwise 199 */ 200 self.validate = function (input, silent) { 201 silent = input && silent === true || input === true; 202 var fields = self.fields; 203 if (input !== true && input !== false) { 204 if (input instanceof HTMLElement) { 205 fields = [input.pristine]; 206 } else if (input instanceof NodeList || input instanceof (window.$ || Array) || input instanceof Array) { 207 fields = Array.from(input).map(function (el) { 208 return el.pristine; 209 }); 210 } 211 } 212 213 var valid = true; 214 215 for (var i = 0; fields[i]; i++) { 216 var field = fields[i]; 217 if (_validateField(field)) { 218 !silent && _showSuccess(field); 219 } else { 220 valid = false; 221 !silent && _showError(field); 222 } 223 } 224 return valid; 225 }; 226 227 /*** 228 * Get errors of a specific field or the whole form 229 * @param input 230 * @returns {Array|*} 231 */ 232 self.getErrors = function (input) { 233 if (!input) { 234 var erroneousFields = []; 235 for (var i = 0; i < self.fields.length; i++) { 236 var field = self.fields[i]; 237 if (field.errors.length) { 238 erroneousFields.push({ input: field.input, errors: field.errors }); 239 } 240 } 241 return erroneousFields; 242 } 243 if (input.tagName && input.tagName.toLowerCase() === "select") { 244 return input.pristine.errors; 245 } 246 return input.length ? input[0].pristine.errors : input.pristine.errors; 247 }; 248 249 /*** 250 * Validates a single field, all validator functions are called and error messages are generated 251 * when a validator fails 252 * @param field 253 * @returns {boolean} 254 * @private 255 */ 256 function _validateField(field) { 257 var errors = []; 258 var valid = true; 259 for (var i = 0; field.validators[i]; i++) { 260 var validator = field.validators[i]; 261 var params = field.params[validator.name] ? field.params[validator.name] : []; 262 params[0] = field.input.value; 263 if (!validator.fn.apply(field.input, params)) { 264 valid = false; 265 266 if (typeof validator.msg === "function") { 267 errors.push(validator.msg(field.input.value, params)); 268 } else if (typeof validator.msg === "string") { 269 errors.push(tmpl.apply(validator.msg, params)); 270 } else if (validator.msg === Object(validator.msg) && validator.msg[currentLocale]) { 271 // typeof generates unnecessary babel code 272 errors.push(tmpl.apply(validator.msg[currentLocale], params)); 273 } else if (field.messages[currentLocale] && field.messages[currentLocale][validator.name]) { 274 errors.push(tmpl.apply(field.messages[currentLocale][validator.name], params)); 275 } else if (lang[currentLocale] && lang[currentLocale][validator.name]) { 276 errors.push(tmpl.apply(lang[currentLocale][validator.name], params)); 277 } 278 279 if (validator.halt === true) { 280 break; 281 } 282 } 283 } 284 field.errors = errors; 285 return valid; 286 } 287 288 /*** 289 * Add a validator to a specific dom element in a form 290 * @param elem => The dom element where the validator is applied to 291 * @param fn => validator function 292 * @param msg => message to show when validation fails. Supports templating. ${0} for the input's value, ${1} and 293 * so on are for the attribute values 294 * @param priority => priority of the validator function, higher valued function gets called first. 295 * @param halt => whether validation should stop for this field after current validation function 296 */ 297 self.addValidator = function (elem, fn, msg, priority, halt) { 298 if (elem instanceof HTMLElement) { 299 elem.pristine.validators.push({ fn: fn, msg: msg, priority: priority, halt: halt }); 300 elem.pristine.validators.sort(function (a, b) { 301 return b.priority - a.priority; 302 }); 303 } else { 304 console.warn("The parameter elem must be a dom element"); 305 } 306 }; 307 308 /*** 309 * An utility function that returns a 2-element array, first one is the element where error/success class is 310 * applied. 2nd one is the element where error message is displayed. 2nd element is created if doesn't exist and cached. 311 * @param field 312 * @returns {*} 313 * @private 314 */ 315 function _getErrorElements(field) { 316 if (field.errorElements) { 317 return field.errorElements; 318 } 319 var errorClassElement = findAncestor(field.input, self.config.classTo); 320 var errorTextParent = null, 321 errorTextElement = null; 322 if (self.config.classTo === self.config.errorTextParent) { 323 errorTextParent = errorClassElement; 324 } else { 325 errorTextParent = errorClassElement.querySelector('.' + self.config.errorTextParent); 326 } 327 if (errorTextParent) { 328 errorTextElement = errorTextParent.querySelector('.' + PRISTINE_ERROR); 329 if (!errorTextElement) { 330 errorTextElement = document.createElement(self.config.errorTextTag); 331 errorTextElement.className = PRISTINE_ERROR + ' ' + self.config.errorTextClass; 332 errorTextParent.appendChild(errorTextElement); 333 errorTextElement.pristineDisplay = errorTextElement.style.display; 334 } 335 } 336 return field.errorElements = [errorClassElement, errorTextElement]; 337 } 338 339 function _showError(field) { 340 var errorElements = _getErrorElements(field); 341 var errorClassElement = errorElements[0], 342 errorTextElement = errorElements[1]; 343 344 if (errorClassElement) { 345 errorClassElement.classList.remove(self.config.successClass); 346 errorClassElement.classList.add(self.config.errorClass); 347 } 348 if (errorTextElement) { 349 errorTextElement.innerHTML = field.errors.join('<br/>'); 350 errorTextElement.style.display = errorTextElement.pristineDisplay || ''; 351 } 352 } 353 354 /*** 355 * Adds error to a specific field 356 * @param input 357 * @param error 358 */ 359 self.addError = function (input, error) { 360 input = input.length ? input[0] : input; 361 input.pristine.errors.push(error); 362 _showError(input.pristine); 363 }; 364 365 function _removeError(field) { 366 var errorElements = _getErrorElements(field); 367 var errorClassElement = errorElements[0], 368 errorTextElement = errorElements[1]; 369 if (errorClassElement) { 370 // IE > 9 doesn't support multiple class removal 371 errorClassElement.classList.remove(self.config.errorClass); 372 errorClassElement.classList.remove(self.config.successClass); 373 } 374 if (errorTextElement) { 375 errorTextElement.innerHTML = ''; 376 errorTextElement.style.display = 'none'; 377 } 378 return errorElements; 379 } 380 381 function _showSuccess(field) { 382 var errorClassElement = _removeError(field)[0]; 383 errorClassElement && errorClassElement.classList.add(self.config.successClass); 384 } 385 386 /*** 387 * Resets the errors 388 */ 389 self.reset = function () { 390 for (var i = 0; self.fields[i]; i++) { 391 self.fields[i].errorElements = null; 392 } 393 Array.from(self.form.querySelectorAll('.' + PRISTINE_ERROR)).map(function (elem) { 394 elem.parentNode.removeChild(elem); 395 }); 396 Array.from(self.form.querySelectorAll('.' + self.config.classTo)).map(function (elem) { 397 elem.classList.remove(self.config.successClass); 398 elem.classList.remove(self.config.errorClass); 399 }); 400 }; 401 402 /*** 403 * Resets the errors and deletes all pristine fields 404 */ 405 self.destroy = function () { 406 self.reset(); 407 self.fields.forEach(function (field) { 408 delete field.input.pristine; 409 }); 410 self.fields = []; 411 }; 412 413 self.setGlobalConfig = function (config) { 414 defaultConfig = config; 415 }; 416 417 return self; 418 } 419 420 /*** 421 * 422 * @param name => Name of the global validator 423 * @param fn => validator function 424 * @param msg => message to show when validation fails. Supports templating. ${0} for the input's value, ${1} and 425 * so on are for the attribute values 426 * @param priority => priority of the validator function, higher valued function gets called first. 427 * @param halt => whether validation should stop for this field after current validation function 428 */ 429 Pristine.addValidator = function (name, fn, msg, priority, halt) { 430 _(name, { fn: fn, msg: msg, priority: priority, halt: halt }); 431 }; 432 433 Pristine.addMessages = function (locale, messages) { 434 var langObj = lang.hasOwnProperty(locale) ? lang[locale] : lang[locale] = {}; 435
436 Object.keys(messages).forEach(function (key, index) { 437 langObj[key] = messages[key]; 438 }); 439 }; 440 441 Pristine.setLocale = function (locale) { 442 currentLocale = locale; 443 }; 444 445 return Pristine; 446 447})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.