Source: form.js

"use strict";
var util = require('util');
var _ = require('underscore');
var utils = require('./utils');
var validation = require('mpm.validation');
var fields = require('./fields');

/** @module  form */

/**
 * @namespace
 */
var form = {};

/**
 * Field loading strategies
 * @namespace
 */
form.loaders = {};
/**
 * load fields by string name,ex :"email"
 * @constructor
 */
form.loaders.FieldLoader = function() {
    return;
};
/**
 * Load field
 * @param type
 * @param name
 * @param options
 * @returns {fields.Base}
 */
form.loaders.FieldLoader.prototype.load = function(name, type, options) {
    switch (type) {
        case "text":
            return new fields.Text(name, options);
        case "email":
            return new fields.Email(name, options);
        case "date":
            return new fields.Date(name, options);
        case "time":
            return new fields.Time(name, options);
        case "textarea":
            return new fields.TextArea(name, options);
        case "password":
            return new fields.Password(name, options);
        case "hidden":
            return new fields.Hidden(name, options);
        case "checkgroup":
        case "checkboxgroup":
            return new fields.CheckboxGroup(name, options);
        case "check":
        case "checkbox":
            return new fields.Check(name, options);
        case "radio":
            return new fields.Radio(name, options);
        case "radiogroup":
            return new fields.RadioGroup(name, options);
        case "select":
            return new fields.Select(name, options);
        case "reset":
            return new fields.Reset(name, options);
        case "button":
            return new fields.Button(name, options);
        case "submit":
            return new fields.Submit(name, options);
        case "repeated":
            return new fields.Repeated(name, options);
        case "boolean":
        case "bool":
            return new fields.Boolean(name, options);
        case "label":
            return new fields.Label(name,options);
    }
};

/**
 * load fields by actual type: ex: fields.Email
 *
 * @constructor
 */
form.loaders.FieldTypeLoader = function() {
    return;
};
/**
 * [load description]
 * @param  {string} name
 * @param  {fields.Base} Type
 * @param  {object} options
 * @return {void|fields.Base}
 */
form.loaders.FieldTypeLoader.prototype.load = function(name, Type, options) {
    if (_.contains(fields, Type)) {
        return new Type(name, options);
    }
};


/**
 * Main Form class to build HTML forms
 * @type {form.FormBuilder}
 * @constructor
 */
form.FormBuilder = function(name) {
    this._fields = [];
    this.fieldLoaders = [];
    this._name = name || 'form';
    this._bound = false;
};
form.FormBuilder.prototype.getName = function() {
    return this._name;
};
form.FormBuilder.prototype.getPrefix = function() {
    return this._name + "_";
};
/**
 * given a prefixed name , return name
 * @param name
 * @returns {*}
 */
form.FormBuilder.prototype.getNameWithoutPrefix = function(name) {
    return name.replace(this.getPrefix(), "");
};

/**
 *
 * @param fieldLoader
 */
form.FormBuilder.prototype.addFieldLoader = function(fieldLoader) {
    this.fieldLoaders.push(fieldLoader);
};
/**
 *
 * @param type
 * @param name
 * @param options
 * @returns {fields.Base}
 */
form.FormBuilder.prototype.resolveField = function(name, type, options) {
    var i = 0,
        field;
    while (!field && i < this.fieldLoaders.length) {
        field = this.fieldLoaders[i].load(name, type, options);
        i += 1;
    }
    if (field !== undefined) {
        return field;
    }
};
/**
 *
 * @param {string} name field name
 * @param {string} type form type
 * @param {Object} options
 * @returns {*}
 */
form.FormBuilder.prototype.add = function(name, type, options) {
    var _field;
    if (name instanceof fields.Base) {
        _field = name;
    } else {
        _field = this.resolveField(name, type, options);
    }
    if (_field !== undefined) {
        _field.getOptions().attributes.id = this.getPrefix() + _field.getName();
        this.getFields().push(_field);
    }
    return this;
};
form.FormBuilder.prototype.remove = function(name) {
    var fields, field = this.getByName(name);
    if (field) {
        fields = this.getFields();
        fields.splice(fields.indexOf(field), 1);
    }
    return this;
};
/**
 * render form
 * @param {Function} iterator
 * @returns {string}
 */
