You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			240 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			240 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | '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, | ||
|  |   schemaHasRules: schemaHasRules, | ||
|  |   schemaHasRulesExcept: schemaHasRulesExcept, | ||
|  |   schemaUnknownRules: schemaUnknownRules, | ||
|  |   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, strictNumbers, 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 + | ||
|  |                            (strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')'; | ||
|  |     case 'number': return '(typeof ' + data + EQUAL + '"' + dataType + '"' + | ||
|  |                           (strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')'; | ||
|  |     default: return 'typeof ' + data + EQUAL + '"' + dataType + '"'; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function checkDataTypes(dataTypes, data, strictNumbers) { | ||
|  |   switch (dataTypes.length) { | ||
|  |     case 1: return checkDataType(dataTypes[0], data, strictNumbers, 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, strictNumbers, 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'); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 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 schemaUnknownRules(schema, rules) { | ||
|  |   if (typeof schema == 'boolean') return; | ||
|  |   for (var key in schema) if (!rules[key]) return key; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 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, '$1'); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 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, '~'); | ||
|  | } |