Coverage

87%
500
435
65

pack/handlebars/handlebars.pack.js

100%
22
22
0
LineHitsSource
1/*jslint node: true,nomen: true, vars: true */
2/*jshint maxcomplexity: 5 */
31'use strict';
4
5/*
6 Proposed format for a Form pack, which is comprised of a template key registry, a template loading, and template rendering.
7 This should let the Schema2Html library be compatible with multiple template rendering/loading methods.
8 */
9
101var fs = require('fs');
111var handlebars = require('handlebars');
12
131module.exports = {
14 built: false,
15 templates: {
16 "formOpen": 'templates/form.open.hbs',
17 "formClose": 'templates/form.close.hbs',
18 "groupItemOpen": 'templates/group.item.open.hbs',
19 "groupItemClose": 'templates/group.item.close.hbs',
20 "textfield": 'templates/textfield.hbs',
21 "textarea": 'templates/textarea.hbs',
22 "upload": 'templates/upload.hbs',
23 "selectlist": 'templates/select.list.hbs',
24 "groupArrayOpen": 'templates/group.array.open.hbs',
25 "groupArrayClose": 'templates/group.array.close.hbs',
26 "help": 'templates/help.hbs',
27 "startGroup": 'templates/group.open.hbs',
28 "endGroup": 'templates/group.close.hbs',
29 "startGroupNoMethod": 'templates/group.no.method.open.hbs',
30 "startGroupHidden": 'templates/group.hidden.open.hbs',
31 "image": "templates/image.hbs",
32 "readonly": "templates/readonly.hbs",
33 "file": "templates/file.hbs",
34 "hidden": "templates/hidden.hbs",
35 "password": "templates/password.hbs"
36 },
37
38 /**
39 * @description Template loading method, each formPack should provide its own method to load its template files
40 *
41 * @param {string} key Key file for template to load
42 * @param {function} callback Callback function accepts err and result
43 */
44
45 loadTemplate: function(key, callback) {
4654 var path = this.templates[key];
4754 fs.readFile(__dirname + '/' + path, function(err, data) {
4854 var html;
49
5054 if (err !== null) {
511 html = null;
52 } else {
5353 html = handlebars.compile(data.toString());
54 }
55
5654 callback(err, html);
57 });
58 },
59
60 /**
61 * @description Template rendering method, each formPack should provide its own rendering method
62 *
63 * @param {*} template Template (function, object, or string) returned from the loadTemplate function
64 * @param {object} data Data object from json schema
65 * @param {function} callback callback function accepts err, and result
66 */
67
68 renderTemplate: function(template, data, callback) {
6994 var result = null,
70 err = null;
71
7294 if (template === null) {
731 callback(new Error("Could not render template of null"));
74 } else {
75
7693 try {
7793 result = template(data);
78 } catch (e) {
791 err = e;
80 }
81
8293 callback(err, result);
83 }
84 },
85
86 /**
87 * @description Utility function to clear up any runtime dependencies neccesary to run the form pack
88 */
89
90 build: function() {
9119 handlebars.registerHelper('ifEqual', function (v1, v2, options) {
92165 return v1 === v2 ? options.fn(this) : false;
93 });
94
9519 this.built = true;
96 },
97
98 /**
99 * @description Utility function to manage roles for each item within a schema file, called by the render loop on each schema item
100 */
101
102 security: function(schema) {
10327 return true;
104 }
105};

schema.dependencies.js

