Projekt

Obecné

Profil

Stáhnout (7.89 KB) Statistiky
| Větev: | Revize:
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
  equal: require('fast-deep-equal'),
13
  ucs2length: require('./ucs2length'),
14
  varOccurences: varOccurences,
15
  varReplace: varReplace,
16
  cleanUpCode: cleanUpCode,
17
  finalCleanUpCode: finalCleanUpCode,
18
  schemaHasRules: schemaHasRules,
19
  schemaHasRulesExcept: schemaHasRulesExcept,
20
  schemaUnknownRules: schemaUnknownRules,
21
  toQuotedString: toQuotedString,
22
  getPathExpr: getPathExpr,
23
  getPath: getPath,
24
  getData: getData,
25
  unescapeFragment: unescapeFragment,
26
  unescapeJsonPointer: unescapeJsonPointer,
27
  escapeFragment: escapeFragment,
28
  escapeJsonPointer: escapeJsonPointer
29
};
30

    
31

    
32
function copy(o, to) {
33
  to = to || {};
34
  for (var key in o) to[key] = o[key];
35
  return to;
36
}
37

    
38

    
39
function checkDataType(dataType, data, negate) {
40
  var EQUAL = negate ? ' !== ' : ' === '
41
    , AND = negate ? ' || ' : ' && '
42
    , OK = negate ? '!' : ''
43
    , NOT = negate ? '' : '!';
44
  switch (dataType) {
45
    case 'null': return data + EQUAL + 'null';
46
    case 'array': return OK + 'Array.isArray(' + data + ')';
47
    case 'object': return '(' + OK + data + AND +
48
                          'typeof ' + data + EQUAL + '"object"' + AND +
49
                          NOT + 'Array.isArray(' + data + '))';
50
    case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
51
                           NOT + '(' + data + ' % 1)' +
52
                           AND + data + EQUAL + data + ')';
53
    default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
54
  }
55
}
56

    
57

    
58
function checkDataTypes(dataTypes, data) {
59
  switch (dataTypes.length) {
60
    case 1: return checkDataType(dataTypes[0], data, true);
61
    default:
62
      var code = '';
63
      var types = toHash(dataTypes);
64
      if (types.array && types.object) {
65
        code = types.null ? '(': '(!' + data + ' || ';
66
        code += 'typeof ' + data + ' !== "object")';
67
        delete types.null;
68
        delete types.array;
69
        delete types.object;
70
      }
71
      if (types.number) delete types.integer;
72
      for (var t in types)
73
        code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
74

    
75
      return code;
76
  }
77
}
78

    
79

    
80
var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]);
81
function coerceToTypes(optionCoerceTypes, dataTypes) {
82
  if (Array.isArray(dataTypes)) {
83
    var types = [];
84
    for (var i=0; i<dataTypes.length; i++) {
85
      var t = dataTypes[i];
86
      if (COERCE_TO_TYPES[t]) types[types.length] = t;
87
      else if (optionCoerceTypes === 'array' && t === 'array') types[types.length] = t;
88
    }
89
    if (types.length) return types;
90
  } else if (COERCE_TO_TYPES[dataTypes]) {
91
    return [dataTypes];
92
  } else if (optionCoerceTypes === 'array' && dataTypes === 'array') {
93
    return ['array'];
94
  }
95
}
96

    
97

    
98
function toHash(arr) {
99
  var hash = {};
100
  for (var i=0; i<arr.length; i++) hash[arr[i]] = true;
101
  return hash;
102
}
103

    
104

    
105
var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
106
var SINGLE_QUOTE = /'|\\/g;
107
function getProperty(key) {
108
  return typeof key == 'number'
109
          ? '[' + key + ']'
110
          : IDENTIFIER.test(key)
111
            ? '.' + key
112
            : "['" + escapeQuotes(key) + "']";
113
}
114

    
115

    
116
function escapeQuotes(str) {
117
  return str.replace(SINGLE_QUOTE, '\\$&')
118
            .replace(/\n/g, '\\n')
119
            .replace(/\r/g, '\\r')
120
            .replace(/\f/g, '\\f')
121
            .replace(/\t/g, '\\t');
122
}
123

    
124

    
125
function varOccurences(str, dataVar) {
126
  dataVar += '[^0-9]';
127
  var matches = str.match(new RegExp(dataVar, 'g'));
128
  return matches ? matches.length : 0;
129
}
130

    
131

    
132
function varReplace(str, dataVar, expr) {
133
  dataVar += '([^0-9])';
134
  expr = expr.replace(/\$/g, '$$$$');
135
  return str.replace(new RegExp(dataVar, 'g'), expr + '$1');
136
}
137

    
138

    
139
var EMPTY_ELSE = /else\s*{\s*}/g
140
  , EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
