<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Form</title>
</head>
<body>	

	<div id="formContainer">
		
	</div>

	<script>
	function Form(el, data, okCallback, cancelCallback){

		let check=[];
    	let formBody = document.createElement('div')
    	let okButton = document.createElement('button')
    	okButton.innerHTML = 'OK'
    	okButton.disabled=true;

		let cancelButton = document.createElement('button')
    	cancelButton.innerHTML = 'Cancel';

    	formBody.innerHTML = '<h1>тут будет форма после перервы</h1>';
    	const table=document.createElement('table');

    	this.okCallback     = okCallback
    	this.cancelCallback = cancelCallback
    
    	let backupData = {...data};
    	this.data           = data
    	this.validators     = {}
 
		this.createData = function (object) {
    	formBody.append(table)
    	formBody.appendChild(cancelButton);
    	formBody.appendChild(okButton);

    	let inputCreators = {
   			String(key, value, oninput){
        		let input = document.createElement('input')
       			input.type = 'text'
        		input.placeholder = key
        		if (/^[*]+$/.test(value)) {
                	input.type = 'password';
                	input.value = '';
            	} else {
                	input.type = 'text'
                	input.value = value
            	}
        		input.oninput     = () => oninput(input.value)
       			return input;
    		},
    		Boolean(key, value, oninput){
       			//label for с input type='checkbox' внутри
        		let input = document.createElement('input')
       			input.type = 'checkbox'
       			input.checked=value
        		input.placeholder = key
        		input.oninput     = () => oninput(input.checked)
       			return input;
    		},
   			Date(key, value, oninput){
        		//используйте datetime-local
        		let input = document.createElement('input')
            	input.type = 'datetime-local'
            	input.placeholder = key
            	let offset = value.getTimezoneOffset()
            	const now = value.getTime()
            	const nowPlusOffset = new Date(now - offset * 60 * 1000)
            	input.value = nowPlusOffset.toISOString().slice(0, -1)
            	input.oninput = () => oninput(input.value)
            	return input
    		},
		}

    	for(let item in data){
    		
    		let tr=document.createElement('tr');
    		let th=document.createElement('th');
    		let p=document.createElement('p');
    		let errorField = document.createElement('td')
        	let errorFieldMessage = document.createElement('p')
    		p.innerText=item+":";
    		th.append(p);
    		tr.append(th);
    		let td=document.createElement('td');
    		let input=inputCreators[data[item].constructor.name](item,data[item],
    			value=>{
    				data[item]=value;
   				}
    		)
    		td.append(input);
    		tr.append(td);
    		table.append(tr);
    		errorField.append(errorFieldMessage)
    		formBody.append(table);

    		input.onchange = () => {
            	if(data[item] === '') {
                	input.style.background = '#FFDEAD';
                	check.push[false]
            	} else {
                	input.style.background = '#fff';
                	check.push[true]
            	}
            	if (this.validators[item]) {
                	let validationResult = this.validators[item](data[item], item, data, input)
                if (validationResult === true) {
                    errorFieldMessage.innerText = ''
                    input.style.background = '#fff';
                    check.push[true]
                }
                else if (typeof validationResult === 'string') {
                    errorFieldMessage.innerText = validationResult
                    input.style.background = '#f00';
                    check.push[false]
                }
            }

            if (item[0] === "*") {
            	input.required = true;
            	th.innerHTML = `<strong>${key.slice(1)} <sup style="color: red">*</sup></strong>`;
        	} else {
            	th.innerText = item;
        	}
        	if (data[item] === '') {
            	input.style.background = '#FFDEAD';
            	check.push[false]
        	} else {
            	input.style.background = '#fff';
            	check.push[true]
        	} 
    	}
    }
}

    	this.createData(this.data);  

    	if(typeof okCallback === 'function'){
        	/*formBody.appendChild(okButton);
        	okButton.onclick = (e) => {
            	console.log(this);
            	this.okCallback(e);
        	}*/
        	if (check.includes(false)){
       			okButton.disabled = true;
    		}else{
        		okButton.disabled = false;
        		okButton.onclick = (e) => {
            		okCallback(this.data);
            		console.log(this);
            		this.okCallback(e);
        		}   
    		}
    	}

    	if (typeof cancelCallback === 'function'){
        	/*formBody.appendChild(cancelButton);
        	cancelButton.onclick = cancelCallback;*/
        	cancelButton.onclick = () => {
        		while (table.hasChildNodes()) {
            	table.removeChild(table.firstChild);
        	}
        		this.createData(backupData)
        		cancelCallback();
    		}
    	}

    	el.appendChild(formBody);
	}

	let form = new Form(formContainer, {
    	name: 'Anakin',
    	surname: 'Skywalker',
    	married: true,
    	birthday: new Date((new Date).getTime() - 86400000 * 30*365)
	}, () => console.log('ok'),() => console.log('cancel') );
	
	form.okCallback = () => console.log('ok2');

	form.validators.surname = (value, key, data, input) => value.length > 2 && value[0].toUpperCase() == value[0] && !value.includes(' ') ? true : 'Wrong name';

	form.validators.name = (value, key, data, input) => value.length > 2 && value[0].toUpperCase() == value[0] && !value.includes(' ') ? true : 'Wrong name'                                  

/*if(item=='married'){
    			let check=document.createElement('input');
    			check.type='checkbox';
    			if(data[item]==true){
    				check.checked=true;
    			}else{
    				check.checked=false;
    			}
    			td.append(check);
    		}else{*/
	</script>

</body>
</html>