100%
117
117
0
LineHitsSource
1/*jslint node: true,nomen: true, vars: true */
2/*jshint maxcomplexity: 5 */
31'use strict';
4
51var async = require('async');
61var _ = require('lodash');
71var schemaResolver = require('./schema.resolver');
81var defaultFormPack = require('./pack/handlebars/handlebars.pack');
9
10/**
11 * @class SchemaDependencies
12 * @description Resolves depdencies based on a json schema file
13 *
14 * @param {string} basePath directory path to base subsequent file based dependencies upon
15 *
16 */
17
181function SchemaDependencies(basePath) {
1924 this.dependencyCache = {};
2024 this.schemaBasePath = basePath;
21}
22
23/**
24 * @callback SchemaDependencyCallback
25 * @param {error} Error
26 * @param {boolean} Did result come from cache?
27 * @param {object} The resolved dependencies result should
28 */
29
30/**
31 * @memberOf SchemaDependencies
32 * @description Resolves and individual schema
33 *
34 * @param {string} ref
35 * @param {object} schema
36 * @param {SchemaDependencyCallback} callback
37 */
38
39
401SchemaDependencies.prototype.resolveSchemaDependency = function(ref, schema, callback) {
4139 var _that = this;
42
4339 if (_that.dependencyCache[ref] !== undefined) {
449 callback(null, true, _that.dependencyCache[ref]); // we all ready loaded this dependency
459 return;
46 }
47
48 // need to parse out logic here request,file, sub schema, etc
4930 if (ref.indexOf('#') === 0) {
50 /*
51 Internal pointer resolution needs some work, need to go into further research of the spec for this
52 */
5314 schemaResolver.definition(schema, ref.replace('#/definitions/', ''), function(err, result) {
5414 _that.dependencyCache[ref] = result || null;
5514 callback(err, false, result);
56 });
5716 } else if (ref.indexOf('http') === 0 || ref.indexOf('https') === 0) {
58 // http reference
593 schemaResolver.http(ref, function(err, result) {
603 _that.dependencyCache[ref] = result || null;
613 callback(err, false, result);
62 });
63 } else {
64 // fs schema reference
6513 schemaResolver.file(this.schemaBasePath, ref, function(err, result) {
6613 _that.dependencyCache[ref] = result || null;
6713 callback(err, false, result);
68 });
69 }
70
71};
72
73/**
74 * @memberOf SchemaDependencies
75 * @description Resolves and individual external data source
76 *
77 * @param {*} ref
78 * @param {DependencyLoopCallback}
79 *
80 */
81
821SchemaDependencies.prototype.resolveDataDependency = function(ref, rootSchema, callback) {
8310 var _that = this,
84 handler,
85 url,
86 type,
87 resolver,
88 allowed = {
89 'http': true,
90 'https': true
91 };
92
93 // need to parse if we should use custom handler to resolve data
9410 handler = ref.split('://');
9510 type = handler[0];
9610 url = allowed[type] === true ? ref : handler.slice(1, handler.length).join("");
97
9810 resolver = schemaResolver[type];
99
10010 if (typeof resolver === 'function') {
1019 if (type === 'file') {
1023 resolver(this.schemaBasePath, url, function(err, result) {
1033 _that.dependencyCache[ref] = result || null;
1043 callback(err, result);
105 });
1066 } else if (type === 'definition') {
107 // this could use a bit of fixing to be more spec friendly
1081 resolver(rootSchema, url, function(err, result) {
1091 _that.dependencyCache[ref] = result || null;
1101 callback(err, result);
111 });
112 } else {
1135 resolver(url, function(err, result) {
1145 _that.dependencyCache[ref] = result || null;
1155 callback(err, result);
116 });
117 }
118 } else {
1191 callback(new Error('No way to resolve data source:' + ref), null);
120 }
121};
122
123/**
124 * @callback DependencyLoopCallback
125 * @param {error} Error
126 * @param {object} The resolved dependencies result should
127 */
128
129/**
130 * @memberOf SchemaDependencies
131 * @description Resolves the anyOf paramater of a json schema object, and passes its result into the render loop
132 *
133 * @param {array} dependencies
134 * @param {object} rootSchema
135 * @param {object} q
136 * @param {DependencyLoopCallback}
137 */
138
139
1401SchemaDependencies.prototype.dependencyLoopAnyOf = function(dependencies, rootSchema, q, callback) {
14111 var _that = this,
142 exited = false,
143 funcs = [];
144
14511 dependencies.forEach(function(dependency) {
14631 if (dependency.$ref !== undefined && dependency.$ref !== null && exited === false) {
147
14829 funcs.push(
149 function(cb) {
15029 _that.resolveSchemaDependency(dependency.$ref, rootSchema, function(err, cached, result) {
151
15229 if (err !== null) {
1531 cb(err);
154 } else {
15528 if (cached === false) {
156
15720 q.push({schema: result, root: result}, function(err) {
15817 _that.dependencyPushError(err);
159 });
160 }
161
16228 cb();
163 }
164
165 });
166 }
167 );
168 } else {
1692 exited = true;
170 }
171 });
172
17311 if (exited === false) {
17410 async.parallel(funcs, function(err) {
17510 if (err !== null) {
1761 callback(err, null);
177 } else {
1789 callback(null, null);
179 }
180 });
181 } else {
1821 callback(new Error('$ref missing'), null); // no reference callback and exit
183 }
184
185};
186
187/**
188 * @memberOf SchemaDependencies
189 * @description Iterates over an objects properties, passing them back into the render loop
190 *
191 * @param {object} schema
192 * @param {object} rootSchema
193 * @param {object} q
194 * @param {DependencyLoopCallback}
195 */
196
1971SchemaDependencies.prototype.dependencyLoopProperties = function(schema, rootSchema, q, callback) {
19814 var _that = this;
199
20014 schema.properties = schema.properties || {};
201 // no schemas to resolve or length is 0 move on to props
20214 _.forOwn(schema.properties, function(property) {
203 // push properties into the queue
20430 q.push({schema: property, root: rootSchema}, function(err) {
20530 _that.dependencyPushError(err);
206 });
207
208 });
209
21014 callback(null);
211};
212
213/**
214 * @memberOf SchemaDependencies
215 * @description Resolves any $ref or datasrc dependencies of an individual property
216 *
217 * @param {object} schema
218 * @param {object} rootSchema
219 * @param {object} q
220 * @param {DependencyLoopCallback}
221 */
222
2231SchemaDependencies.prototype.dependencyLoopProperty = function(schema, rootSchema, q, callback) {
22438 var _that = this,
225 opts = schema.options || {};
226
22738 if (opts.datasrc) {
2287 _that.resolveDataDependency(opts.datasrc, rootSchema, function(err, result) {
229
2307 if (err !== null) {
2311 callback(new Error('could not resolve data:' + opts.datasrc), null);
232 } else {
2336 callback(null, result);
234 }
235 });
23631 } else if (schema.$ref) {
237 // single dependency
2384 _that.resolveSchemaDependency(schema.$ref, rootSchema, function(err, cached, result) {
2394 if (err !== null) {
2401 callback(err, null);
241 } else {
2423 if (cached === false) {
243
2443 q.push({schema: result, root: result});
245 }
246
2473 callback(null, result);
248 }
249 });
250
251 } else {
25227 callback(null, null);
253 }
254};
255
256/**
257 * @memberOf SchemaDependencies
258 * @description Pushes errors into an array for dependencies that could not be resolved
259 *
260 * @param {array} e
261 * @param {err} error The error to push intot the array
262 */
263
2641SchemaDependencies.prototype.dependencyPushError = function(e, err) {
265110 if (err !== null && err !== undefined) {
2661 e.push(err.toString());
267 }
268};
269
270/**
271 * @memberOf SchemaDependencies
272 * @description Main recurisve function, loops through all possible schema and loaded dependencies until all dependencies have been resolved, or an error has occured
273 *
274 * @param {object} schema
275 * @param {DependencyLoopCallback}
276 */
277
2781SchemaDependencies.prototype.resolveDependencies = function(schema, callback) {
279
2808 var _that = this,
281 errors = [],
282 q;
283
2848 q = async.queue(function(payload, asyncCallback) {
28559 var schema = payload.schema || {},
286 root = payload.root;
287
28859 if (schema.type === 'object') {
289
29022 if (_.isArray(schema.anyOf) && schema.anyOf.length > 0) {
291 // resolve sub schema
2924 _that.dependencyLoopAnyOf(schema.anyOf, root, q, function(err) {
2934 _that.dependencyPushError(errors, err);
2944 asyncCallback();
295 });
29618 } else if (_.isArray(schema.oneOf) && schema.oneOf.length > 0) {
2974 _that.dependencyLoopAnyOf(schema.oneOf, root, q, function(err) {
2984 _that.dependencyPushError(errors, err);
2994 asyncCallback();
300 });
301 } else {
302
30314 _that.dependencyLoopProperties(schema, root, q, function(err) {
30414 _that.dependencyPushError(errors, err);
30514 asyncCallback();
306
307 });
308 }
309
31037 } else if (schema.type === 'array') {
311
3124 if (schema.items) {
3133 q.push({schema: schema.items, root: schema.items});
314 }
315
3164 asyncCallback(); // need to handle hidden references within array
317
318 } else {
319
32033 _that.dependencyLoopProperty(schema, root, q, function(err) {
32133 _that.dependencyPushError(errors, err);
32233 asyncCallback();
323
324 });
325
326 }
327
328 });
329
3308 q.drain = function() {
3318 if (errors.length > 0) {
3321 callback(new Error('Dependencies could not be resolved:' + errors.join(',')), null);
333 } else {
3347 callback(null, _that.dependencyCache); // empty dependency queue, callback
335 }
336
337 };
338
3398 q.push({root: schema, schema: schema}, function(err) {
3408 _that.dependencyPushError(errors, err);
341 });
342};
343
3441module.exports = SchemaDependencies;