form.FormBuilder.prototype.toHTML = function(iterator) {
    var _iterator = iterator;
    if (_.isUndefined(_iterator)) {
        _iterator = function(w) {
            return w.toHTML();
        };
    }
    return this._fields.map(_iterator).join("\n");
};
/**
 *
 * @returns {Array}
 */
form.FormBuilder.prototype.toJSON = function() {
    return this._fields.map(function(w) {
        return w.toJSON();
    });
};
/**
 *
 * @param value
 */
form.FormBuilder.prototype.setModel = function(value) {
    this._model = value;
    this.setData(this.getModel());
    return this;
};
/**
 *
 * @returns {*}
 */
form.FormBuilder.prototype.getModel = function() {
    return this._model;
};
/**
 *
 * @param data
 */
form.FormBuilder.prototype.setData = function(data) {
    this._fields.forEach(function(field, i) {
        this._fields[i].setData(data[this._fields[i].name]);
    }, this);
};
/**
 *
 * @returns {{}}
 */
form.FormBuilder.prototype.getData = function() {
    var datas = {};
    this._fields.forEach(function(w) {
        datas[w.name] = w.getData();
    }, this);
    return datas;
};
/**
 * Find field by name
 * @param name
 * @returns {*}
 */
form.FormBuilder.prototype.getByName = function(name) {
    return _.find(this._fields, function(field) {
        return field.name === this.getNameWithoutPrefix(name);
    }, this);
};
form.FormBuilder.prototype.find = form.FormBuilder.prototype.getByName;
form.FormBuilder.prototype.getName = function() {
    return this._name;
};
form.FormBuilder.prototype.setName = function(name) {
    this._name = name;
};
form.FormBuilder.prototype.getFields = function() {
    return this._fields;
};
/**
 *
 * @param callback
 * @returns {*}
 */
form.FormBuilder.prototype.validate = function(callback) {
    var i = 0,
        length = this.getFields().length,
        cb, self = this,
        res = true;
    this.setErrors(undefined);
    cb = function(error, result) {
        if (error) {
            self.addError(error);
            res = false;
        }
        i += 1;
        if (i < length) {
            return self.getFields()[i].validate(cb);
        }
        return callback(self.getErrors(), res);
    };
    return this.getFields()[i].validate(cb);

};
form.FormBuilder.prototype.validateSync = function() {
    var valid = true;
    this.setErrors(undefined);
    this.getFields().forEach(function(field) {
        if (!field.validateSync()) {
            this.addError(field.getError());
        }
    }, this);
    if (this.hasError()) {
        valid = false;
    }
    return valid;
};
form.FormBuilder.prototype.addError = function(err) {
    if (!this._errors) {
        this._errors = [];
    }
    this._errors.push(err);
};
form.FormBuilder.prototype.getErrors = function() {
    return this._errors;
};
form.FormBuilder.prototype.setErrors = function(errors) {
    this._errors = errors;
};
form.FormBuilder.prototype.hasError = function() {
    return this._errors !== undefined;
};
/**
 *
 * @param body the body of a request
 */
form.FormBuilder.prototype.bind = function(body) {
    var model = this.getModel();
    var modelProto = model ? Object.getPrototypeOf(model) : null;
    this.getFields().forEach(function(field) {
        var name = field.getName();
        field.setData(body[name]);
        if (model && (model[name] || modelProto[name])) {
            model[name] = field.getData();
        }
    }, this);
    this._bound = true;
    return this;
};
form.FormBuilder.prototype.isBound = function() {
    return this._bound;
};

/**
 * @param {string} name name of the form
 * @returns {form.FormBuilder}
 */
form.create = function(name) {
    var f = new form.FormBuilder(name);
    f.addFieldLoader(new form.loaders.FieldLoader());
    f.addFieldLoader(new form.loaders.FieldTypeLoader());
    return f;
};

module.exports=form;