HTMLElement.prototype.isChildOf = function(parent){ var node = this.parentNode; while (node != null) { if (node == parent) { return true; } node = node.parentNode; } return false; } class SerializerField { /** * constructor * * @param {HTMLElement} f Input field (input|select|textarea) * @returns {SerializerField} */ constructor(f){ this.name; this.parent; this.field = f; this.required = f.required this.type = this.field.getAttribute("type"); } /** * serialize * * @returns {(String|Number|Boolean|Date)} */ serialize(){ if(this.type == "number" || this.type == "range"){ return Number(this.field.value); } else if(this.type == "date" || this.type == "datetime-local" || this.type == "month" || this.type == "week") { return new Date(this.field.value); } else if(this.type == "checkbox") { let checked = this.field.checked; if(this.field.hasAttribute("string")){ if(checked) { return this.field.value } else { return null; } } return checked; } else { return this.field.value; } } } class Serializer { /** * constructor * * @param {HTMLElement} element * @returns {Serializer} */ constructor(element){ let fields = element.querySelectorAll(`:scope input:not([type="submit"]), :scope select, :scope textarea, :scope group`); let groups = [].slice.call(element.querySelectorAll(`:scope group`)); this.name = ":root"; this.parent; this._fields = new Map(); this.min = Number(element.getAttribute("min") || -1); this.required = element.getAttribute("required") !== null || this.min != -1; for(let f of fields){ if(f.isChildOf(element) && groups.every(g => !f.isChildOf(g))){ this.addField(f); } } } /** * addField * * @param {HTMLElement} f */ addField(f){ let fieldName = f.getAttribute("name"); let isArray = fieldName.match(/^\[(\d*)\]/) fieldName = fieldName.replace(/^\[\d*\]/, ''); let field; if(f.tagName == "GROUP"){ field = new Serializer(f) } else { field = new SerializerField(f); } field.name = fieldName; field.parent = this; f.serializer = field if(isArray){ if(!this._fields.has(fieldName)){ this._fields.set(fieldName, []) } if(isArray[1] != ""){ this._fields.get(fieldName)[isArray[1]] = field } else { this._fields.get(fieldName).push(field) } } else { this._fields.set(fieldName, field) } } /** * serialize * * @returns {Object} */ serialize(){ let json = {}; for(let [k, f] of this._fields){ if(Array.isArray(f)){ if(k != ""){ json[k] = []; } else { json = []; } for(let key in f){ if(f[key]){ let d = f[key].serialize(); if(d !== null){ if(k == ""){ json[key] = d; continue; } json[k][key] = d; } } } } else { let d = f.serialize(); if(f !== null){ if(k == ""){ json = d continue; } json[k] = d } } } return json; } } class Validator { /** * validateRequired * * @static * @param {(erializer|SerializerField)} field * @returns {Object} */ static validateRequired(field){ let e = { valid: true, errors: {}, } let v; if(field instanceof Serializer){ v = Validator.validateRequiredSerializer(field); } else { v = Validator.validateRequiredSerialzerField(field) } if(!v.valid){ e.valid = false; e.errors = v.errors; } return e; } /** * validateRequiredSerializer * * @static * @param {Serializer} field * @returns {Object} */ static validateRequiredSerializer(field){ let e = { valid: true, errors: {}, } let min = field.min; let validFieldsCount = 0; for(let [k, f] of field._fields){ if(Array.isArray(f)){ e.errors = { message: null, }; let validCount = 0; for(let sf of f){ if(!(sf.name in e.errors)){ e.errors[sf.name] = []; } let v = Validator.validateRequired(sf) if(!v.valid){ console.log(e.errors) e.errors[sf.name].push(v.errors); continue; } validCount++ } e.valid = min <= validCount if(e.valid){ e.errors = {} } else { e.errors.message = `Should contain atleast ${min} value(s)` } } else { let v = Validator.validateRequired(f); if(!v.valid){ e.valid = false; e.errors[f.name] = v.errors; } } } return e; } /** * validateRequiredSerialzerField * * @static * @param {SerializerField} field * @returns {Object} */ static validateRequiredSerialzerField(field){ let e = { valid: true, errors: {}, } if(field.required || (field.parent && field.parent.required)){ if(field.type != "checkbox"){ e.valid = field.field.validity.valueMissing !== true e.errors = "Is required" } else { e.valid = field.field.checked e.errors = "Not checked" } } return e; } }