2020-05-19 11:43:42 +03:00

684 lines
22 KiB
JavaScript

'use strict';
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _objectpath = require('objectpath');
var _objectpath2 = _interopRequireDefault(_objectpath);
var _tv = require('tv4');
var _tv2 = _interopRequireDefault(_tv);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function stripNullType(type) {
if (Array.isArray(type) && type.length == 2) {
if (type[0] === 'null') return type[1];
if (type[1] === 'null') return type[0];
}
return type;
}
//Creates an default titleMap list from an enum, i.e. a list of strings.
var enumToTitleMap = function enumToTitleMap(enm) {
var titleMap = []; //canonical titleMap format is a list.
enm.forEach(function (name) {
titleMap.push({ name: name, value: name });
});
return titleMap;
};
// Takes a titleMap in either object or list format and returns one in
// in the list format.
var canonicalTitleMap = function canonicalTitleMap(titleMap, originalEnum) {
if (!_lodash2.default.isArray(titleMap)) {
var canonical = [];
if (originalEnum) {
originalEnum.forEach(function (value) {
canonical.push({ name: titleMap[value], value: value });
});
} else {
for (var k in titleMap) {
if (titleMap.hasOwnProperty(k)) {
canonical.push({ name: k, value: titleMap[k] });
}
}
}
return canonical;
}
return titleMap;
};
//Creates a form object with all common properties
var stdFormObj = function stdFormObj(name, schema, options) {
options = options || {};
var f = options.global && options.global.formDefaults ? _lodash2.default.cloneDeep(options.global.formDefaults) : {};
if (options.global && options.global.supressPropertyTitles === true) {
f.title = schema.title;
} else {
f.title = schema.title || name;
}
if (schema.description) {
f.description = schema.description;
}
if (options.required === true || schema.required === true) {
f.required = true;
}
if (schema.maxLength) {
f.maxlength = schema.maxLength;
}
if (schema.minLength) {
f.minlength = schema.minLength;
}
if (schema.readOnly || schema.readonly) {
f.readonly = true;
}
if (schema.minimum) {
f.minimum = schema.minimum + (schema.exclusiveMinimum ? 1 : 0);
}
if (schema.maximum) {
f.maximum = schema.maximum - (schema.exclusiveMaximum ? 1 : 0);
}
// Non standard attributes (DONT USE DEPRECATED)
// If you must set stuff like this in the schema use the x-schema-form attribute
if (schema.validationMessage) {
f.validationMessage = schema.validationMessage;
}
if (schema.enumNames) {
f.titleMap = canonicalTitleMap(schema.enumNames, schema['enum']);
}
f.schema = schema;
// Ng model options doesn't play nice with undefined, might be defined
// globally though
f.ngModelOptions = f.ngModelOptions || {};
return f;
};
var text = function text(name, schema, options) {
if (stripNullType(schema.type) === 'string' && !schema['enum']) {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'text';
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
//default in json form for number and integer is a text field
//input type="number" would be more suitable don't ya think?
var number = function number(name, schema, options) {
if (stripNullType(schema.type) === 'number') {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'number';
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var integer = function integer(name, schema, options) {
if (stripNullType(schema.type) === 'integer') {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'number';
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var date = function date(name, schema, options) {
if (stripNullType(schema.type) === 'date') {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'date';
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var checkbox = function checkbox(name, schema, options) {
if (stripNullType(schema.type) === 'boolean') {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'checkbox';
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var select = function select(name, schema, options) {
if (stripNullType(schema.type) === 'string' && schema['enum']) {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'select';
if (!f.titleMap) {
f.titleMap = enumToTitleMap(schema['enum']);
}
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var checkboxes = function checkboxes(name, schema, options) {
if (stripNullType(schema.type) === 'array' && schema.items && schema.items['enum']) {
var f = stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'checkboxes';
if (!f.titleMap) {
f.titleMap = enumToTitleMap(schema.items['enum']);
}
options.lookup[_objectpath2.default.stringify(options.path)] = f;
return f;
}
};
var fieldset = function fieldset(name, schema, options) {
if (stripNullType(schema.type) === 'object') {
var f = stdFormObj(name, schema, options);
f.type = 'fieldset';
f.items = [];
options.lookup[_objectpath2.default.stringify(options.path)] = f;
//recurse down into properties
for (var k in schema.properties) {
if (schema.properties.hasOwnProperty(k)) {
var path = options.path.slice();
path.push(k);
if (options.ignore[_objectpath2.default.stringify(path)] !== true) {
var required = schema.required && schema.required.indexOf(k) !== -1;
var def = defaultFormDefinition(k, schema.properties[k], {
path: path,
required: required || false,
lookup: options.lookup,
ignore: options.ignore,
global: options.global
});
if (def) {
f.items.push(def);
}
}
}
}
return f;
}
};
var array = function array(name, schema, options) {
if (stripNullType(schema.type) === 'array') {
var f = stdFormObj(name, schema, options);
f.type = 'array';
f.key = options.path;
options.lookup[_objectpath2.default.stringify(options.path)] = f;
// don't do anything if items is not defined.
if (typeof schema.items !== 'undefined') {
var required = schema.required && schema.required.indexOf(options.path[options.path.length - 1]) !== -1;
// The default is to always just create one child. This works since if the
// schemas items declaration is of type: "object" then we get a fieldset.
// We also follow json form notatation, adding empty brackets "[]" to
// signify arrays.
var arrPath = options.path.slice();
arrPath.push('');
var def = defaultFormDefinition(name, schema.items, {
path: arrPath,
required: required || false,
lookup: options.lookup,
ignore: options.ignore,
global: options.global
});
if (def) {
f.items = [def];
} else {
// This is the case that item only contains key value pair for rc-select multipel
f.items = schema.items;
}
}
return f;
}
};
var defaults = {
string: [select, text],
object: [fieldset],
number: [number],
integer: [integer],
boolean: [checkbox],
array: [checkboxes, array],
date: [date]
};
function defaultFormDefinition(name, schema, options) {
//console.log("defaultFormDefinition name, schema", name, schema);
var rules = defaults[stripNullType(schema.type)];
//console.log('defaultFormDefinition:defaults = ', defaults);
//console.log('defaultFormDefinition:rules = ', rules);
if (rules) {
var def;
for (var i = 0; i < rules.length; i++) {
def = rules[i](name, schema, options);
//first handler in list that actually returns something is our handler!
if (def) {
// Do we have form defaults in the schema under the x-schema-form-attribute?
if (def.schema['x-schema-form'] && _lodash2.default.isObject(def.schema['x-schema-form'])) {
def = _lodash2.default.extend(def, def.schema['x-schema-form']);
}
return def;
}
}
}
}
function getDefaults(schema, ignore, globalOptions) {
var form = [];
var lookup = {}; //Map path => form obj for fast lookup in merging
ignore = ignore || {};
globalOptions = globalOptions || {};
//console.log('getDefaults:schema.type = ', schema.type);
if (stripNullType(schema.type) === 'object') {
//console.log('getDefaults:schema.properties = ', schema.properties);
for (var k in schema.properties) {
if (schema.properties.hasOwnProperty(k)) {
if (ignore[k] !== true) {
var required = schema.required && schema.required.indexOf(k) !== -1;
//console.log('getDefaults:required = ', required);
//console.log('getDefaults: k = ', k);
//console.log('getDefaults: v = ', schema.properties[k]);
var def = defaultFormDefinition(k, schema.properties[k], {
path: [k], // Path to this property in bracket notation.
lookup: lookup, // Extra map to register with. Optimization for merger.
ignore: ignore, // The ignore list of paths (sans root level name)
required: required, // Is it required? (v4 json schema style)
global: globalOptions // Global options, including form defaults
});
//console.log('getDefaults:def = ', def);
if (def) {
form.push(def);
}
}
}
}
} else {
throw new Error('Not implemented. Only type "object" allowed at root level of schema.');
}
return { form: form, lookup: lookup };
}
var postProcessFn = function postProcessFn(form) {
return form;
};
/**
* Append default form rule
* @param {string} type json schema type
* @param {Function} rule a function(propertyName,propertySchema,options) that returns a form
* definition or undefined
*/
function appendRule(type, rule) {
if (!defaults[type]) {
defaults[type] = [];
}
defaults[type].push(rule);
}
/**
* Prepend default form rule
* @param {string} type json schema type
* @param {Function} rule a function(propertyName,propertySchema,options) that returns a form
* definition or undefined
*/
function prependRule(type, rule) {
if (!defaults[type]) {
defaults[type] = [];
}
defaults[type].unshift(rule);
}
//Utility functions
/**
* Traverse a schema, applying a function(schema,path) on every sub schema
* i.e. every property of an object.
*/
function traverseSchema(schema, fn, path, ignoreArrays) {
ignoreArrays = typeof ignoreArrays !== 'undefined' ? ignoreArrays : true;
path = path || [];
var traverse = function traverse(schema, fn, path) {
fn(schema, path);
for (var k in schema.properties) {
if (schema.properties.hasOwnProperty(k)) {
var currentPath = path.slice();
currentPath.push(k);
traverse(schema.properties[k], fn, currentPath);
}
}
//Only support type "array" which have a schema as "items".
if (!ignoreArrays && schema.items) {
var arrPath = path.slice();arrPath.push('');
traverse(schema.items, fn, arrPath);
}
};
traverse(schema, fn, path || []);
}
function traverseForm(form, fn) {
fn(form);
if (form.items) {
form.items.forEach(function (f) {
traverseForm(f, fn);
});
}
if (form.tabs) {
form.tabs.forEach(function (tab) {
tab.items.forEach(function (f) {
traverseForm(f, fn);
});
});
}
}
function merge(schema, form, ignore, options, readonly) {
//console.log('merge schema', schema);
//console.log('merge form', form);
form = form || ['*'];
options = options || {};
// Get readonly from root object
readonly = readonly || schema.readonly || schema.readOnly;
var stdForm = getDefaults(schema, ignore, options);
//console.log('merge stdForm', stdForm);
//simple case, we have a "*", just put the stdForm there
var idx = form.indexOf('*');
if (idx !== -1) {
form = form.slice(0, idx).concat(stdForm.form).concat(form.slice(idx + 1));
}
//ok let's merge!
//We look at the supplied form and extend it with schema standards
var lookup = stdForm.lookup;
//console.log('form', form);
return postProcessFn(form.map(function (obj) {
//handle the shortcut with just a name
if (typeof obj === 'string') {
obj = { key: obj };
}
//console.log('obj', obj);
if (obj.key) {
if (typeof obj.key === 'string') {
obj.key = _objectpath2.default.parse(obj.key);
}
}
//If it has a titleMap make sure it's a list
if (obj.titleMap) {
obj.titleMap = canonicalTitleMap(obj.titleMap);
}
//
if (obj.itemForm) {
obj.items = [];
var str = _objectpath2.default.stringify(obj.key);
var stdForm = lookup[str];
stdForm.items.forEach(function (item) {
var o = _lodash2.default.cloneDeep(obj.itemForm);
o.key = item.key;
obj.items.push(o);
});
}
//extend with std form from schema.
if (obj.key) {
var strid = _objectpath2.default.stringify(obj.key);
if (lookup[strid]) {
var schemaDefaults = lookup[strid];
for (var k in schemaDefaults) {
if (schemaDefaults.hasOwnProperty(k)) {
if (obj[k] === undefined) {
obj[k] = schemaDefaults[k];
}
}
}
}
}
// Are we inheriting readonly?
if (readonly === true) {
// Inheriting false is not cool.
obj.readonly = true;
}
//if it's a type with items, merge 'em!
if (obj.items && obj.items.length > 0) {
//console.log('items is not empty schema', schema);
//console.log('items is not empty obj.items', obj.items);
obj.items = merge(schema, obj.items, ignore, options, obj.readonly);
}
//if its has tabs, merge them also!
if (obj.tabs) {
obj.tabs.forEach(function (tab) {
tab.items = merge(schema, tab.items, ignore, options, obj.readonly);
});
}
// Special case: checkbox
// Since have to ternary state we need a default
if (obj.type === 'checkbox' && _lodash2.default.isUndefined(obj.schema['default'])) {
obj.schema['default'] = false;
}
return obj;
}));
}
function selectOrSet(projection, obj, valueToSet) {
//console.log('selectOrSet', projection, obj, valueToSet);
var numRe = /^\d+$/;
if (!obj) {
obj = this;
}
//Support [] array syntax
var parts = typeof projection === 'string' ? _objectpath2.default.parse(projection) : projection;
if (typeof valueToSet !== 'undefined' && parts.length === 1) {
//special case, just setting one variable
obj[parts[0]] = valueToSet;
return obj;
}
if (typeof valueToSet !== 'undefined' && typeof obj[parts[0]] === 'undefined') {
// We need to look ahead to check if array is appropriate
obj[parts[0]] = parts.length > 2 && numRe.test(parts[1]) ? [] : {};
}
var value = obj[parts[0]];
for (var i = 1; i < parts.length; i++) {
// Special case: We allow JSON Form syntax for arrays using empty brackets
// These will of course not work here so we exit if they are found.
if (parts[i] === '') {
return undefined;
}
if (typeof valueToSet !== 'undefined') {
if (i === parts.length - 1) {
//last step. Let's set the value
value[parts[i]] = valueToSet;
return valueToSet;
} else {
// Make sure to create new objects on the way if they are not there.
// We need to look ahead to check if array is appropriate
var tmp = value[parts[i]];
if (typeof tmp === 'undefined' || tmp === null) {
tmp = numRe.test(parts[i + 1]) ? [] : {};
value[parts[i]] = tmp;
}
value = tmp;
}
} else if (value) {
//Just get nex value.
value = value[parts[i]];
}
}
return value;
}
function validateBySchema(schema, value) {
return _tv2.default.validateResult(value, schema);
}
function validate(form, value) {
//console.log('utils validate form ', form);
if (!form) {
return { valid: true };
}
var schema = form.schema;
if (!schema) {
return { valid: true };
}
//console.log('utils validate schema = ', schema);
// Input of type text and textareas will give us a viewValue of ''
// when empty, this is a valid value in a schema and does not count as something
// that breaks validation of 'required'. But for our own sanity an empty field should
// not validate if it's required.
if (value === '') {
value = undefined;
}
// Numbers fields will give a null value, which also means empty field
if (form.type === 'number' && value === null) {
//console.log('utils validate form.type is number');
value = undefined;
}
if (form.type === 'number' && isNaN(parseFloat(value))) {
value = undefined;
}
// Version 4 of JSON Schema has the required property not on the
// property itself but on the wrapping object. Since we like to test
// only this property we wrap it in a fake object.
var wrap = { type: 'object', 'properties': {} };
var propName = form.key[form.key.length - 1];
wrap.properties[propName] = schema;
if (form.required) {
wrap.required = [propName];
}
var valueWrap = {};
if (typeof value !== 'undefined') {
valueWrap[propName] = value;
}
//console.log('utils validate value = ', typeof value);
//console.log('utils validate valueWrap = ', valueWrap);
//console.log('utils validate wrap = ', wrap);
var tv4Result = _tv2.default.validateResult(valueWrap, wrap);
if (tv4Result != null && !tv4Result.valid && form.validationMessage != null && typeof value !== 'undefined') {
tv4Result.error.message = form.validationMessage;
}
return tv4Result;
}
module.exports = {
traverseForm: traverseForm,
traverseSchema: traverseSchema,
prependRule: prependRule,
appendRule: appendRule,
postProcessFn: postProcessFn,
getDefaults: getDefaults,
defaultFormDefinition: defaultFormDefinition,
defaults: defaults,
array: array,
fieldset: fieldset,
checkboxes: checkboxes,
select: select,
checkbox: checkbox,
integer: integer,
number: number,
text: text,
stdFormObj: stdFormObj,
canonicalTitleMap: canonicalTitleMap,
enumToTitleMap: enumToTitleMap,
stripNullType: stripNullType,
merge: merge,
validate: validate,
validateBySchema: validateBySchema,
selectOrSet: selectOrSet
};
;
var _temp = function () {
if (typeof __REACT_HOT_LOADER__ === 'undefined') {
return;
}
__REACT_HOT_LOADER__.register(stripNullType, 'stripNullType', 'src/utils.js');
__REACT_HOT_LOADER__.register(enumToTitleMap, 'enumToTitleMap', 'src/utils.js');
__REACT_HOT_LOADER__.register(canonicalTitleMap, 'canonicalTitleMap', 'src/utils.js');
__REACT_HOT_LOADER__.register(stdFormObj, 'stdFormObj', 'src/utils.js');
__REACT_HOT_LOADER__.register(text, 'text', 'src/utils.js');
__REACT_HOT_LOADER__.register(number, 'number', 'src/utils.js');
__REACT_HOT_LOADER__.register(integer, 'integer', 'src/utils.js');
__REACT_HOT_LOADER__.register(date, 'date', 'src/utils.js');
__REACT_HOT_LOADER__.register(checkbox, 'checkbox', 'src/utils.js');
__REACT_HOT_LOADER__.register(select, 'select', 'src/utils.js');
__REACT_HOT_LOADER__.register(checkboxes, 'checkboxes', 'src/utils.js');
__REACT_HOT_LOADER__.register(fieldset, 'fieldset', 'src/utils.js');
__REACT_HOT_LOADER__.register(array, 'array', 'src/utils.js');
__REACT_HOT_LOADER__.register(defaults, 'defaults', 'src/utils.js');
__REACT_HOT_LOADER__.register(defaultFormDefinition, 'defaultFormDefinition', 'src/utils.js');
__REACT_HOT_LOADER__.register(getDefaults, 'getDefaults', 'src/utils.js');
__REACT_HOT_LOADER__.register(postProcessFn, 'postProcessFn', 'src/utils.js');
__REACT_HOT_LOADER__.register(appendRule, 'appendRule', 'src/utils.js');
__REACT_HOT_LOADER__.register(prependRule, 'prependRule', 'src/utils.js');
__REACT_HOT_LOADER__.register(traverseSchema, 'traverseSchema', 'src/utils.js');
__REACT_HOT_LOADER__.register(traverseForm, 'traverseForm', 'src/utils.js');
__REACT_HOT_LOADER__.register(merge, 'merge', 'src/utils.js');
__REACT_HOT_LOADER__.register(selectOrSet, 'selectOrSet', 'src/utils.js');
__REACT_HOT_LOADER__.register(validateBySchema, 'validateBySchema', 'src/utils.js');
__REACT_HOT_LOADER__.register(validate, 'validate', 'src/utils.js');
}();
;