Passed
Pull Request — develop (#92)
by Felipe
06:21
created

.tests/selenium/selenium-lib/core/xpath/util.js   F

Complexity

Total Complexity 123
Complexity/F 2.32

Size

Lines of Code 539
Function Count 53

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 4
dl 0
loc 539
rs 3.12
c 0
b 0
f 0
wmc 123
mnd 6
bc 118
fnc 53
bpm 2.2264
cpm 2.3207
noi 22

52 Functions

Rating   Name   Duplication   Size   Complexity  
A util.js ➔ xsltLog 0 1 1
A Set.set 0 9 2
A util.js ➔ copyArrayIgnoringAttributesWithoutValue 0 11 4
A util.js ➔ domAppendChild 0 3 1
A util.js ➔ windowClearInterval 0 3 1
A Set.add 0 7 2
A util.js ➔ xmlEscapeAttr 0 3 1
A util.js ➔ domRemoveNode 0 3 1
A util.js ➔ domCreateElement 0 3 1
A util.js ➔ xmlOwnerDocument 0 7 2
A util.js ➔ domCreateTextNode 0 3 1
A Set.clear 0 6 2
A Set.inc 0 8 2
A util.js ➔ xmlValueIE6Hack 0 9 4
A util.js ➔ domCreateCDATASection 0 3 1
A util.js ➔ domSetAttribute 0 3 1
B util.js ➔ exprReturnsNumberValue 0 33 5
A Set.remove 0 6 2
A RegExp.escape.constructor 0 14 1
A util.js ➔ xmlEscapeText 0 4 1
B util.js ➔ predicateExprHasPositionalSelector 0 19 6
A util.js ➔ assert 0 5 2
C util.js ➔ xmlTextR 0 41 15
B util.js ➔ xmlImportNode 0 27 6
A util.js ➔ xmlText 0 5 1
A util.js ➔ xmlEscapeTags 0 3 1
A util.js ➔ Set 0 3 1
A Set.size 0 3 1
A util.js ➔ stringSplit 0 18 4
A util.js ➔ domCreateAttribute 0 3 1
C util.js ➔ xmlValue 0 39 13
A Set.map 0 6 2
A Set.get 0 8 2
A util.js ➔ windowSetInterval 0 3 1
B util.js ➔ removeFromArray 0 10 5
A util.js ➔ domReplaceChild 0 3 1
A RegExp.escape 0 3 1
A util.js ➔ mapExec 0 5 2
A util.js ➔ xsltLogXml 0 1 1
A util.js ➔ domGetElementById 0 3 1
A util.js ➔ copyArray 0 7 3
A util.js ➔ domCreateComment 0 3 1
A util.js ➔ reverseInplace 0 8 2
A util.js ➔ domRemoveAttribute 0 3 1
A util.js ➔ domInsertBefore 0 3 1
A Set.contains 0 3 1
A util.js ➔ mapExpr 0 7 2
A Set.items 0 9 2
A util.js ➔ domCreateDocumentFragment 0 3 1
A util.js ➔ domGetAttribute 0 3 1
A util.js ➔ domRemoveChild 0 3 1
A util.js ➔ xmlFullNodeName 0 7 3

How to fix   Complexity   

Complexity

Complex classes like .tests/selenium/selenium-lib/core/xpath/util.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// Copyright 2005 Google
2
//
3
// Author: Steffen Meschkat <[email protected]>
4
//
5
// Miscellaneous utility and placeholder functions.
6
7
// Dummy implmentation for the logging functions. Replace by something
8
// useful when you want to debug.
9
function xpathLog(msg) {};
0 ignored issues
show
Unused Code introduced by
The parameter msg is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
10
function xsltLog(msg) {};
0 ignored issues
show
Unused Code introduced by
The parameter msg is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
11
function xsltLogXml(msg) {};
0 ignored issues
show
Unused Code introduced by
The parameter msg is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
12
13
var ajaxsltIsIE6 = navigator.appVersion.match(/MSIE 6.0/);
14
15
// Throws an exception if false.
16
function assert(b) {
17
  if (!b) {
18
    throw "Assertion failed";
19
  }
20
}
21
22
// Splits a string s at all occurrences of character c. This is like
23
// the split() method of the string object, but IE omits empty
24
// strings, which violates the invariant (s.split(x).join(x) == s).
25
function stringSplit(s, c) {
26
  var a = s.indexOf(c);
27
  if (a == -1) {
28
    return [ s ];
29
  }
30
  var parts = [];
31
  parts.push(s.substr(0,a));
32
  while (a != -1) {
33
    var a1 = s.indexOf(c, a + 1);
34
    if (a1 != -1) {
35
      parts.push(s.substr(a + 1, a1 - a - 1));
36
    } else {
37
      parts.push(s.substr(a + 1));
38
    }
39
    a = a1;
40
  }
41
  return parts;
42
}
43
44
// The following function does what document.importNode(node, true)
45
// would do for us here; however that method is broken in Safari/1.3,
46
// so we have to emulate it.
47
function xmlImportNode(doc, node) {
48
  if (node.nodeType == DOM_TEXT_NODE) {
49
    return domCreateTextNode(doc, node.nodeValue);
50
51
  } else if (node.nodeType == DOM_CDATA_SECTION_NODE) {
52
    return domCreateCDATASection(doc, node.nodeValue);
53
54
  } else if (node.nodeType == DOM_ELEMENT_NODE) {
55
    var newNode = domCreateElement(doc, node.nodeName);
56
    for (var i = 0; i < node.attributes.length; ++i) {
57
      var an = node.attributes[i];
58
      var name = an.nodeName;
59
      var value = an.nodeValue;
60
      domSetAttribute(newNode, name, value);
61
    }
62
63
    for (var c = node.firstChild; c; c = c.nextSibling) {
64
      var cn = arguments.callee(doc, c);
0 ignored issues
show
Compatibility introduced by
Calls to arguments.callee have been deprecated in future version of Javascript and are disallowed in strict mode. Consider removing them.
Loading history...
65
      domAppendChild(newNode, cn);
66
    }
67
68
    return newNode;
69
70
  } else {
71
    return domCreateComment(doc, node.nodeName);
72
  }
73
}
74
75
// A set data structure. It can also be used as a map (i.e. the keys
76
// can have values other than 1), but we don't call it map because it
77
// would be ambiguous in this context. Also, the map is iterable, so
78
// we can use it to replace for-in loops over core javascript Objects.
79
// For-in iteration breaks when Object.prototype is modified, which
80
// some clients of the maps API do.
81
//
82
// NOTE(mesch): The set keys by the string value of its element, NOT
83
// by the typed value. In particular, objects can't be used as keys.
84
//
85
// @constructor
86
function Set() {
87
  this.keys = [];
88
}
89
90
Set.prototype.size = function() {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
91
  return this.keys.length;
92
}
93
94
// Adds the entry to the set, ignoring if it is present.
95
Set.prototype.add = function(key, opt_value) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
96
  var value = opt_value || 1;
97
  if (!this.contains(key)) {
98
    this[':' + key] = value;
99
    this.keys.push(key);
100
  }
101
}
102
103
// Sets the entry in the set, adding if it is not yet present.
104
Set.prototype.set = function(key, opt_value) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
105
  var value = opt_value || 1;
