var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import formToObj from 'form-to-obj';
import validate from 'validate.js';
// import populate from 'populate.js';
import { TranslateService } from "../../lib";
export class Form {
    constructor() {
        this.formClass = 'form-horizontal col-12';
        this.resettable = false;
        this.submitLabel = 'SUBMIT';
        this.translateService = TranslateService.instance;
    }
    componentDidLoad() {
        this.postRender();
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        this.form = this.el.getElementsByTagName('form')[0];
        if (this.el.getElementsByTagName('ema-entity-form').length) {
            this.entityForm = this.el.getElementsByTagName('ema-entity-form')[0];
        }
        this.initValidations();
    }
    handleSubmit(e) {
        return __awaiter(this, void 0, void 0, function* () {
            let errors;
            if (this.constraints) {
                errors = validate(this.form, this.constraints);
                this.showErrors(this.form, errors || {});
            }
            if (errors)
                this.logErrors(errors);
            if (!this.action) {
                e.preventDefault();
            }
            if (this.action && errors) {
                e.preventDefault();
            }
            if (!errors) {
                let obj = formToObj(this.form);
                if (this.submitHandler) {
                    obj = yield this.submitHandler(obj, this.dataType);
                }
                if (obj) {
                    this.formSubmitSuccesHandler();
                }
                else {
                    console.error('form submit failure');
                }
            }
        });
    }
    formSubmitSuccesHandler() {
        this.formSubmitSucces.emit({ form: this.form, formData: formToObj(this.form) });
    }
    setDefinitions(definitions) {
        typeof definitions === 'string' ? this.definitions = JSON.parse(definitions) : this.definitions = definitions;
        let constraints = {};
        Object.keys(this.definitions).forEach((k, i) => {
            let field = this.definitions[k];
            if (field.constraints) {
                constraints[k] = field.constraints;
            }
        });
        this.setConstraints(constraints);
    }
    setConstraints(constraints) {
        typeof constraints === 'string' ? this.constraints = JSON.parse(constraints) : this.constraints = constraints;
    }
    setSubmitInterceptor(submitFn) {
        this.submitHandler = submitFn;
    }
    setEntity(entity) {
        this.entity = entity;
    }
    update() {
        this.render();
    }
    initValidations() {
        setTimeout((arg) => {
            const self = this;
            let inputs = this.el.querySelectorAll("input, textarea, select");
            for (let i = 0; i < inputs.length; ++i) {
                inputs.item(i).addEventListener("change", function (ev) {
                    let errors = validate(self.form, self.constraints) || {};
                    self.showErrorsForInput(this, errors[this['getAttribute']('name')]);
                });
            }
            // data validation: requires moment.js
            validate.extend(validate.validators.datetime, {
                // The value is guaranteed not to be null or undefined but otherwise it could be anything.
                parse: function (value, options) {
                    return +moment.utc(value);
                },
                // Input is a unix timestamp
                format: function (value, options) {
                    var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss";
                    return moment.utc(value).format(format);
                }
            });
        }, 20);
    }
    showErrors(form, errors) {
        // We loop through all the inputs and show the errors for that input
        Array.from(form.querySelectorAll("input[name], select[name]")).forEach((input) => {
            // Since the errors can be null if no errors were found we need to handle that
            this.showErrorsForInput(input, errors && errors[input['getAttribute']('name')]);
        });
    }
    showErrorsForInput(input, errors) {
        // This is the root of the input: Find where the error messages will be insert into
        let formGroup = this.closestParent(input.parentNode, "form-group");
        if (formGroup) {
            let messages = formGroup.querySelector(".messages");
            // First we remove any old messages and resets the classes
            this.resetFormGroup(formGroup);
            // If we have errors
            if (errors) {
                // we first mark the group has having errors
                formGroup.classList.add("has-error");
                // then we append all the errors
                Object.keys(errors).forEach((k, i) => {
                    this.addError(messages, errors[k]);
                });
            }
            else {
                // otherwise we simply mark it as success
                formGroup.classList.add("has-success");
            }
        }
        else {
            if (errors) {
                this.logErrors(errors, input['getAttribute']('name'));
            }
        }
    }
    logErrors(errors, fieldName = undefined) {
        Object.keys(errors).forEach((k, i) => {
            if (fieldName) {
                console.error(fieldName + ': ' + errors[k]);
            }
            else {
                console.error(k + ': ' + errors[k]);
            }
        });
    }
    closestParent(child, className) {
        // Recusively finds the closest parent that has the specified class
        if (!child || child == document) {
            return null;
        }
        if (child.classList.contains(className)) {
            return child;
        }
        else {
            return this.closestParent(child.parentNode, className);
        }
    }
    resetFormGroup(formGroup) {
        // Remove the success and error classes
        formGroup.classList.remove("has-error");
        formGroup.classList.remove("has-success");
        // and remove any old messages
        Array.from(formGroup.querySelectorAll(".help-block.error")).forEach((el) => {
            el.parentNode.removeChild(el);
        });
    }
    // Adds the specified error with the following markup: <p class="help-block error">[message]</p>
    addError(messages, error) {
        let block = document.createElement("p");
        block.classList.add("help-block");
        block.classList.add("error");
        block.innerText = this.translate(error);
        messages.appendChild(block);
    }
    translate(str) {
        return this.translateService.get(str);
    }
    render() {
        if (this.dataJson) {
            this.entity = JSON.parse(this.dataJson);
        }
        // todo: check
        if (this.definitionsJson) {
            this.setDefinitions(JSON.parse(this.definitionsJson));
        }
        if (this.language && this.language !== this.translateService.language) {
            this.translateService.use(this.language);
        }
        return (h("form", { onSubmit: (e) => this.handleSubmit(e), class: this.formClass, role: 'form', action: this.action || 'javascript:;', method: this.method || 'get' },
            this.entity &&
                h("div", null,
                    h("ema-entity-form", { "data-json": JSON.stringify(this.entity), "definitions-json": JSON.stringify(this.definitions) })),
            h("slot", null),
            h("div", { class: "form-group row" },
                h("div", { class: "col-md-8 offset-md-4" },
                    h("button", { type: "submit", class: "btn btn-primary mr-2" }, this.translate(this.submitLabel)),
                    this.resettable &&
                        h("button", { type: "reset", class: "btn btn-primary" }, this.translate('RESET'))),
                this.submitError &&
                    h("div", { class: "col-md-8 offset-md-4 messages help-block error" },
                        this.translate(this.submitError),
                        "."))));
    }
    static get is() { return "ema-form"; }
    static get properties() { return {
        "action": {
            "type": String,
            "attr": "action"
        },
        "dataJson": {
            "type": String,
            "attr": "data-json"
        },
        "dataType": {
            "type": String,
            "attr": "data-type"
        },
        "definitionsJson": {
            "type": String,
            "attr": "definitions-json"
        },
        "el": {
            "elementRef": true
        },
        "entity": {
            "state": true
        },
        "formClass": {
            "type": String,
            "attr": "form-class"
        },
        "language": {
            "type": String,
            "attr": "language"
        },
        "method": {
            "type": String,
            "attr": "method"
        },
        "resettable": {
            "type": Boolean,
            "attr": "resettable"
        },
        "setConstraints": {
            "method": true
        },
        "setDefinitions": {
            "method": true
        },
        "setEntity": {
            "method": true
        },
        "setSubmitInterceptor": {
            "method": true
        },
        "submitError": {
            "type": String,
            "attr": "submit-error"
        },
        "submitLabel": {
            "type": String,
            "attr": "submit-label"
        },
        "update": {
            "method": true
        }
    }; }
    static get events() { return [{
            "name": "formSubmitSucces",
            "method": "formSubmitSucces",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
    static get style() { return "/**style-placeholder:ema-form:**/"; }
}
//# sourceMappingURL=form.js.map