UNPKG

10.3 kBJavaScriptView Raw
1
2var utils = require('./utils'),
3 humanize = utils.humanize;
4
5module.exports = FormForResource;
6
7function FormForResource(res, formParams, path, hs) {
8 this.__helpers = hs;
9 this.__formParams = formParams;
10 this.__app = hs.controller.app;
11 this.__resourceName = res&& res.constructor && res.constructor.modelName || false;
12 this.__res = res;
13 this.__path = path || this.__resourceName;
14
15 this.__complexNames = (this.__app.set('view options') || {}).complexNames;
16 if (typeof this.__complexNames === 'undefined') {
17 this.__complexNames = true;
18 }
19}
20
21/**
22 * Generates an id field
23 *
24 * @requires resourceName
25 * @param {String} name Name of the element
26 * @returns {String} returns the generated id name
27 */
28FormForResource.prototype.__makeId = function makeId(name, params) {
29 if (params && params.id) {
30 return params.id;
31 }
32 return this.__complexNames && this.__path ? ((this.__path + '_' + name).replace(/[\[\]_]+/g, '_').replace(/_$/, '')) : name;
33};
34
35/**
36 * Generates a name
37 *
38 * @requires resourceName
39 * @param {String} name Name of the element
40 * @returns {String} returns the generated name
41 */
42FormForResource.prototype.__makeName = function makeName(name, params) {
43 if (params && params.name) {
44 return params.name;
45 }
46 return this.__complexNames && this.__path ? (this.__path + name.replace(/^([^\[]+)/, '[$1]')) : name;
47};
48
49/**
50 * Opening form tag
51 *
52 * For formFor() calls without passing a block
53 */
54FormForResource.prototype.begin = function begin() {
55 return this.__helpers.formTagBegin(this.__formParams || {});
56};
57
58/**
59 * Closing form tag
60 *
61 * For formFor() calls without passing a block
62 */
63FormForResource.prototype.end = function end() {
64 return this.__helpers.formTagEnd();
65};
66
67/**
68 * Fields for nested resource
69 *
70 * @param {String} name - resource name
71 *
72 */
73FormForResource.prototype.fieldsFor = function fieldsFor(name) {
74 return new FormForResource(this.__res[name], this.__formParams, this.__path + '[' + name + ']', this.__helpers);
75};
76
77/**
78 * Input tag helper
79 *
80 * Example in ejs:
81 *
82 * <%- form.input("test") %>
83 *
84 * This returns:
85 *
86 * <input name="test"/>
87 *
88 * @param {String} name Name of the element
89 * @param {Object} params Additional parameters
90 */
91FormForResource.prototype.input = function (name, params) {
92 params = params || {};
93 if (params.type === undefined) {
94 params.type = 'text';
95 }
96 if (params.value === undefined && params.type.toLowerCase() !== 'password') {
97 params.value = typeof this.__res[name] !== 'undefined' ? this.__res[name] : '';
98 }
99 return this.__helpers.inputTag({
100 name: this.__makeName(name, params),
101 id: this.__makeId(name, params)
102 }, params);
103};
104
105FormForResource.prototype.checkbox = function checkbox(name, params) {
106 params = params || {};
107 if (params.value === undefined) {
108 params.value = this.__res[name] || 1;
109 }
110 if (params.checked === undefined) {
111 if(this.__res[name]) {
112 params.checked = 'checked';
113 }
114 } else if (params.checked === false) {
115 delete params.checked;
116 }
117 return this.__helpers.inputTag({
118 name: this.__makeName(name, params),
119 id: this.__makeId(name, params),
120 type: 'checkbox'
121 }, params);
122};
123
124FormForResource.prototype.file = function file(name, params) {
125 return this.__helpers.inputTag({
126 name: this.__makeName(name, params),
127 id: this.__makeId(name, params),
128 type: 'file'
129 }, params);
130};
131
132/*
133 * Label helper
134 *
135 * Example in ejs:
136 *
137 * <%- form.label("test", false, {class: "control-label"}) %>
138 *
139 * This returns:
140 *
141 * <label for="test" class="control-label">Test</label>
142 *
143 * @param {String} name Name of the element
144 * @param {String} caption Optional different caption of the elemt
145 * @param {Object} params Additional parameters
146 */
147FormForResource.prototype.label = function label(name, caption, params) {
148 if (typeof caption !== 'string') {
149 if (!params) {
150 params = caption;
151 }
152 var description = '';
153 var model = this.__res.constructor.modelName;
154 var ctl = this.__helpers.controller;
155 var shortPath = 'models.' + model + '.fields.' + name;
156 var long = ctl.t(shortPath + '.label', '');
157
158 if (long) {
159 caption = long;
160 } else {
161 caption = ctl.t(shortPath, humanize(name));
162 }
163
164 description = ctl.t(shortPath + '.description', description);
165 if (description) {
166 caption += this.__helpers.icon('info-sign', {
167 rel: 'popover',
168 title: 'Help',
169 'data-content': description
170 });
171 }
172 }
173 return this.__helpers.labelTag(
174 caption,
175 {for: this.__makeId(name, params) },
176 params);
177};
178
179FormForResource.prototype.submit = function submit(name, params) {
180 return this.__helpers.tag('button', name || 'Commit', {type: 'submit'}, params);
181};
182
183FormForResource.prototype.button = function button(name, params) {
184 return this.__helpers.tag('button', name, params);
185};
186
187FormForResource.prototype.textarea = function textarea(name, params) {
188 var value = params && 'value' in params ? params.value : this.__res[name] || '';
189 return this.__helpers.textareaTag(this.__helpers.sanitize(value), {name: this.__makeName(name, params), id: this.__makeId(name, params)}, params);
190};
191
192/*
193 * Provides a select tag
194 *
195 * In ejs:
196 *
197 * <%- form.select("state", states, {fieldname: 'name', fieldvalue: '_id'}) %>
198 *
199 * Possible params:
200 * * blank: {STRING} Blank value to be added at the beginning of the list
201 * * fieldname: {STRING} Sets the name of the field in "options" field where the displayed values can be found. Default: "value"
202 * * fieldvalue: {STRING} Sets the name of the field in "options" field where the submitted values can be found. Default = fieldname
203 * * multiple: Can be set to false if size >1 to only select one value.
204 * * select: Select a value. If fieldname and fieldvalue are different, the value is compared with fieldvalue otherwise with fieldname.
205 * * size: Sets the displayed size of the select field
206 *
207 * @author [Uli Wolf](https://github.com/SirUli)
208 * @param {String} name Name of the select tag
209 * @param {Object} options Array of possible options
210 * @param {Object} params Additional parameters
211 */
212FormForResource.prototype.select = function select(name, options, params) {
213 options = options || [];
214 params = params || {};
215
216 // optional: Holds the displayed fieldname where the data can be found in 'options'
217 var optionFieldname = params.fieldname || 'value';
218 delete params.fieldname;
219
220 // optional: Holds the submittable fieldvalue where the data can be found in 'options'
221 var optionFieldvalue = params.fieldvalue || optionFieldname;
222 delete params.fieldvalue;
223
224 // optional: Holds the number of entries that can be seen at once
225 // If size > 1, multiple values can be selected (can be switched off via multiple:false)
226 // If size = 1, only one value is selectable (Drop-Down)
227 if (params.size === undefined) {
228 params.size = 1;
229 } else {
230 if (params.size > 1) {
231 if (params.multiple === undefined || params.multiple === true) {
232 params.multiple = 'multiple';
233 } else {
234 delete params.multiple;
235 }
236 }
237 }
238
239 // optional: Preselect an entry
240 if (params.select === undefined) {
241 params.select = this.__res[name] || '';
242 }
243 var optionSelected = params.select;
244 delete params.select;
245
246 // Render the options
247 var innerOptions = '';
248
249 // optional: Add a blank field at the beginning
250 if (params.blank !== undefined) {
251 innerOptions += this.__helpers.optionTag(this.__helpers.sanitize(params.blank), {value: ''});
252 }
253
254 for (var optionsNr in options) {
255 var option = options[optionsNr];
256 var optionParameters = {};
257
258 // Is the value in a seperate field?
259 if (option[optionFieldvalue] != option[optionFieldname]) {
260 optionParameters.value = option[optionFieldvalue];
261 }
262
263 var actualValue, displayValue;
264 if (typeof option === 'object') {
265 actualValue = optionFieldname in option ? option[optionFieldvalue] : option;
266 displayValue = optionFieldname in option ? option[optionFieldname] : option;
267 } else {
268 displayValue = actualValue = option + '';
269
270 }
271
272 if (activeValue(actualValue, optionSelected, options.matcher)) {
273 optionParameters.selected = 'selected';
274 }
275
276
277 // Generate the option Tags
278 innerOptions += this.__helpers.optionTag(this.__helpers.sanitize(displayValue), optionParameters);
279 }
280 // Render the select
281 return this.__helpers.selectTag(innerOptions, {name: this.__makeName(name, params), id: this.__makeId(name, params)}, params);
282};
283
284/**
285 * Compares a string against a string or an array
286 *
287 * @author [Uli Wolf](https://github.com/SirUli)
288 * @param {String} value Content of the String to be compared
289 * @param {String|Array} selectvalue String or Array of possiblities to be equal to the first string
290 * @returns {Boolean} True if the string matches the other string or array. False when not.
291 */
292function activeValue(value, selectvalue, matcher) {
293 var returnBool = false;
294
295 // If this is an Array (e.g. when multiple values should be selected), iterate
296 if (Object.prototype.toString.call(selectvalue) === '[object Array]') {
297 // This is an Array (e.g. when multiple values should be selected), iterate
298 for (var selectvalueNr in selectvalue) {
299 // Cast to String as these might be objects.
300 if (matcher) {
301 if (matcher(value, selectvalue[selectvalueNr])) {
302 returnBool = true;
303 }
304 } else if (String(value) == String(selectvalue[selectvalueNr])) {
305 returnBool = true;
306 continue;
307 }
308 }
309 } else {
310 // This is just one entry
311 // Cast to String as these might be objects.
312 if (String(value) == String(selectvalue)) {
313 returnBool = true;
314 }
315 }
316 return returnBool;
317}