123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- 'use strict';
- module.exports = {
- copy: copy,
- checkDataType: checkDataType,
- checkDataTypes: checkDataTypes,
- coerceToTypes: coerceToTypes,
- toHash: toHash,
- getProperty: getProperty,
- escapeQuotes: escapeQuotes,
- equal: require('fast-deep-equal'),
- ucs2length: require('./ucs2length'),
- varOccurences: varOccurences,
- varReplace: varReplace,
- cleanUpCode: cleanUpCode,
- finalCleanUpCode: finalCleanUpCode,
- schemaHasRules: schemaHasRules,
- schemaHasRulesExcept: schemaHasRulesExcept,
- toQuotedString: toQuotedString,
- getPathExpr: getPathExpr,
- getPath: getPath,
- getData: getData,
- unescapeFragment: unescapeFragment,
- unescapeJsonPointer: unescapeJsonPointer,
- escapeFragment: escapeFragment,
- escapeJsonPointer: escapeJsonPointer
- };
- function copy(o, to) {
- to = to || {};
- for (var key in o) to[key] = o[key];
- return to;
- }
- function checkDataType(dataType, data, negate) {
- var EQUAL = negate ? ' !== ' : ' === '
- , AND = negate ? ' || ' : ' && '
- , OK = negate ? '!' : ''
- , NOT = negate ? '' : '!';
- switch (dataType) {
- case 'null': return data + EQUAL + 'null';
- case 'array': return OK + 'Array.isArray(' + data + ')';
- case 'object': return '(' + OK + data + AND +
- 'typeof ' + data + EQUAL + '"object"' + AND +
- NOT + 'Array.isArray(' + data + '))';
- case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
- NOT + '(' + data + ' % 1)' +
- AND + data + EQUAL + data + ')';
- default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
- }
- }
- function checkDataTypes(dataTypes, data) {
- switch (dataTypes.length) {
- case 1: return checkDataType(dataTypes[0], data, true);
- default:
- var code = '';
- var types = toHash(dataTypes);
- if (types.array && types.object) {
- code = types.null ? '(': '(!' + data + ' || ';
- code += 'typeof ' + data + ' !== "object")';
- delete types.null;
- delete types.array;
- delete types.object;
- }
- if (types.number) delete types.integer;
- for (var t in types)
- code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
- return code;
- }
- }
- var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]);
- function coerceToTypes(optionCoerceTypes, dataTypes) {
- if (Array.isArray(dataTypes)) {
- var types = [];
- for (var i=0; i<dataTypes.length; i++) {
- var t = dataTypes[i];
- if (COERCE_TO_TYPES[t]) types[types.length] = t;
- else if (optionCoerceTypes === 'array' && t === 'array') types[types.length] = t;
- }
- if (types.length) return types;
- } else if (COERCE_TO_TYPES[dataTypes]) {
- return [dataTypes];
- } else if (optionCoerceTypes === 'array' && dataTypes === 'array') {
- return ['array'];
- }
- }
- function toHash(arr) {
- var hash = {};
- for (var i=0; i<arr.length; i++) hash[arr[i]] = true;
- return hash;
- }
- var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
- var SINGLE_QUOTE = /'|\\/g;
- function getProperty(key) {
- return typeof key == 'number'
- ? '[' + key + ']'
- : IDENTIFIER.test(key)
- ? '.' + key
- : "['" + escapeQuotes(key) + "']";
- }
- function escapeQuotes(str) {
- return str.replace(SINGLE_QUOTE, '\\$&')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\f/g, '\\f')
- .replace(/\t/g, '\\t');
- }
- function varOccurences(str, dataVar) {
- dataVar += '[^0-9]';
- var matches = str.match(new RegExp(dataVar, 'g'));
- return matches ? matches.length : 0;
- }
- function varReplace(str, dataVar, expr) {
- dataVar += '([^0-9])';
- expr = expr.replace(/\$/g, '$$$$');
- return str.replace(new RegExp(dataVar, 'g'), expr + '$1');
- }
- var EMPTY_ELSE = /else\s*{\s*}/g
- , EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
- , EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
- function cleanUpCode(out) {
- return out.replace(EMPTY_ELSE, '')
- .replace(EMPTY_IF_NO_ELSE, '')
- .replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
- }
- var ERRORS_REGEXP = /[^v.]errors/g
- , REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
- , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
- , RETURN_VALID = 'return errors === 0;'
- , RETURN_TRUE = 'validate.errors = null; return true;'
- , RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
- , RETURN_DATA_ASYNC = 'return data;'
- , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
- , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;
- function finalCleanUpCode(out, async) {
- var matches = out.match(ERRORS_REGEXP);
- if (matches && matches.length == 2) {
- out = async
- ? out.replace(REMOVE_ERRORS_ASYNC, '')
- .replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
- : out.replace(REMOVE_ERRORS, '')
- .replace(RETURN_VALID, RETURN_TRUE);
- }
- matches = out.match(ROOTDATA_REGEXP);
- if (!matches || matches.length !== 3) return out;
- return out.replace(REMOVE_ROOTDATA, '');
- }
- function schemaHasRules(schema, rules) {
- if (typeof schema == 'boolean') return !schema;
- for (var key in schema) if (rules[key]) return true;
- }
- function schemaHasRulesExcept(schema, rules, exceptKeyword) {
- if (typeof schema == 'boolean') return !schema && exceptKeyword != 'not';
- for (var key in schema) if (key != exceptKeyword && rules[key]) return true;
- }
- function toQuotedString(str) {
- return '\'' + escapeQuotes(str) + '\'';
- }
- function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
- var path = jsonPointers // false by default
- ? '\'/\' + ' + expr + (isNumber ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')')
- : (isNumber ? '\'[\' + ' + expr + ' + \']\'' : '\'[\\\'\' + ' + expr + ' + \'\\\']\'');
- return joinPaths(currentPath, path);
- }
- function getPath(currentPath, prop, jsonPointers) {
- var path = jsonPointers // false by default
- ? toQuotedString('/' + escapeJsonPointer(prop))
- : toQuotedString(getProperty(prop));
- return joinPaths(currentPath, path);
- }
- var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
- var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
- function getData($data, lvl, paths) {
- var up, jsonPointer, data, matches;
- if ($data === '') return 'rootData';
- if ($data[0] == '/') {
- if (!JSON_POINTER.test($data)) throw new Error('Invalid JSON-pointer: ' + $data);
- jsonPointer = $data;
- data = 'rootData';
- } else {
- matches = $data.match(RELATIVE_JSON_POINTER);
- if (!matches) throw new Error('Invalid JSON-pointer: ' + $data);
- up = +matches[1];
- jsonPointer = matches[2];
- if (jsonPointer == '#') {
- if (up >= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl);
- return paths[lvl - up];
- }
- if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl);
- data = 'data' + ((lvl - up) || '');
- if (!jsonPointer) return data;
- }
- var expr = data;
- var segments = jsonPointer.split('/');
- for (var i=0; i<segments.length; i++) {
- var segment = segments[i];
- if (segment) {
- data += getProperty(unescapeJsonPointer(segment));
- expr += ' && ' + data;
- }
- }
- return expr;
- }
- function joinPaths (a, b) {
- if (a == '""') return b;
- return (a + ' + ' + b).replace(/' \+ '/g, '');
- }
- function unescapeFragment(str) {
- return unescapeJsonPointer(decodeURIComponent(str));
- }
- function escapeFragment(str) {
- return encodeURIComponent(escapeJsonPointer(str));
- }
- function escapeJsonPointer(str) {
- return str.replace(/~/g, '~0').replace(/\//g, '~1');
- }
- function unescapeJsonPointer(str) {
- return str.replace(/~1/g, '/').replace(/~0/g, '~');
- }
|