106
  if (!this.contains(key)) {
107
    this[':' + key] = value;
108
    this.keys.push(key);
109
  } else {
110
    this[':' + key] = value;
111
  }
112
}
113
114
// Increments the key's value by 1. This works around the fact that
115
// numbers are always passed by value, never by reference, so that we
116
// can't increment the value returned by get(), or the iterator
117
// argument. Sets the key's value to 1 if it doesn't exist yet.
118
Set.prototype.inc = function(key) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
119
  if (!this.contains(key)) {
120
    this[':' + key] = 1;
121
    this.keys.push(key);
122
  } else {
123
    this[':' + key]++;
124
  }
125
}
126
127
Set.prototype.get = function(key) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
128
  if (this.contains(key)) {
129
    return this[':' + key];
130
  } else {
131
    var undefined;
132
    return undefined;
0 ignored issues
show
Bug introduced by
The variable undefined seems to be never initialized.
Loading history...
133
  }
134
}
135
136
// Removes the entry from the set.
137
Set.prototype.remove = function(key) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
138
  if (this.contains(key)) {
139
    delete this[':' + key];
140
    removeFromArray(this.keys, key, true);
141
  }
142
}
143
144
// Tests if an entry is in the set.
145
Set.prototype.contains = function(entry) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
146
  return typeof this[':' + entry] != 'undefined';
147
}
148
149
// Gets a list of values in the set.
150
Set.prototype.items = function() {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
151
  var list = [];
152
  for (var i = 0; i < this.keys.length; ++i) {
153
    var k = this.keys[i];
154
    var v = this[':' + k];
155
    list.push(v);
156
  }
157
  return list;
158
}
159
160
161
// Invokes function f for every key value pair in the set as a method
162
// of the set.
163
Set.prototype.map = function(f) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
164
  for (var i = 0; i < this.keys.length; ++i) {
165
    var k = this.keys[i];
166
    f.call(this, k, this[':' + k]);
167
  }