schema.resolver.js

100%
37
37
0
LineHitsSource
1/*jslint node: true,nomen: true, vars: true */
2/*jshint maxcomplexity: 5 */
31'use strict';
4
51var fs = require('fs');
61var path = require('path');
71var http = require('http');
81var request = require('request');
9
10/**
11 * @description resolves a schema file from the local file system
12 */
13
141function resolveSchemaFile(basePath, refPath, callback) {
1520 var result,
16 parseErr = null;
17
1820 if (basePath === undefined || basePath === null) {
192 callback(new Error("No Basepath Specified:" + refPath));
202 return false;
21 }
22
2318 fs.readFile(path.normalize(basePath + '/' + refPath), function(err, data) {
2418 if (err !== null) {
253 callback(err);
26 } else {
2715 try {
2815 result = JSON.parse(data);
29 } catch (e) {
301 parseErr = e;
31 }
32
3315 callback(parseErr, result);
34 }
35 });
36
37}
38
39/**
40 * @description resolves a schema file based on an internal definition
41 */
42
431function resolveSchemaDefinition(baseObject, refPath, callback) {
44
4518 if (baseObject === null) {
461 callback(new Error("Missing Baseobject"));
471 return;
48 }
49
5017 baseObject.definitions = baseObject.definitions || {}; // stub in pointer
51
52
5317 if (baseObject.definitions[refPath]) {
5416 callback(null, baseObject.definitions[refPath]);
55 } else {
561 callback(new Error('missing definition'));
57 }
58
59 //callback(null, {"type": "object"});
60}
61
62/**
63 * @description resolves a schema files based on http
64 */
65
661function resolveHttp(refPath, callback) {
6712 request(refPath, function(err, res, body) {
6812 var json = null,
69 parseErr = null;
70
71
7212 if (!err && res.statusCode === 200) {
7311 try {
7411 json = JSON.parse(body);
75 } catch (e) {
763 parseErr = e;
77 }
78
7911 callback(parseErr, json);
80
81 } else {
821 callback(new Error('failed to load resource:' + refPath), null);
83 }
84
85 });
86}
87
88/**
89 * @description resolves a schema file based on https
90 */
91
921function resolveHttps(refPath, callback) {
931 resolveHttp(refPath, callback);
94}
95
961module.exports = {
97 file: resolveSchemaFile,
98 definition: resolveSchemaDefinition,
99 http: resolveHttp,
100 https: resolveHttps
101};

schema.util.js

