Initial commit
[yaffs-website] / node_modules / ajv / lib / compile / util.js
1 'use strict';
2
3
4 module.exports = {
5   copy: copy,
6   checkDataType: checkDataType,
7   checkDataTypes: checkDataTypes,
8   coerceToTypes: coerceToTypes,
9   toHash: toHash,
10   getProperty: getProperty,
11   escapeQuotes: escapeQuotes,
12   ucs2length: require('./ucs2length'),
13   varOccurences: varOccurences,
14   varReplace: varReplace,
15   cleanUpCode: cleanUpCode,
16   cleanUpVarErrors: cleanUpVarErrors,
17   schemaHasRules: schemaHasRules,
18   schemaHasRulesExcept: schemaHasRulesExcept,
19   stableStringify: require('json-stable-stringify'),
20   toQuotedString: toQuotedString,
21   getPathExpr: getPathExpr,
22   getPath: getPath,
23   getData: getData,
24   unescapeFragment: unescapeFragment,
25   escapeFragment: escapeFragment,
26   escapeJsonPointer: escapeJsonPointer
27 };
28
29
30 function copy(o, to) {
31   to = to || {};
32   for (var key in o) to[key] = o[key];
33   return to;
34 }
35
36
37 function checkDataType(dataType, data, negate) {
38   var EQUAL = negate ? ' !== ' : ' === '
39     , AND = negate ? ' || ' : ' && '
40     , OK = negate ? '!' : ''
41     , NOT = negate ? '' : '!';
42   switch (dataType) {
43     case 'null': return data + EQUAL + 'null';
44     case 'array': return OK + 'Array.isArray(' + data + ')';
45     case 'object': return '(' + OK + data + AND +
46                           'typeof ' + data + EQUAL + '"object"' + AND +
47                           NOT + 'Array.isArray(' + data + '))';
48     case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
49                            NOT + '(' + data + ' % 1)' +
50                            AND + data + EQUAL + data + ')';
51     default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
52   }
53 }
54
55
56 function checkDataTypes(dataTypes, data) {
57   switch (dataTypes.length) {
58     case 1: return checkDataType(dataTypes[0], data, true);
59     default:
60       var code = '';
61       var types = toHash(dataTypes);
62       if (types.array && types.object) {
63         code = types.null ? '(': '(!' + data + ' || ';
64         code += 'typeof ' + data + ' !== "object")';
65         delete types.null;
66         delete types.array;
67         delete types.object;
68       }
69       if (types.number) delete types.integer;
70       for (var t in types)
71         code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
72
73       return code;
74   }
75 }
76
77
78 var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]);
79 function coerceToTypes(optionCoerceTypes, dataTypes) {
80   if (Array.isArray(dataTypes)) {
81     var types = [];
82     for (var i=0; i<dataTypes.length; i++) {
83       var t = dataTypes[i];
84       if (COERCE_TO_TYPES[t]) types[types.length] = t;
85       else if (optionCoerceTypes === 'array' && t === 'array') types[types.length] = t;
86     }
87     if (types.length) return types;
88   } else if (COERCE_TO_TYPES[dataTypes]) {
89     return [dataTypes];
90   } else if (optionCoerceTypes === 'array' && dataTypes === 'array') {
91     return ['array'];
92   }
93 }
94
95
96 function toHash(arr) {
97   var hash = {};
98   for (var i=0; i<arr.length; i++) hash[arr[i]] = true;
99   return hash;
100 }
101
102
103 var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
104 var SINGLE_QUOTE = /'|\\/g;
105 function getProperty(key) {
106   return typeof key == 'number'
107           ? '[' + key + ']'
108           : IDENTIFIER.test(key)
109             ? '.' + key
110             : "['" + escapeQuotes(key) + "']";
111 }
112
113
114 function escapeQuotes(str) {
115   return str.replace(SINGLE_QUOTE, '\\$&')
116             .replace(/\n/g, '\\n')
117             .replace(/\r/g, '\\r')
118             .replace(/\f/g, '\\f')
119             .replace(/\t/g, '\\t');
120 }
121
122
123 function varOccurences(str, dataVar) {
124   dataVar += '[^0-9]';
125   var matches = str.match(new RegExp(dataVar, 'g'));
126   return matches ? matches.length : 0;
127 }
128
129
130 function varReplace(str, dataVar, expr) {
131   dataVar += '([^0-9])';
132   expr = expr.replace(/\$/g, '$$$$');
133   return str.replace(new RegExp(dataVar, 'g'), expr + '$1');
134 }
135
136
137 var EMPTY_ELSE = /else\s*{\s*}/g
138   , EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
139   , EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
140 function cleanUpCode(out) {
141   return out.replace(EMPTY_ELSE, '')
142             .replace(EMPTY_IF_NO_ELSE, '')
143             .replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
144 }
145
146
147 var ERRORS_REGEXP = /[^v\.]errors/g
148   , REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
149   , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
150   , RETURN_VALID = 'return errors === 0;'
151   , RETURN_TRUE = 'validate.errors = null; return true;'
152   , RETURN_ASYNC = /if \(errors === 0\) return true;\s*else throw new ValidationError\(vErrors\);/
153   , RETURN_TRUE_ASYNC = 'return true;';
154
155 function cleanUpVarErrors(out, async) {
156   var matches = out.match(ERRORS_REGEXP);
157   if (!matches || matches.length !== 2) return out;
158   return async
159           ? out.replace(REMOVE_ERRORS_ASYNC, '')
160                .replace(RETURN_ASYNC, RETURN_TRUE_ASYNC)
161           : out.replace(REMOVE_ERRORS, '')
162                .replace(RETURN_VALID, RETURN_TRUE);
163 }
164
165
166 function schemaHasRules(schema, rules) {
167   for (var key in schema) if (rules[key]) return true;
168 }
169
170
171 function schemaHasRulesExcept(schema, rules, exceptKeyword) {
172   for (var key in schema) if (key != exceptKeyword && rules[key]) return true;
173 }
174
175
176 function toQuotedString(str) {
177   return '\'' + escapeQuotes(str) + '\'';
178 }
179
180
181 function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
182   var path = jsonPointers // false by default
183               ? '\'/\' + ' + expr + (isNumber ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')')
184               : (isNumber ? '\'[\' + ' + expr + ' + \']\'' : '\'[\\\'\' + ' + expr + ' + \'\\\']\'');
185   return joinPaths(currentPath, path);
186 }
187
188
189 function getPath(currentPath, prop, jsonPointers) {
190   var path = jsonPointers // false by default
191               ? toQuotedString('/' + escapeJsonPointer(prop))
192               : toQuotedString(getProperty(prop));
193   return joinPaths(currentPath, path);
194 }
195
196
197 var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
198 var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
199 function getData($data, lvl, paths) {
200   var up, jsonPointer, data, matches;
201   if ($data === '') return 'rootData';
202   if ($data[0] == '/') {
203     if (!JSON_POINTER.test($data)) throw new Error('Invalid JSON-pointer: ' + $data);
204     jsonPointer = $data;
205     data = 'rootData';
206   } else {
207     matches = $data.match(RELATIVE_JSON_POINTER);
208     if (!matches) throw new Error('Invalid JSON-pointer: ' + $data);
209     up = +matches[1];
210     jsonPointer = matches[2];
211     if (jsonPointer == '#') {
212       if (up >= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl);
213       return paths[lvl - up];
214     }
215
216     if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl);
217     data = 'data' + ((lvl - up) || '');
218     if (!jsonPointer) return data;
219   }
220
221   var expr = data;
222   var segments = jsonPointer.split('/');
223   for (var i=0; i<segments.length; i++) {
224     var segment = segments[i];
225     if (segment) {
226       data += getProperty(unescapeJsonPointer(segment));
227       expr += ' && ' + data;
228     }
229   }
230   return expr;
231 }
232
233
234 function joinPaths (a, b) {
235   if (a == '""') return b;
236   return (a + ' + ' + b).replace(/' \+ '/g, '');
237 }
238
239
240 function unescapeFragment(str) {
241   return unescapeJsonPointer(decodeURIComponent(str));
242 }
243
244
245 function escapeFragment(str) {
246   return encodeURIComponent(escapeJsonPointer(str));
247 }
248
249
250 function escapeJsonPointer(str) {
251   return str.replace(/~/g, '~0').replace(/\//g, '~1');
252 }
253
254
255 function unescapeJsonPointer(str) {
256   return str.replace(/~1/g, '/').replace(/~0/g, '~');
257 }