168
}
169
170
Set.prototype.clear = function() {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Set. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
171
  for (var i = 0; i < this.keys.length; ++i) {
172
    delete this[':' + this.keys[i]];
173
  }
174
  this.keys.length = 0;
175
}
176
177
178
// Applies the given function to each element of the array, preserving
179
// this, and passing the index.
180
function mapExec(array, func) {
181
  for (var i = 0; i < array.length; ++i) {
182
    func.call(this, array[i], i);
183
  }
184
}
185
186
// Returns an array that contains the return value of the given
187
// function applied to every element of the input array.
188
function mapExpr(array, func) {
189
  var ret = [];
190
  for (var i = 0; i < array.length; ++i) {
191
    ret.push(func(array[i]));
192
  }
193
  return ret;
194
};
195
196
// Reverses the given array in place.
197
function reverseInplace(array) {
198
  for (var i = 0; i < array.length / 2; ++i) {
199
    var h = array[i];
200
    var ii = array.length - i - 1;
201
    array[i] = array[ii];
202
    array[ii] = h;
203
  }
204
}
205
206
// Removes value from array. Returns the number of instances of value
207
// that were removed from array.
208
function removeFromArray(array, value, opt_notype) {
209
  var shift = 0;
210
  for (var i = 0; i < array.length; ++i) {
211
    if (array[i] === value || (opt_notype && array[i] == value)) {
212
      array.splice(i--, 1);
213
      shift++;
214
    }
215
  }
216
  return shift;
217
}
218
219
// Shallow-copies an array to the end of another array
220
// Basically Array.concat, but works with other non-array collections
221
function copyArray(dst, src) {
222
  if (!src) return;
223
  var dstLength = dst.length;
224
  for (var i = src.length - 1; i >= 0; --i) {
225
    dst[i+dstLength] = src[i];
226
  }
227
}
228
229
/**
230
 * This is an optimization for copying attribute lists in IE. IE includes many
231
 * extraneous properties in its DOM attribute lists, which take require
232
 * significant extra processing when evaluating attribute steps. With this
233
 * function, we ignore any such attributes that has an empty string value.
234
 */