81%
37
30
7
LineHitsSource
1/*jslint node: true,nomen: true, vars: true */
2/*jshint maxcomplexity: 5 */
31'use strict';
4
51var util = {
6
7 dotSyntax: function(s) {
825 if(typeof s === 'string') {
925 s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
1025 s = s.replace(/^\./, '');
11 }
12
1325 return s;
14 },
15
16 rawName: function(s) {
1743 var r;
18
1943 if(typeof s === 'string') {
2043 s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
2143 s = s.replace(/^\./, '');
22
23 }
24
2543 return s;
26 },
27
28 innerName: function(s,d) {
2943 var r;
30
3143 if(typeof s === 'string') {
3243 s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
3343 s = s.replace(/^\./, '');
34
3543 r = s.split(']');
3643 s = r[r.length-1];
3743 s = s.indexOf('.') === 0 ? s.substr(1,s.length-1) : s;
38
39 }
40
4143 return s;
42 },
43
44 /**
45 * @memberOf Schema2Html
46 * @description Retreive a value from object based on dot notation
47 */
48
49 retrieveValue: function(o, s, t) {
5019 if (o && s!==null && typeof o !== 'string') {
5119 s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
5219 s = s.replace(/^\./, ''); // strip a leading dot
53
5419 if(t){
550 return '{{' + "data." + s + '}}';
56 }
57
5819 var a = s.split('.');
5919 if (!t) {
6019 while (a.length) {
6119 var n = a.shift();
6219 if (n in o) {
630 o = o[n];
64 } else {
6519 return;
66 }
67 }
68 }
690 return t === true ? '{{' + "data." + s + '}}' : o;
70 } else {
710 return null;
72 }
73 },
74
75
76 repeat: function(num,dp) {
770 if (num > 1) {
780 return new Array( num ).join( dp );
79 } else {
800 return "";
81 }
82 }
83
84}
85
861module.exports = util;
87
88
89
90
91

schema2html.js

