import EventEmitter from 'eventemitter3' const any = function(obj, pred) { for (let name in obj) { if (pred(obj[name])) { return true } } return false } export class Validator extends EventEmitter { constructor(originalObj, validations, onModifiedChanged) { super() this.updateValue = this.updateValue.bind(this) this._id = originalObj._id this.onModifiedChanged = onModifiedChanged this.fields = {} this.nonValueFields = {} for (let name in validations) { let validation = validations[name] let field = { isDisabled: this.normalize(validation.isDisabled, false), isReadOnly: this.normalize(validation.isReadOnly, false), isVisible: this.normalize(validation.isVisible, true), nonValue: validation.nonValue || false } if (field.nonValue) { field.disabled = field.isDisabled(this) field.readOnly = field.isReadOnly(this) field.visible = field.isVisible(this) this.nonValueFields[name] = field } else { field.alwaysGet = validation.alwaysGet field.isValid = this.normalize(validation.isValid, true) field.initValue = (validation.initValue === undefined ? '' : validation.initValue) field.originalValue = Validator.getObjectValue(originalObj, name) this.updateFieldValue(field, field.originalValue || field.initValue) this.fields[name] = field } } this.updateNonValueFields() } normalize(obj, def) { return obj ? ((obj.constructor === Function) ? obj : () => (!!obj)) : () => (def) } static stateSafeField(field) { return { value: field.value, modified: field.modified, valid: field.valid, disabled: field.disabled, readOnly: field.readOnly, visible: field.visible } } updateValue(name, newValue) { let lastAnyModified = this.anyModified let field = this.fields[name] if (field) { this.updateFieldValue(field, newValue) this.updateNonValueFields() if (lastAnyModified !== this.anyModified && this.onModifiedChanged) { this.onModifiedChanged(this.anyModified) } } return Validator.stateSafeField(field) } updateFieldValue(field, newValue) { field.value = newValue field.disabled = field.isDisabled(this) field.readOnly = field.isReadOnly(this) field.visible = field.isVisible(this) field.valid = field.isValid(this, newValue) field.modified = field.originalValue !== undefined ? (field.originalValue !== newValue) : (newValue !== field.initValue) this.anyModified = field.modified || any(this.fields, (field) => (field.modified)) this.allValid = !field.valid ? false : !any(this.fields, (field) => (!field.valid)) } updateNonValueFields() { for (let name in this.nonValueFields) { let field = this.nonValueFields[name] let disabled = field.isDisabled(this) let readOnly = field.isReadOnly(this) let visible = field.isVisible(this) let b = (disabled !== field.disabled || readOnly !== field.readOnly || visible !== field.visible) field.disabled = disabled field.readOnly = readOnly field.visible = visible if (b) { this.emit(name, name) } } } getField(name) { let field = this.fields[name] || this.nonValueFields[name] if (!field) { throw new Error(`Field '${name}' does not have a validation entry`) } return Validator.stateSafeField(field) } getValues() { // Generate an object that has the modified and alwaysGet fields let obj = {} if (!this.anyModified && !this.allValid) { return obj } // Will have an _id if updating if (this._id) { obj._id = this._id } for (let name in this.fields) { let field = this.fields[name] if (field.alwaysGet || (!field.nonValue && field.modified)) { let value = field.value if (value && value.constructor === 'String') { value = value.trim() } Validator.setObjectValue(obj, name, value) } } return obj } getOriginalValues() { // Generate an object that has the original values of all fields let obj = {} if (this._id) { obj._id = this._id } for (let name in this.fields) { let field = this.fields[name] if (field.originalValue !== undefined) { Validator.setObjectValue(obj, name, field.originalValue) } } return obj } static getObjectValue(obj, name) { name.split('.').forEach((namePart) => { if (obj) { obj = obj[namePart] } }) return obj } static setObjectValue(obj, name, value) { name.split('.').forEach((namePart, i, nameParts) => { if (i < nameParts.length - 1) { if (!obj[namePart]) { obj[namePart] = {} } obj = obj[namePart] } else { obj[namePart] = value } }) } }