235
function copyArrayIgnoringAttributesWithoutValue(dst, src)
236
{
237
  if (!src) return;
238
  for (var i = src.length - 1; i >= 0; --i) {
239
    // this test will pass so long as the attribute has a non-empty string
240
    // value, even if that value is "false", "0", "undefined", etc.
241
    if (src[i].nodeValue) {
242
      dst.push(src[i]);
243
    }
244
  }
245
}
246
247
// Returns the text value of a node; for nodes without children this
248
// is the nodeValue, for nodes with children this is the concatenation
249
// of the value of all children. Browser-specific optimizations are used by
250
// default; they can be disabled by passing "true" in as the second parameter.
251
function xmlValue(node, disallowBrowserSpecificOptimization) {
252
  if (!node) {
253
    return '';
254
  }
255
256
  var ret = '';
257
  if (node.nodeType == DOM_TEXT_NODE ||
258
      node.nodeType == DOM_CDATA_SECTION_NODE) {
259
    ret += node.nodeValue;
260
261
  } else if (node.nodeType == DOM_ATTRIBUTE_NODE) {
262
    if (ajaxsltIsIE6) {
263
      ret += xmlValueIE6Hack(node);
264
    } else {
265
      ret += node.nodeValue;
266
    }
267
  } else if (node.nodeType == DOM_ELEMENT_NODE ||
268
             node.nodeType == DOM_DOCUMENT_NODE ||
269
             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
270
    if (!disallowBrowserSpecificOptimization) {
271
      // IE, Safari, Opera, and friends
272
      var innerText = node.innerText;
273
      if (innerText != undefined) {
274
        return innerText;
275
      }
276
      // Firefox
277
      var textContent = node.textContent;
278
      if (textContent != undefined) {
279
        return textContent;
280
      }
281
    }
282
    // pobrecito!
283
    var len = node.childNodes.length;
284
    for (var i = 0; i < len; ++i) {
285
      ret += arguments.callee(node.childNodes[i]);
0 ignored issues
show
Compatibility introduced by
Calls to arguments.callee have been deprecated in future version of Javascript and are disallowed in strict mode. Consider removing them.
Loading history...
286
    }
287
  }
288
  return ret;
289
}
290
291
function xmlValueIE6Hack(node) {
292
    // Issue 19, IE6 mangles href attribute when it's a javascript: url
293
    var nodeName = node.nodeName;
294
    var nodeValue = node.nodeValue;
295
    if (nodeName.length != 4) return nodeValue;
296
    if (!/^href$/i.test(nodeName)) return nodeValue;
297
    if (!/^javascript:/.test(nodeValue)) return nodeValue;
298
    return unescape(nodeValue);
299
}
300
301
// Returns the representation of a node as XML text.
302
function xmlText(node, opt_cdata) {
303
  var buf = [];
304
  xmlTextR(node, buf, opt_cdata);
305
  return buf.join('');
306
}
307
308
function xmlTextR(node, buf, cdata) {
309
  if (node.nodeType == DOM_TEXT_NODE) {
310
    buf.push(xmlEscapeText(node.nodeValue));
311
312
  } else if (node.nodeType == DOM_CDATA_SECTION_NODE) {
313
    if (cdata) {
314
      buf.push(node.nodeValue);
315
    } else {
316
      buf.push('<![CDATA[' + node.nodeValue + ']]>');
317
    }
318
319
  } else if (node.nodeType == DOM_COMMENT_NODE) {
320
    buf.push('<!--' + node.nodeValue + '-->');
321
322
  } else if (node.nodeType == DOM_ELEMENT_NODE) {
323
    buf.push('<' + xmlFullNodeName(node));
324
    for (var i = 0; i < node.attributes.length; ++i) {
325
      var a = node.attributes[i];
326
      if (a && a.nodeName && a.nodeValue) {
327
        buf.push(' ' + xmlFullNodeName(a) + '="' +
328
                 xmlEscapeAttr(a.nodeValue) + '"');
329
      }
330
    }
331
332
    if (node.childNodes.length == 0) {
333
      buf.push('/>');
334
    } else {
335
      buf.push('>');
336
      for (var i = 0; i < node.childNodes.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 324. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
337
        arguments.callee(node.childNodes[i], buf, cdata);
0 ignored issues
show
Compatibility introduced by
Calls to arguments.callee have been deprecated in future version of Javascript and are disallowed in strict mode. Consider removing them.
Loading history...
338
      }
339
      buf.push('</' + xmlFullNodeName(node) + '>');
340
    }
341
342
  } else if (node.nodeType == DOM_DOCUMENT_NODE ||
343
             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
344
    for (var i = 0; i < node.childNodes.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 324. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
345
      arguments.callee(node.childNodes[i], buf, cdata);
0 ignored issues
show
Compatibility introduced by
Calls to arguments.callee have been deprecated in future version of Javascript and are disallowed in strict mode. Consider removing them.
Loading history...
346
    }
347
  }
348
}
349
350
function xmlFullNodeName(n) {
351
  if (n.prefix && n.nodeName.indexOf(n.prefix + ':') != 0) {
352
    return n.prefix + ':' + n.nodeName;
353
  } else {
354
    return n.nodeName;
355
  }
356
}
357
358
// Escape XML special markup chracters: tag delimiter < > and entity
359
// reference start delimiter &. The escaped string can be used in XML
360
// text portions (i.e. between tags).
361
function xmlEscapeText(s) {
362
  return ('' + s).replace(/&/g, '&amp;').replace(/</g, '&lt;').
363
    replace(/>/g, '&gt;');
364
}
365
366
// Escape XML special markup characters: tag delimiter < > entity
367
// reference start delimiter & and quotes ". The escaped string can be
368
// used in double quoted XML attribute value portions (i.e. in
369
// attributes within start tags).
370
function xmlEscapeAttr(s) {
371
  return xmlEscapeText(s).replace(/\"/g, '&quot;');
372
}
373
374
// Escape markup in XML text, but don't touch entity references. The
375
// escaped string can be used as XML text (i.e. between tags).
376
function xmlEscapeTags(s) {
377
  return s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
378
}
379
380
/**
381
 * Wrapper function to access the owner document uniformly for document
382
 * and other nodes: for the document node, the owner document is the
383
 * node itself, for all others it's the ownerDocument property.
384
 *
385
 * @param {Node} node
386
 * @return {Document}
387
 */
388
function xmlOwnerDocument(node) {
389
  if (node.nodeType == DOM_DOCUMENT_NODE) {
390
    return node;
391
  } else {
392
    return node.ownerDocument;
393
  }
394
}
395
396
// Wrapper around DOM methods so we can condense their invocations.
397
function domGetAttribute(node, name) {
398
  return node.getAttribute(name);
399
}
400
401
function domSetAttribute(node, name, value) {
402
  return node.setAttribute(name, value);
403
}
404
405
function domRemoveAttribute(node, name) {
406
  return node.removeAttribute(name);
407
}
408
409
function domAppendChild(node, child) {
410
  return node.appendChild(child);
411
}
412
413
function domRemoveChild(node, child) {
414
  return node.removeChild(child);
415
}
416
417
function domReplaceChild(node, newChild, oldChild) {
418
  return node.replaceChild(newChild, oldChild);
419
}
420
421
function domInsertBefore(node, newChild, oldChild) {
422
  return node.insertBefore(newChild, oldChild);
423
}
424
425
function domRemoveNode(node) {
426
  return domRemoveChild(node.parentNode, node);
427
}
428
429
function domCreateTextNode(doc, text) {
430
  return doc.createTextNode(text);
431
}
432
433
function domCreateElement(doc, name) {
434
  return doc.createElement(name);
435
}
436
437
function domCreateAttribute(doc, name) {
438
  return doc.createAttribute(name);
439
}
440
441
function domCreateCDATASection(doc, data) {
442
  return doc.createCDATASection(data);
443
}
444
445
function domCreateComment(doc, text) {
446
  return doc.createComment(text);
447
}
448
449
function domCreateDocumentFragment(doc) {
450
  return doc.createDocumentFragment();
451
}
452
453
function domGetElementById(doc, id) {
454
  return doc.getElementById(id);
455
}
456
457
// Same for window methods.
458
function windowSetInterval(win, fun, time) {
459
  return win.setInterval(fun, time);
460
}
461
462
function windowClearInterval(win, id) {
463
  return win.clearInterval(id);
464
}
465
466
/**
467
 * Escape the special regular expression characters when the regular expression
468
 * is specified as a string.
469
 *
470
 * Based on: http://simonwillison.net/2006/Jan/20/escape/
471
 */
472
RegExp.escape = (function() {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type RegExp. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
473
  var specials = [
474
    '/', '.', '*', '+', '?', '|', '^', '$',
475
    '(', ')', '[', ']', '{', '}', '\\'
476
  ];
477
    
478
  var sRE = new RegExp(
479
    '(\\' + specials.join('|\\') + ')', 'g'
480
  );
481
    
482
  return function(text) {
483
    return text.replace(sRE, '\\$1');
484
  }
485
})();
486
487
/**
488
 * Determines whether a predicate expression contains a "positional selector".
489
 * A positional selector filters nodes from the nodelist input based on their
490
 * position within that list. When such selectors are encountered, the
491
 * evaluation of the predicate cannot be depth-first, because the positional
492
 * selector may be based on the result of evaluating predicates that precede
493
 * it.
494
 */
495
function predicateExprHasPositionalSelector(expr, isRecursiveCall) {
496
  if (!expr) {
497
    return false;
498
  }
499
  if (!isRecursiveCall && exprReturnsNumberValue(expr)) {
500
    // this is a "proximity position"-based predicate
501
    return true;
502
  }
503
  if (expr instanceof FunctionCallExpr) {
504
    var value = expr.name.value;
505
    return (value == 'last' || value == 'position');
506
  }
507
  if (expr instanceof BinaryExpr) {
508
    return (
509
      predicateExprHasPositionalSelector(expr.expr1, true) ||
510
      predicateExprHasPositionalSelector(expr.expr2, true));
511
  }
512
  return false;
513
}
514
515
function exprReturnsNumberValue(expr) {
516
  if (expr instanceof FunctionCallExpr) {
517
    var isMember = {
518
      last: true
519
      , position: true
520
      , count: true
521
      , 'string-length': true
522
      , number: true
523
      , sum: true
524
      , floor: true
525
      , ceiling: true
526
      , round: true
527
    };
528
    return isMember[expr.name.value];
529
  }
530
  else if (expr instanceof UnaryMinusExpr) {
531
    return true;
532
  }
533
  else if (expr instanceof BinaryExpr) {
534
    var isMember = {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable isMember already seems to be declared on line 517. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
535
      '+': true
536
      , '-': true
537
      , '*': true
538
      , mod: true
539
      , div: true
540
    };
541
    return isMember[expr.op.value];
542
  }
543
  else if (expr instanceof NumberExpr) {
544
    return true;
545
  }
546
  return false;
547
}
548
549
550