79%
287
229
58
LineHitsSource
1/*jslint node: true,nomen: true, vars: true */
2/*jshint maxcomplexity: 5 */
31'use strict';
4
51var async = require('async');
61var _ = require('lodash');
71var schemaResolver = require('./schema.resolver');
81var SchemaDependencies = require('./schema.dependencies');
91var defaultFormPack = require('./pack/handlebars/handlebars.pack');
101var util = require('./schema.util');
11
121var expId = /-([0-9]+)/g; // id abstraction
131var expBracket = /\[([0-9]+)\]/g; // array key abstraction
141var expIdDummy = /id\=\"/g;
151var idxReplace = "--index--";
16
17/**
18 * @class Schema2Html
19 *
20 * @param {object} schema Json Schema object to render into form
21 * @param {object} data Data object to render
22 * @param {Schema2HtmlOptions} options Configuration object for rendering loop
23 */
24
25/**
26 * @typedef Schema2HtmlOptions
27 * @description Configuration object for render loop
28 *
29 * @param {string} schemaBasePath If loading $ref schema files from local fs, path should be specified
30 * @param {string} dataPrefix If your data needs prefixing, assign the a prefix to each variable and name
31 * @param {object} pack Form pack to use
32 */
33
341function Schema2Html(schema, data, options) {
3515 this.schema = schema || {};
3615 this.data = data || {};
3715 this.options = options || {};
3815 this.options.dataPrefix = this.options.dataPrefix || null;
3915 this.templates = {};
4015 this.funcs = [];
4115 this.html = [];
4215 this.htmlTemplate = [];
4315 this.dependencyCache = {};
4415 this.additionalData = {};
4515 this.schemaBasePath = this.options.schemaBasePath;
4615 this.pack = this.options.pack || defaultFormPack;
4715 this.pack.build();
4815 this.renderMode = this.pack.renderMode || 1;
4915 this.pos = 0;
5015 this.arrayLoopCount = 0;
5115 this.rawData = this.options.rawData || false;
5215 this.lookupTable = {
53 object: this.renderLoopObject.bind(this),
54 string: this.renderLoopString.bind(this),
55 array: this.renderLoopArray.bind(this),
56 number: this.renderLoopNumber.bind(this),
57 integer: this.renderLoopInteger.bind(this),
58 boolean: this.renderLoopBoolean.bind(this),
59 ref: this.renderLoopRef.bind(this)
60 };
61}
62
63/**
64 * @memberOf Schema2Html
65 * @description Add a data resolving function
66 *
67 * @param {string} key
68 * @param {function} func
69 */
70
711Schema2Html.prototype.addResolver = function(key, func) {
720 schemaResolver[key] = func;
73};
74
75/**
76 * @memberOf Schema2Html
77 * @description Load all dependencies and output html form
78 *
79 * @param {function} callback
80 */
81
821Schema2Html.prototype.buildForm = function(callback) {
833 var _that = this,
84 schemaDependencies = new SchemaDependencies(this.options.schemaBasePath);
85
86
87
883 schemaDependencies.resolveDependencies(this.schema, function(err, dependencies) {
89
90
91
923 if (err !== null) {
931 callback(err);
941 return null;
95 }
96
972 _that.dependencyCache = dependencies;
982 _that.addRenderTask(_that.addFormOpen(_that.schema.id, _that.options.endpoint, _that.options.method));
992 _that.renderLoop(_that.schema, 0, _that.options.dataPrefix, false);
1002 _that.addRenderTask(_that.addFormClose());
101
1022 async.series(_that.funcs, function(err) {
1032 callback(err, _that.html.join(""), _that.htmlTemplate.join("").replace(expId,'-' + idxReplace).replace(expBracket, '[' + idxReplace + ']').split('id="').join('id="tmpl-').split("id='").join("id='tmpl-"));
104 });
105 });
106
107};
108
109/**
110 * @memberOf Schema2Html
111 * @description Main circular render loop, all objects are passed through the loop, with render functions then being executed through the aysnc library
112 *
113 * @param {object} schema
114 * @param {integer} depth
115 * @param {string} scopeName
116 */
117
1181Schema2Html.prototype.renderLoop = function(schema, depth, scopeName) {
11928 schema = schema || {};
12028 var type = schema.$ref ? 'ref' : schema.type,
121 id = schema.id || scopeName,
122 execProcess;
123
12428 id = this.generateId(id);
125
12628 execProcess = this.lookupTable[type] || null;
127
12828 if (execProcess !== null && this.pack.security(schema) === true) {
12927 execProcess(id, scopeName, depth, schema);
130 }
131};
132
133/**
134 * @memberOf Schema2Html
135 * @description adds an individual render task for the string schema type
136 *
137 * @param {string} id html id string
138 * @param {string} name html name string
139 * @param {integer} depth how deep the item is nested
140 * @param {object} schema
141 */
142
1431Schema2Html.prototype.renderLoopString = function(id, name, depth, schema) {
14417 var label = schema.title || name,
145 options = schema.options || {},
146 val = util.retrieveValue(this.data, name, false);
147
14817 options.depth = depth;
14917 options.key = util.dotSyntax(name);
150
151
15217 this.addRenderTask(this.addString(id, name, val, label, null, options));
153};
154
155/**
156 * @memberOf Schema2Html
157 *
158 * @param {string} id html id string
159 * @param {string} name html name string
160 * @param {integer} depth how deep the item is nested
161 * @param {object} schema
162 *
163 */
164
1651Schema2Html.prototype.renderLoopRef = function(id, name, depth, schema) {
166 //this.dependencyCache[schema.$ref].type || null; // try ref if no type
1671 this.renderLoop(this.dependencyCache[schema.$ref], depth, name);
168};
169
170/**
171 * @memberOf Schema2Html
172 * @description renders schema#object
173 *
174 * @param {string} id html id string
175 * @param {string} scopeName html name string
176 * @param {integer} depth how deep the item is nested
177 * @param {object} schema
178 */
179
1801Schema2Html.prototype.renderLoopObject = function(id, scopeName, depth, schema) {
1817 var _that = this, name, match, options;
182
1837 options = schema.options || {};
1847 depth += 1;
185
1867 this.addRenderTask(this.addGroupOpen(id, 0, depth)); // open a group
187
1887 _.forOwn(schema.properties, function(property, key) {
18923 name = _that.generateName(scopeName, key);
19023 property.id = _that.generateId(name);
19123 if (property.anyOf !== undefined) {
1920 if (_.isArray(property.anyOf)) {
1930 match = options.matchOn;
194
1950 _that.renderLoopArrayAnyOf(id, scopeName, depth, 0, match, property.anyOf);
196
197 }
198 } else {
19923 _that.renderLoop(property, depth + 1, name); // send property back in the loop
200 }
201 });
202
2037 this.addRenderTask(this.addGroupClose(id, depth)); // close group
204
205
206};
207
208/**
209 * @memberOf Schema2Html
210 * @description adds an individual render task for the array schema type
211 *
212 * @param {string} id
213 * @param {string} name
214 * @param {integer} depth
215 * @param {integer} index
216 * @param {string} match
217 * @param {object} schema
218 */
219
2201Schema2Html.prototype.renderLoopArrayAnyOf = function(id, name, depth, index, match, schema) {
2210 var _that = this,
222 ref,
223 matchVal;
224
2250 _that.arrayLoopCount += 1;
226
2270 schema.anyOf.forEach(function(val) {
2280 ref = val.$ref;
229
2300 matchVal = util.retrieveValue(_that.data, (name + '[' + index + ']' + '[' + match + ']'));
231
2320 if (ref === matchVal && _that.renderMode !== 2) {
2330 _that.renderLoop(_that.dependencyCache[ref], (depth + 1), (name + '[' + index + ']'));
2340 } else if (_that.renderMode === 2) {
235 // render mode is 2, means were rendering a template based output
2360 var indexVal = _that.pack.engine.index;
2370 var backtickVal = _that.pack.engine.backTick;
2380 var open = _that.pack.engine.open;
2390 var close = _that.pack.engine.close;
240
2410 _that.addRenderTask(_that.addAnyOfOpen(ref));
2420 _that.renderLoop(_that.dependencyCache[ref], (depth + 1), (name + '[' + open + util.repeat(_that.arrayLoopCount, backtickVal) + indexVal + close + ']'));
2430 _that.addRenderTask(_that.addAnyOfClose(ref));
244 }
245
246 });
247
2480 _that.arrayLoopCount += -1;
249};
250
251/**
252 * @memberOf Schema2Html
253 * @description adds render tasks for an item within an array
254 *
255 * @param {string} id
256 * @param {string} name
257 * @param {integer} depth
258 * @param {integer} index
259 * @param {object} schema
260 */
261
2621Schema2Html.prototype.renderLoopArrayItem = function(id, name, depth, index, schema) {
263
264
265 // plain old item definition
2662 if (this.renderMode === 2) {
267
2680 var indexVal = this.pack.engine.index;
2690 var open = this.pack.engine.open;
2700 var close = this.pack.engine.close;
271
2720 this.addRenderTask(this.addGroupTag('groupItemOpen', id + '-group-' + open + indexVal + close + '-' + name + '[' + open + indexVal + close + ']', depth + 1)); // render tag open
2730 this.renderLoop(schema, depth + 1, name + '[' + open + indexVal + close + ']');
2740 this.addRenderTask(this.addGroupTag('groupItemClose', id + '-group-' + open + indexVal + close, name + '[' + open + indexVal + close + ']', depth + 1)); // render tag close
275
2760 return id + '-group-' + open + indexVal + close + '-' + name + '[' + open + indexVal + close + ']';
277
278 } else {
2792 this.addRenderTask(this.addGroupTag('groupItemOpen', id + '-group-' + index, name + '[' + index + ']', depth + 1)); // render tag open
2802 this.renderLoop(schema, depth + 1, name + '[' + index + ']');
2812 this.addRenderTask(this.addGroupTag('groupItemClose', id + '-group-' + index, name + '[' + index + ']', depth + 1)); // render tag close
282
2832 return id + '-group-' + index;
284 }
285
286
287};
288
289/**
290 * @memberOf Schema2Html
291 * @description adds render tasks for schema#array
292 *
293 * @param {string} id
294 * @param {string} name
295 * @param {integer} depth
296 * @param {object} schema
297 */
298
299
3001Schema2Html.prototype.renderLoopArray = function(id, name, depth, schema) {
3012 var _that = this,
302 insertId = "",
303 minItems = schema.minItems || 1,
304 maxItems = schema.maxItems,
305 uniqueItems = schema.uniqueItems,
306 itemsToRender,
307 options,
308 dataArr,
309 match,
310 ref,
311 i;
312
313
3142 options = schema.items.options || {};
3152 dataArr = util.retrieveValue(this.data, name);
316
3172 itemsToRender = _.isArray(dataArr) ? dataArr.length : minItems;
3182 itemsToRender = itemsToRender || 1;
3192 itemsToRender = this.renderMode === 2 ? 1 : itemsToRender;
320
3212 this.addRenderTask(this.addGroupTag('groupArrayOpen', id + '-group-many', name, depth, schema)); // open a group
322
3232 for (i = 0; i < itemsToRender; i += 1) {
3242 if (schema.items.$ref) {
3250 this.renderLoopArrayItem(id, name, depth, i, _that.dependencyCache[schema.items.$ref]);
3262 } else if (schema.items.anyOf) {
3270 match = options.matchOn;
328
3290 this.renderLoopArrayAnyOf(id, name, depth, i, match, schema.items);
330
3312 } else if (schema.items.oneOf) {
3320 match = options.matchOn;
333
3340 schema.items.oneOf.forEach(function(val, index) {
3350 ref = val.$ref;
336
3370 if (ref === match) {
3380 this.renderLoop(_that.dependencyCache[ref], depth + 1, name + '[' + index + ']');
339 }
340
341 });
342
343 } else {
3442 insertId = this.renderLoopArrayItem(id, name, depth, i, schema.items);
345 }
346 }
347
3482 insertId = insertId.replace(expId, '-' + idxReplace).replace(expBracket, '[' + idxReplace + ']');
349
350
351
3522 this.addRenderTask(this.addGroupTag('groupArrayClose', id + '-group-many', name, depth, {insertTemplate: 'tmpl-' + insertId})); // open a group
353
354};
355
356/**
357 * @memberOf Schema2Html
358 * @description adds an individual render task for the number schema type
359 *
360 * @param {string} id html id string
361 * @param {string} name html name string
362 * @param {integer} depth how deep the item is nested
363 * @param {object} schema
364 */
365
3661Schema2Html.prototype.renderLoopNumber = function(id, name, depth, schema) {
3671 this.renderLoopString(id, name, depth, schema);
368};
369
370/**
371 * @memberOf Schema2Html
372 * @description adds an individual render task for the integer schema type
373 *
374 * @param {string} id html id string
375 * @param {string} name html name string
376 * @param {integer} depth how deep the item is nested
377 * @param {object} schema
378 */
379
3801Schema2Html.prototype.renderLoopInteger = function(id, name, depth, schema) {
3811 this.renderLoopString(id, name, depth, schema);
382};
383
384/**
385 * @memberOf Schema2Html
386 * @description adds an individual render task for the boolean schema type
387 *
388 * @param {string} id html id string
389 * @param {string} name html name string
390 * @param {integer} depth how deep the item is nested
391 * @param {object} schema
392 */
393
3941Schema2Html.prototype.renderLoopBoolean = function(id, name, depth, schema) {
3951 this.renderLoopString(id, name, depth, schema);
396};
397
398/**
399 * @memberOf Schema2Html
400 * @description return a function adding a formOpenTag with a single callback that can be added to the control flow, or executed standalone
401 *
402 * @param {string} id
403 * @param {string} endpoint
404 * @param {string} method
405 */
406
4071Schema2Html.prototype.addFormOpen = function(id, endpoint, method) {
4083 var _that = this,
409 pos = this.pos;
410
4113 id = this.generateId(id);
4123 this.pos += 1;
413
4143 return function(callback) {
4153 _that.renderFormTag('formOpen', id, endpoint, method, function(err, result) {
4163 _that.appendHtml(result, pos);
4173 _that.appendDummyHtml(result, pos);
4183 callback(err, result);
419 });
420 };
421};
422
423/**
424 * @memberOf Schema2Html
425 * @description
426 *
427 * @param {string} dataRef
428 */
429
4301Schema2Html.prototype.addAnyOfOpen = function(dataRef) {
4310 var _that = this,
432 pos = this.pos;
4330 this.pos += 1;
4340 return function(callback) {
4350 _that.renderAnyOfOpenTag(dataRef, function(err, result) {
4360 _that.appendHtml(result, pos);
4370 _that.appendDummyHtml(result, pos);
4380 callback(err, result);
439 });
440 };
441};
442
443/**
444 * @memberOf Schema2Html
445 * @description
446 *
447 * @param {string} dataRef
448 */
449
4501Schema2Html.prototype.addAnyOfClose = function(dataRef) {
4510 var _that = this,
452 pos = this.pos;
4530 this.pos += 1;
4540 return function(callback) {
4550 _that.renderAnyOfCloseTag(dataRef, function(err, result) {
4560 _that.appendHtml(result, pos);
4570 _that.appendDummyHtml(result, pos);
4580 callback(err, result);
459 });
460 };
461};
462
463/**
464 * @memberOf Schema2Html
465 * @description
466 *
467 * @param {string} dataRef
468 * @param {function} callback
469 */
470
4711Schema2Html.prototype.renderAnyOfOpenTag = function(dataRef, callback) {
4720 var params = {
473 dataRef: dataRef
474 };
475
476
477
4780 this.renderTemplate('anyOfOpen', params, function(err, result) {
4790 callback(err, result);
480 });
481
482};
483
484/**
485 * @memberOf Schema2Html
486 * @description
487 *
488 * @param {string} dataRef
489 * @param {function} callback
490 */
491
4921Schema2Html.prototype.renderAnyOfCloseTag = function(dataRef, callback) {
4930 var params = {
494 dataRef: dataRef
495 };
496
4970 this.renderTemplate('anyOfClose', params, function(err, result) {
4980 callback(err, result);
499 });
500
501};
502
503/**
504 * @memberOf Schema2Html
505 * @description Renders the opening and closing tags
506 *
507 * @param {string[formOpen,formClose]} type specify if type is opening or closing
508 * @param {number} id html id assigned to form
509 * @param {string} form endpoint submission endpoint
510 * @param {string} method http method
511 * @param {callback}
512 */
513
5141Schema2Html.prototype.renderFormTag = function(type, id, endpoint, method, callback) {
51510 var params = {};
516
51710 params.id = id;
51810 params.endpoint = endpoint;
51910 params.method = method;
520
52110 this.renderTemplate(type, params, function(err, result) {
52210 callback(err, result);
523 });
524
525};
526
527/**
528 * @memberOf Schema2Html
529 * @description return a function adding a formCloseTag with a single callback that can be added to the control flow, or executed standalone
530 */
531
5321Schema2Html.prototype.addFormClose = function() {
5333 var _that = this,
534 pos = this.pos;
535
5363 this.pos += 1;
537
5383 return function(callback) {
5393 _that.renderFormTag('formClose', null, null, null, function(err, result) {
5403 _that.appendHtml(result, pos);
5413 _that.appendDummyHtml(result, pos);
5423 callback(err, result);
543 });
544 };
545};
546
547/**
548 * @memberOf Schema2Html
549 * @description Renders a template based on a path of a given key in the configuration
550 *
551 * @param {string} key
552 * @param {object} params
553 * @param {callback} callback
554 */
555
5561Schema2Html.prototype.renderTemplate = function(key, params, callback) {
55772 var result = null,
558 template = null,
559 _that = this;
560
56172 if (this.pack.templates[key] === undefined) {
5621 callback(new Error("Template has no valid path:" + key));
5631 return null; // exit this is not going to work
564 }
565
56671 if (typeof (this.pack.templates[key]) !== 'function') {
56710 this.pack.loadTemplate(key, function(err, compiled) {
56810 if (err === null) {
56910 try {
57010 _that.pack.templates[key] = compiled;
571 // result = compiled(params);
57210 _that.pack.renderTemplate(compiled, params, function(pErr, pResult) {
57310 callback(pErr, pResult);
574 });
575
576 } catch (templateErr) {
5770 callback(templateErr, result);
578 }
579 } else {
5800 callback(err, result);
581 }
582
583 });
584 } else {
58561 template = this.pack.templates[key];
58661 _that.pack.renderTemplate(template, params, function(pErr, pResult) {
58761 callback(pErr, pResult);
588 });
589 //result = template(params);
590 //callback(null, result);
591 }
592};
593
594/**
595 * @memberOf Schema2Html
596 * @description changes a template path
597 *
598 * @param {string} key
599 * @param {string} path
600 */
601
6021Schema2Html.prototype.changeTemplate = function(key, path) {
6030 this.config.templates[key] = path;
604};
605
6061Schema2Html.prototype.renderCustomHandler = function() {};
607
608/**
609 * @memberOf Schema2Html
610 * @description render group tag open
611 *
612 * @param {string} id
613 * @param {integer} total
614 * @param {integer} depth
615 */
616
6171Schema2Html.prototype.addGroupOpen = function(id, total, depth) {
6188 var _that = this,
619 pos = this.pos;
620
6218 this.pos += 1;
6228 id = this.generateId(id);
623
6248 return function(callback) {
6258 _that.renderGroupOpen(id, total, depth, function(err, result) {
6268 _that.appendHtml(result, pos);
6278 _that.appendDummyHtml(result, pos);
6288 callback(err, result);
629 });
630 };
631};
632
633/**
634 * @memberOf Schema2Html
635 * @description render group tag open
636 *
637 * @param {string} id
638 * @param {integer} total
639 * @param {integer} depth
640 * @param {function} callback
641 */
642
6431Schema2Html.prototype.renderGroupOpen = function(id, total, depth, callback) {
6449 var params = {};
645
6469 params.id = id;
6479 params.total = total || 0;
6489 params.options = {
649 depth: depth
650 };
651
6529 this.renderTemplate('startGroup', params, function(err, result) {
6539 callback(err, result);
654 });
655};
656
657/**
658 * @memberOf Schema2Html
659 * @description add group tag close
660 *
661 * @param {string} id
662 * @param {integer} depth
663 */
664
6651Schema2Html.prototype.addGroupClose = function(id, depth) {
6668 var _that = this,
667 pos = this.pos,
668 params = {
669 id: id
670 };
671
6728 params.options = {
673
674 depth: depth
675 };
676
6778 this.pos += 1;
678
6798 return function(callback) {
6808 _that.renderGroupClose(params, function(err, result) {
6818 _that.appendHtml(result, pos);
6828 _that.appendDummyHtml(result, pos);
6838 callback(err, result);
684 });
685 };
686};
687
688/**
689 * @memberOf Schema2Html
690 * @description render group tag close
691 *
692 * @param {object} params
693 * @param {function} callback
694 */
695
6961Schema2Html.prototype.renderGroupClose = function(params, callback) {
6979 this.renderTemplate('endGroup', params, function(err, result) {
6989 callback(err, result);
699 });
700};
701
702/**
703 * @memberOf Schema2Html
704 * @description returns a render function for a open/close tag for generic groups
705 *
706 * @param {string} type
707 * @param {string} id
708 * @param {string} name
709 * @param {integer} depth
710 * @param {object} options
711 */
712
7131Schema2Html.prototype.addGroupTag = function(type, id, name, depth, options) {
7148 var _that = this,
715 pos = this.pos;
716
7178 id = this.generateId(id);
718
7198 options = options || {};
7208 options.key = util.dotSyntax(name);
7218 options.keyName = util.rawName(name);
7228 options.keyInner = util.innerName(name);
7238 options.arrayDepth = this.arrayLoopCount;
7248 this.pos += 1;
725
7268 return function(callback) {
7278 _that.renderGroupTag(id, name, depth, options, type, function(err, result) {
7288 _that.appendHtml(result, pos);
7298 _that.appendDummyHtml(result, pos);
7308 callback(err, result);
731 });
732 };
733};
734
735/**
736 * @description renders are group open/close tag
737 *
738 * @param {string} id
739 * @param {string} name
740 * @param {integer} depth
741 * @param {object} options
742 * @param {string} type
743 * @param {function} callback
744 *
745 */
746
7471Schema2Html.prototype.renderGroupTag = function(id, name, depth, options, type, callback) {
7488 var params = {
749 id: id,
750 name: name,
751 depth: depth,
752 options: options || {}
753 };
754
7558 params.options.depth = depth;
756
7578 this.renderTemplate(type, params, function(err, result) {
7588 callback(err, result);
759 });
760};
761
762/**
763 * @memberOf Schema2Html
764 * @description Appends rendered html to the internal html variable
765 *
766 * @param {string} html
767 * @param {integer} pos
768 */
769
7701Schema2Html.prototype.appendHtml = function(html, pos) {
77147 this.html[pos] = html;
772
773 //this.appendDummyHtml(html, pos);
774};
775
776/**
777 * @memberOf Schema2Html
778 * @description Append a string to the dummy html output
779 */
780
7811Schema2Html.prototype.appendDummyHtml = function(html, pos) {
78247 this.htmlTemplate[pos] = html;
783};
784
785/**
786 * @memberOf Schema2Html
787 * @description return a function that renders string template based on input variables
788 *
789 * @param {string} id
790 * @param {string} name
791 * @param {*} val
792 * @param {string} label
793 * @param {array} src
794 * @param {object} options
795 * @param {boolean} required
796 */
797
7981Schema2Html.prototype.addString = function(id, name, val, label, src, options, required) {
79917 var _that = this,
800 pos = this.pos;
801
80217 this.pos += 1;
803
80417 return function(callback) {
80517 _that.renderString(id, name, val, label, src, options, required, function(err, result) {
80617 _that.appendHtml(result, pos);
807 //callback(err, result);
80817 _that.renderString(id, name, null, label, src, options, required, function(err, resultTwo) {
80917 _that.appendDummyHtml(resultTwo, pos);
81017 callback(err, result);
811 });
812 });
813
814
815 };
816};
817
818/**
819 * @memberOf Schema2Html
820 * @description Render string template
821 *
822 * @param {string} id
823 * @param {string} name
824 * @param {*} val
825 * @param {string} label
826 * @param {array} src
827 * @param {object} options
828 * @param {boolean} required
829 * @param {function} callback
830 */
831
8321Schema2Html.prototype.renderString = function(id, name, val, label, src, options, required, callback) {
83335 var params = {},
834 template = 'textfield';
835
83635 params.id = this.generateId(id);
83735 params.name = name;
83835 params.val = val;
83935 params.src = src;
84035 params.label = label;
84135 params.options = options || {};
84235 params.required = required || false;
84335 params.options.keyName = util.rawName(name);
84435 params.options.keyInner = util.innerName(name, params.options.depth || 0);
84535 params.options.arrayDepth = this.arrayLoopCount;
84635 params.datasrc = params.options.datasrc ? this.dependencyCache[params.options.datasrc] : null;
847
84835 template = params.options.format || template;
849
85035 this.renderTemplate(template, params, function(err, result) {
85135 callback(err, result);
852 });
853};
854
855/**
856 * @memberOf Schema2Html
857 * @description Push a task into what will make up the rendering loop
858 *
859 * @param {function} func
860 */
861
8621Schema2Html.prototype.addRenderTask = function(func) {
86343 this.funcs.push(func);
864};
865
866/**
867 * @memberOf Schema2Html
868 * @description Generate a valid html id for use in the dom
869 *
870 * @param {string} scope
871 */
872
8731Schema2Html.prototype.generateId = function(scope) {
874106 scope = scope || "";
875106 return scope !== null ? scope.toLowerCase().split('../').join('-@-').split('[').join('-').split(']').join('').split('.').join('-').split(' ').join('-').split('-@-').join('../') : null;
876};
877
878/**
879 * @memberOf Schema2Html
880 * @description Genetate a valid form name
881 *
882 * @param {string} preScope
883 * @param {string} newScope
884 *
885 */
886
8871Schema2Html.prototype.generateName = function(preScope, newScope) {
88823 var name;
889
89023 if (preScope !== null) {
89110 name = preScope + '[' + newScope + ']';
892 } else {
89313 name = newScope;
894 }
895
89623 return name;
897};
898
899
9001module.exports = Schema2Html;