141
  , EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
142
function cleanUpCode(out) {
143
  return out.replace(EMPTY_ELSE, '')
144
            .replace(EMPTY_IF_NO_ELSE, '')
145
            .replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
146
}
147

    
148

    
149
var ERRORS_REGEXP = /[^v.]errors/g
150
  , REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
151
  , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
152
  , RETURN_VALID = 'return errors === 0;'
153
  , RETURN_TRUE = 'validate.errors = null; return true;'
154
  , RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
155
  , RETURN_DATA_ASYNC = 'return data;'
156
  , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
157
  , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;
158

    
159
function finalCleanUpCode(out, async) {
160
  var matches = out.match(ERRORS_REGEXP);
161
  if (matches && matches.length == 2) {
162
    out = async
163
          ? out.replace(REMOVE_ERRORS_ASYNC, '')
164
               .replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
165
          : out.replace(REMOVE_ERRORS, '')
166
               .replace(RETURN_VALID, RETURN_TRUE);
167
  }
168

    
169
  matches = out.match(ROOTDATA_REGEXP);
170
  if (!matches || matches.length !== 3) return out;
171
  return out.replace(REMOVE_ROOTDATA, '');
172
}
173

    
174

    
175
function schemaHasRules(schema, rules) {
176
  if (typeof schema == 'boolean') return !schema;
177
  for (var key in schema) if (rules[key]) return true;
178
}
179

    
180

    
181
function schemaHasRulesExcept(schema, rules, exceptKeyword) {
182
  if (typeof schema == 'boolean') return !schema && exceptKeyword != 'not';
183
  for (var key in schema) if (key != exceptKeyword && rules[key]) return true;
184
}
185

    
186

    
187
function schemaUnknownRules(schema, rules) {
188
  if (typeof schema == 'boolean') return;
189
  for (var key in schema) if (!rules[key]) return key;
190
}
191

    
192

    
193
function toQuotedString(str) {
194
  return '\'' + escapeQuotes(str) + '\'';
195
}
196

    
197

    
198
function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
199
  var path = jsonPointers // false by default
200
              ? '\'/\' + ' + expr + (isNumber ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')')
201
              : (isNumber ? '\'[\' + ' + expr + ' + \']\'' : '\'[\\\'\' + ' + expr + ' + \'\\\']\'');
202
  return joinPaths(currentPath, path);
203
}
204

    
205

    
206
function getPath(currentPath, prop, jsonPointers) {
207
  var path = jsonPointers // false by default
208
              ? toQuotedString('/' + escapeJsonPointer(prop))
209
              : toQuotedString(getProperty(prop));
210
  return joinPaths(currentPath, path);
211
}
212

    
213

    
214
var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
215
var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
216
function getData($data, lvl, paths) {
217
  var up, jsonPointer, data, matches;
218
  if ($data === '') return 'rootData';
219
  if ($data[0] == '/') {
220
    if (!JSON_POINTER.test($data)) throw new Error('Invalid JSON-pointer: ' + $data);
221
    jsonPointer = $data;
222
    data = 'rootData';
223
  } else {
224
    matches = $data.match(RELATIVE_JSON_POINTER);
225
    if (!matches) throw new Error('Invalid JSON-pointer: ' + $data);
226
    up = +matches[1];
227
    jsonPointer = matches[2];
228
    if (jsonPointer == '#') {
229
      if (up >= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl);
230
      return paths[lvl - up];
231
    }
232

    
233
    if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl);
234
    data = 'data' + ((lvl - up) || '');
235
    if (!jsonPointer) return data;
236
  }
237

    
238
  var expr = data;
239
  var segments = jsonPointer.split('/');
240
  for (var i=0; i<segments.length; i++) {
241
    var segment = segments[i];
242
    if (segment) {
243
      data += getProperty(unescapeJsonPointer(segment));
244
      expr += ' && ' + data;
245
    }
246
  }
247
  return expr;
248
}
249

    
250

    
251
function joinPaths (a, b) {
252
  if (a == '""') return b;
253
  return (a + ' + ' + b).replace(/' \+ '/g, '');
254
}
255

    
256

    
257
function unescapeFragment(str) {
258
  return unescapeJsonPointer(decodeURIComponent(str));
259
}
260

    
261

    
262
function escapeFragment(str) {
263
  return encodeURIComponent(escapeJsonPointer(str));
264
}
265

    
266

    
267
function escapeJsonPointer(str) {
268
  return str.replace(/~/g, '~0').replace(/\//g, '~1');
269
}
270

    
271

    
272
function unescapeJsonPointer(str) {
273
  return str.replace(/~1/g, '/').replace(/~0/g, '~');
274
}
(10-10/10)