prometheus
Version:
Simple ODM for Node.js with pluggable adapters
361 lines (279 loc) • 11.9 kB
JavaScript
/**
Creates object from a model's data that can be used in making HTML forms
@class FormBuilder
@constructor
@param {Object} schema Model's schema
@param {Object} data Model's attributes
@param {Object} options Additional options, e.g. method POST, PUT etc, action URL etc
@return {Object} Returns object with form tag and form inputs
*/
var _ = require('underscore'),
datatypes = require('../includes/datatypes'),
FormBuilder;
FormBuilder = function(schema, data, options) {
var form = {
tag: {
name : options.name || '',
id : options.name || '',
method : options.method || 'POST',
action : options.url,
enctype : 'application/x-www-form-urlencoded'
},
fields: []
},
escape_val = function(val) {
return (_.isString(val)) ? val.replace(/"/g, '"').replace(/'/g, ''') : val;
},
make_props = function(props) {
return _.reduce(props, function(memo, prop) {
_.each(prop, function(val, attr) {
memo += ' ' + attr + '="' + val + '"';
});
return memo;
}, '');
}
show_hidden = options.show_hidden || false;
make = {
/**
Creates <input ="text/file/email/tel/url/number/date"> field
@method text
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
text: function(obj) {
var props = [],
type,
tag;
if (obj.content_type) {
type = 'file';
form.tag.enctype = 'multipart/form-data';
} else if (obj.type === datatypes.EMAIL) {
type = 'email';
} else if (obj.type === datatypes.PHONE) {
type = 'tel';
} else if (obj.type === datatypes.URL) {
type = 'url';
} else if (obj.type === datatypes.NUMBER || obj.type === datatypes.DECIMAL) {
type = 'number';
} else if (obj.type === datatypes.DATE) {
type = 'date';
} else if (obj.type === datatypes.DATETIME || obj.type === datatypes.TIMESTAMP) {
type = 'datetime';
} else if (obj.type === datatypes.TIME) {
type = 'time';
} else if (obj.type === datatypes.COLOR) {
type = 'color';
} else if (obj.type === datatypes.PASSWORD) {
type = 'password';
} else {
type = 'text';
}
/* --- Required properties --- */
props.push({ type : type });
props.push({ name : obj.attr });
props.push({ 'data-for': obj.attr });
props.push({ id : obj.id });
props.push({ value : obj.val_escaped });
props.push({ class : 'form-control' });
/* --- Optional properties --- */
if (obj.maxlength) {
props.push({ maxlength: obj.maxlength });
}
if (obj.readonly) {
props.push({ readonly: obj.readonly });
}
if (obj.content_type) {
props.push({ accept: obj.content_type });
}
/* --- Build HTML tag --- */
tag = '<input' + make_props(props) + ' />';
if (type === 'file') {
tag = '<span class="inp-file" data-filename="">' + tag + '</span>';
}
return [tag];
},
/**
Creates <input ="checkbox"> field
@method checkbox
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
checkbox: function(obj) {
var tags = [];
/* --- Value is an array ---- */
_.each(obj.permitted, function(val, index) {
var val_escaped = escape_val(val),
props = [],
id = obj.id + '_' + index,
tag;
/* --- Required properties --- */
props.push({ type : 'checkbox' });
props.push({ id : id });
props.push({ name : obj.attr });
props.push({ 'data-for': obj.attr });
props.push({ value : index });
props.push({ class : 'form-control' });
/* --- Optional properties --- */
if (obj.val.indexOf(val) !== -1) {
props.push({ checked: 'checked' });
}
/* --- Build HTML tag --- */
tag = '<input' + make_props(props) + ' /><label for="' + id + '">' + val + '</label>';
tags.push(tag);
});
return tags;
},
/**
Creates <input ="radio"> field
@method radio
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
radio: function(obj) {
var tags = [];
_.each(obj.permitted, function(val, index) {
var val_escaped = escape_val(val),
props = [],
id = obj.id + '_' + index,
tag;
/* --- Required properties --- */
props.push({ type : 'radio' });
props.push({ id : id });
props.push({ name : obj.attr });
props.push({ 'data-for': obj.attr });
props.push({ value : index });
/* --- Optional properties --- */
if (obj.val == val || (_.isArray(obj.val) && obj.val.indexOf(val) !== -1)) {
props.push({ checked: 'checked' });
}
/* --- Build HTML tag --- */
tag = '<input' + make_props(props) + ' /><label for="' + id + '">' + val + '</label>';
tags.push(tag);
});
return tags;
},
/**
Creates <textarea> field
@method checkbox
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
textarea: function(obj) {
var val = String(obj.val).replace(/"/g, '"').replace(/'/g, '''),
props = [],
tag;
/* --- Required properties --- */
props.push({ name : obj.attr });
props.push({ 'data-for': obj.attr });
props.push({ id : obj.id });
props.push({ class : 'form-control' });
/* --- Optional properties --- */
if (obj.readonly) {
props.push({ readonly: obj.readonly });
}
/* --- Build HTML tag --- */
tag = '<textarea' + make_props(props) + '>' + obj.val_escaped + '</textarea>';
return [tag];
},
/**
Creates <select> field
@method select
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
select: function(obj) {
var multiple = _.isArray(obj.val) ? ' multiple="multiple"' : '',
props = [],
options = _.reduce(obj.permitted, function(memo, val, index) {
var checked;
if (multiple) {
checked = (obj.val.indexOf(val) !== -1) ? true : false;
} else {
checked = (obj.val == val) ? true : false;
}
if (checked) {
memo += '<option value="' + index + '" checked="checked">' + val + '</option>';
} else {
memo += '<option value="' + index + '">' + val + '</option>';
}
return memo;
}, ''),
tag;
/* --- Required properties --- */
props.push({ name : obj.attr });
props.push({ 'data-for' : obj.attr });
props.push({ id : obj.id });
props.push({ class : 'form-control' });
/* --- Optional properties --- */
if (multiple) {
props.push({ multiple: 'multiple' });
}
/* --- Build HTML tag --- */
tag = '<select' + make_props(props) + '>' + options + '</select>';
return [tag];
},
/**
Creates a custom field
@method custom
@param {Object} obj Object containing data for the field
@return {Array} Returns array with input tags
*/
custom: function(obj) {
return {
type: 'other',
inputs: [obj.maketag(obj.val)]
}
}
};
/* --- Iterate through provided fields --- */
_.each(schema, function(_obj, attr) {
var obj = _.extend({}, _obj);
/* --- Do not generate hidden tags --- */
if (obj.hidden && !show_hidden) {
return;
}
var field = {
label: obj.name,
type: null,
inputs: []
},
multiple = _.isArray(obj.val),
tags;
obj.id = form.tag.name + '_' + attr;
obj.attr = attr;
obj.val = data[attr] || obj.default;
/* --- Convert values to strings and escape --- */
switch (obj.type) {
case datatypes.JSON:
case datatypes.ARRAY:
obj.val_escaped = JSON.stringify(obj.val);
break;
default:
obj.val_escaped = (obj.val === null) ? '' : escape_val(String(obj.val));
}
/* --- Use appropriate tag generator --- */
if (obj.maketag && _.isFunction(obj.maketag)) {
var custom = make.custom(obj);
field.type = custom.type;
field.inputs = custom.inputs;
} else if (obj.permitted && obj.permitted.length <= 3 && !multiple) {
field.type = 'radio';
field.inputs = make.radio(obj);
} else if (obj.permitted && obj.permitted.length <= 3 && multiple) {
field.type = 'checkbox';
field.inputs = make.checkbox(obj);
} else if (obj.permitted && obj.permitted.length > 3) {
field.type = 'select';
field.inputs = make.select(obj);
} else if (obj.type === datatypes.TEXT || (obj.maxlength && obj.maxlength > 255)) {
field.type = 'textarea';
field.inputs = make.textarea(obj);
} else {
field.type = 'text';
field.inputs = make.text(obj);
}
form.fields.push(field);
});
return form;
}
module.exports = FormBuilder;