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

tests/selenium/selenium-lib/core/xpath/javascript-xpath-0.1.11.js   F

Complexity

Total Complexity 664
Complexity/F 3.86

Size

Lines of Code 2803
Function Count 172

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 0
dl 0
loc 2803
rs 2.4
c 0
b 0
f 0
wmc 664
mnd 10
bc 457
fnc 172
bpm 2.6568
cpm 3.8604
noi 72

148 Functions

Rating   Name   Duplication   Size   Complexity  
A Literal.parse 0 7 2
A UnaryExpr.show 0 8 1
A FunctionCall.funcs.last 0 6 2
B UnionExpr.parse 0 23 5
A BinaryExpr.ops.or 0 3 1
C FilterExpr.parse 0 54 13
A UnionExpr.evaluate 0 10 3
C PathExpr.evaluate 0 52 17
C Step.axises.preceding 0 23 7
B Step.axises.ancestor 0 9 5
B Step.predicate 0 20 7
A NameTest.parse 0 6 4
A FunctionCall.funcs.substring-after 0 10 3
A FunctionCall.funcs.ceiling 0 7 2
A NodeSet.bool 0 3 1
A FunctionCall.funcs.normalize-space 0 14 4
A javascript-xpath-0.1.11.js ➔ BaseExprHasPredicates 0 1 1
A UnaryExpr.parse 0 7 2
A FunctionCall.funcs.root-node 0 13 3
A Lexer.empty 0 3 1
A Lexer.peek 0 3 1
A javascript-xpath-0.1.11.js ➔ NodeSet 0 7 1
A FunctionCall.evaluate 0 3 1
C javascript-xpath-0.1.11.js ➔ BinaryExpr 0 26 12
A NodeSet.merge 0 18 4
A javascript-xpath-0.1.11.js ➔ BaseExpr 0 1 1
A NodeSet.first 0 11 3
A UnaryExpr.evaluate 0 3 1
A Lexer.next 0 3 1
A BinaryExpr.ops.mod 0 3 1
A BinaryExpr.ops.+ 0 3 1
A javascript-xpath-0.1.11.js ➔ FilterExpr 0 10 1
A FunctionCall.funcs.boolean 0 6 2
A FunctionCall.show 0 16 3
A javascript-xpath-0.1.11.js ➔ Number 0 3 1
A Step.self 0 3 1
A NodeSet.doDel 0 15 4
A Step.axises.namespace 0 4 1
A NodeSet.list 0 14 2
A FunctionCall.funcs.position 0 6 2
B FunctionCall.funcs.local-name 0 17 5
A NodeType.match 0 3 1
A NodeUtil.getDescendantNodes 0 9 4
A FunctionCall.funcs.name 0 4 1
C Step.evaluate 0 55 13
A FunctionCall.funcs.round 0 7 2
A FunctionCall.arg 0 14 3
C FunctionCall.funcs.id 0 56 10
A BinaryExpr.ops.- 0 3 1
A VariableReference.parse 0 7 2
A Literal.evaluate 0 3 1
B FunctionCall.parse 0 21 6
B Step.axises.preceding-sibling 0 13 5
A NodeSet.string 0 4 2
A javascript-xpath-0.1.11.js ➔ Ctx 0 5 1
C NodeUtil.attrMatch 0 23 8
A Number.show 0 6 1
A NodeSet.del 0 21 3
A javascript-xpath-0.1.11.js ➔ UnaryExpr 0 7 1
A BinaryExpr.ops.and 0 3 1
A FunctionCall.funcs.lang 0 4 1
A BinaryExpr.ops.> 0 4 1
A javascript-xpath-0.1.11.js ➔ FunctionCall 0 18 3
A BaseExpr.string 0 5 2
A javascript-xpath-0.1.11.js ➔ NameTest 0 3 1
A Lexer.back 0 3 1
B NodeUtil.getChildNodes 0 70 4
A NodeSet.unshift 0 17 3
A BinaryExpr.ops.<= 0 4 1
A javascript-xpath-0.1.11.js ➔ Lexer 0 12 4
A BinaryExpr.ops.!= 0 4 1
A javascript-xpath-0.1.11.js ➔ VariableReference 0 3 1
A NodeSet._add 0 59 3
A FilterExpr.show 0 15 3
A VariableReference.show 0 6 1
A javascript-xpath-0.1.11.js ➔ Step 0 9 1
A FunctionCall.funcs.namespace-uri 0 4 1
A BinaryExpr.evaluate 0 3 1
D BaseExprHasPredicates.evaluatePredicates 0 44 9
A Number.parse 0 3 1
A PathExpr.show 0 18 3
B NodeType.parse 0 20 6
A BinaryExpr.ops.* 0 3 1
A FunctionCall.funcs.string-length 0 14 4
A BinaryExpr.ops.= 0 4 1
B NodeSet.reserveDelByNodeID 0 15 6
A UnionExpr.show 0 10 2
C PathExpr.parse 0 45 15
C FunctionCall.funcs.substring 0 26 7
A Step.axises.self 0 4 2
A NodeSet.createIdIndexMap 0 22 3
B FunctionCall.funcs.translate 0 20 6
A BinaryExpr.ops.>= 0 4 1
A FunctionCall.funcs.floor 0 7 2
A FunctionCall.funcs.context-node 0 9 2
A FunctionCall.funcs.concat 0 9 3
A BinaryExpr.ops.div 0 3 1
A FilterExpr.evaluate 0 12 3
A FilterExpr.predicate 0 3 1
A UnionExpr.path 0 10 3
A NodeSet.push 0 17 3
A NodeType.show 0 10 2
D BinaryExpr.compare 0 43 17
A BinaryExpr.show 0 9 1
A BaseExpr.bool 0 5 2
A NameTest.show 0 6 1
B NodeSet.iterator 0 32 2
A FunctionCall.funcs.false 0 6 2
A BaseExpr.number 0 5 2
A FilterExpr.context 0 3 1
A Step.parent 0 3 1
A FunctionCall.funcs.string 0 14 4
A FunctionCall.funcs.count 0 6 3
B NodeSet.sort 0 63 4
A javascript-xpath-0.1.11.js ➔ Literal 0 3 1
A NodeSet.number 0 3 1
A NodeSet.reserveDelByNode 0 8 1
A FunctionCall.funcs.substring-before 0 11 3
A Literal.show 0 6 1
A Number.evaluate 0 3 1
B BaseExprHasPredicates.parsePredicates 0 17 5
A Step.axises.descendant-or-self 0 6 3
C BinaryExpr.parse 0 35 8
A FunctionCall.funcs.true 0 6 2
B PathExpr.step 0 16 6
B Step.axises.following 0 12 5
A javascript-xpath-0.1.11.js ➔ PathExpr 0 9 1
A FunctionCall.funcs.number 0 14 4
C Step.axises.attribute 0 32 7
C Step.parse 0 68 13
B NameTest.match 0 10 5
A FunctionCall.funcs.sum 0 12 4
A Step.axises.parent 0 12 4
A FunctionCall.funcs.contains 0 8 2
A BinaryExpr.ops.< 0 4 1
B javascript-xpath-0.1.11.js ➔ install 0 81 1
A NodeID.get 0 3 1
A javascript-xpath-0.1.11.js ➔ UnionExpr 0 3 1
A FunctionCall.funcs.starts-with 0 8 2
D NodeSet.delDescendant 0 39 9
A Step.show 0 16 4
B Step.axises.following-sibling 0 13 5
F javascript-xpath-0.1.11.js ➔ ??? 0 134 40
B Step.axises.ancestor-or-self 0 9 5
A FunctionCall.funcs.not 0 6 2
B javascript-xpath-0.1.11.js ➔ NodeType 0 19 5
A FilterExpr.root 0 3 1
D NodeUtil.to 0 50 20

How to fix   Complexity   

Complexity

Complex classes like .tests/selenium/selenium-lib/core/xpath/javascript-xpath-0.1.11.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
/*  JavaScript-XPath 0.1.11
2
 *  (c) 2007 Cybozu Labs, Inc.
3
 *
4
 *  JavaScript-XPath is freely distributable under the terms of an MIT-style license.
5
 *  For details, see the JavaScript-XPath web site: http://coderepos.org/share/wiki/JavaScript-XPath
6
 *
7
/*--------------------------------------------------------------------------*/
8
9
10
11
(function () {
12
13
var undefined = void(0);
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
14
15
var defaultConfig = {
16
    targetFrame: undefined,
17
    exportInstaller: false,
18
    useNative: true,
19
    useInnerText: true
20
};
21
22
var config;
23
24
if (window.jsxpath) {
25
    config = window.jsxpath;
26
}
27
else {
28
    var scriptElms = document.getElementsByTagName('script');
29
    var scriptElm = scriptElms[scriptElms.length - 1];
30
    var scriptSrc = scriptElm.src;
31
    config = {};
32
    var scriptSrcMatchResult = scriptSrc.match(/\?(.*)$/);
33
    if (scriptSrcMatchResult) {
34
        var configStrings = scriptSrcMatchResult[1].split('&');
35
        for (var i = 0, l = configStrings.length; i < l; i ++) {
36
            var configString = configStrings[i];
37
            var configStringSplited = configString.split('=');
38
            var configName = configStringSplited[0];
39
            var configValue = configStringSplited[1];
40
            if (configValue == undefined) {
41
                configValue == true;
0 ignored issues
show
Unused Code introduced by
The expression configValue == true has no effects. Consider removing it.
Loading history...
42
            }
43
            else if (configValue == 'false' || /^-?\d+$/.test(configValue)) {
44
                configValue = eval(configValue);
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
45
            }
46
            config[configName] = configValue;
47
        }
48
    }
49
}
50
51
for (var n in defaultConfig) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
52
    if (!(n in config)) config[n] = defaultConfig[n];
53
}
54
55
config.hasNative = !!(document.implementation
56
                        && document.implementation.hasFeature
57
                        && document.implementation.hasFeature("XPath", null));
58
59
if (config.hasNative && config.useNative && !config.exportInstaller) {
60
    return;
61
}
62
63
64
65
var BinaryExpr;
66
var FilterExpr;
67
var FunctionCall;
68
var Literal;
69
var NameTest;
70
var NodeSet;
71
var NodeType;
72
var NodeUtil;
73
var Number;
74
var PathExpr;
75
var Step;
76
var UnaryExpr;
77
var UnionExpr;
78
var VariableReference;
79
80
/*
81
 * object: user agent identifier
82
 */
83
var uai = new function() {
84
85
    var ua = navigator.userAgent;
86
87
    if (RegExp == undefined) {
0 ignored issues
show
Comprehensibility Bug Compatibility introduced by
Using RegExp == undefined to check if a variable is declared may throw an Error. Consider using typeof {name} === "undefined"instead.
Loading history...
88
        if (ua.indexOf("Opera") >= 0) {
89
            this.opera = true;
90
        } else if (ua.indexOf("Netscape") >= 0) {
91
            this.netscape = true;
92
        } else if (ua.indexOf("Mozilla/") == 0) {
93
            this.mozilla = true;
94
        } else {
95
            this.unknown = true;
96
        }
97
98
        if (ua.indexOf("Gecko/") >= 0) {
99
            this.gecko = true;
100
        }
101
102
        if (ua.indexOf("Win") >= 0) {
103
            this.windows = true;
104
        } else if (ua.indexOf("Mac") >= 0) {
105
            this.mac = true;
106
        } else if (ua.indexOf("Linux") >= 0) {
107
            this.linux = true;
108
        } else if (ua.indexOf("BSD") >= 0) {
109
            this.bsd = true;
110
        } else if (ua.indexOf("SunOS") >= 0) {
111
            this.sunos = true;
112
        }
113
    }
114
    else {
115
116
        /* for Trident/Tasman */
117
        /*@cc_on
118
        @if (@_jscript)
119
            function jscriptVersion() {
120
                switch (@_jscript_version) {
121
                    case 3.0:  return "4.0";
122
                    case 5.0:  return "5.0";
123
                    case 5.1:  return "5.01";
124
                    case 5.5:  return "5.5";
125
                    case 5.6:
126
                        if ("XMLHttpRequest" in window) return "7.0";
127
                        return "6.0";
128
                    case 5.7:
129
                        return "7.0";
130
                    default:   return true;
131
                }
132
            }
133
            if (@_win16 || @_win32 || @_win64) {
134
                this.windows = true;
135
                this.trident = jscriptVersion();
136
            } else if (@_mac || navigator.platform.indexOf("Mac") >= 0) {
137
                // '@_mac' may be 'NaN' even if the platform is Mac,
138
                // so we check 'navigator.platform', too.
139
                this.mac = true;
140
                this.tasman = jscriptVersion();
141
            }
142
            if (/MSIE (\d+\.\d+)b?;/.test(ua)) {
143
                this.ie = RegExp.$1;
144
                this['ie' + RegExp.$1.charAt(0)] = true;
145
            }
146
        @else @*/
147
148
        /* for AppleWebKit */
149
        if (/AppleWebKit\/(\d+(?:\.\d+)*)/.test(ua)) {
150
            this.applewebkit = RegExp.$1;
151
            if (RegExp.$1.charAt(0) == 4) {
152
                this.applewebkit2 = true;
153
            }
154
            else {
155
                this.applewebkit3 = true;
156
            }
157
        }
158
159
        /* for Gecko */
160
        else if (typeof Components == "object" &&
161
                 (/Gecko\/(\d{8})/.test(ua) ||
162
                  navigator.product == "Gecko" && /^(\d{8})$/.test(navigator.productSub))) {
163
            this.gecko = RegExp.$1;
164
        }
165
166
        /*@end @*/
167
168
        if (typeof(opera) == "object" && typeof(opera.version) == "function") {
169
            this.opera = opera.version();
170
            this['opera' + this.opera[0] + this.opera[2]] = true;
171
        } else if (typeof opera == "object"
172
                && (/Opera[\/ ](\d+\.\d+)/.test(ua))) {
173
            this.opera = RegExp.$1;
174
        } else if (this.ie) {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
175
        } else if (/Safari\/(\d+(?:\.\d+)*)/.test(ua)) {
176
            this.safari = RegExp.$1;
177
        } else if (/NetFront\/(\d+(?:\.\d+)*)/.test(ua)) {
178
            this.netfront = RegExp.$1;
179
        } else if (/Konqueror\/(\d+(?:\.\d+)*)/.test(ua)) {
180
            this.konqueror = RegExp.$1;
181
        } else if (ua.indexOf("(compatible;") < 0
182
                && (/^Mozilla\/(\d+\.\d+)/.test(ua))) {
183
            this.mozilla = RegExp.$1;
184
            if (/\([^(]*rv:(\d+(?:\.\d+)*).*?\)/.test(ua))
185
                this.mozillarv = RegExp.$1;
186
            if (/Firefox\/(\d+(?:\.\d+)*)/.test(ua)) {
187
                this.firefox = RegExp.$1;
188
            } else if (/Netscape\d?\/(\d+(?:\.\d+)*)/.test(ua)) {
189
                this.netscape = RegExp.$1;
190
            }
191
        } else {
192
            this.unknown = true;
193
        }
194
195
        if (ua.indexOf("Win 9x 4.90") >= 0) {
196
            this.windows = "ME";
197
        } else if (/Win(?:dows)? ?(NT ?(\d+\.\d+)?|\d+|ME|Vista|XP)/.test(ua)) {
198
            this.windows = RegExp.$1;
199
            if (RegExp.$2) {
200
                this.winnt = RegExp.$2;
201
            } else switch (RegExp.$1) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
202
                case "2000":   this.winnt = "5.0";  break;
203
                case "XP":     this.winnt = "5.1";  break;
204
                case "Vista":  this.winnt = "6.0";  break;
205
            }
206
        } else if (ua.indexOf("Mac") >= 0) {
207
            this.mac = true;
208
        } else if (ua.indexOf("Linux") >= 0) {
209
            this.linux = true;
210
        } else if (/(\w*BSD)/.test(ua)) {
211
            this.bsd = RegExp.$1;
212
        } else if (ua.indexOf("SunOS") >= 0) {
213
            this.sunos = true;
214
        }
215
    }
216
};
217
218
219
/**
220
 * pseudo class: Lexer
221
 */
222
var Lexer = function(source) {
223
    var proto = Lexer.prototype;
224
    var tokens = source.match(proto.regs.token);
225
    for (var i = 0, l = tokens.length; i < l; i ++) {
226
        if (proto.regs.strip.test(tokens[i])) {
227
            tokens.splice(i, 1);
228
        }
229
    }
230
    for (var n in proto) tokens[n] = proto[n];
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
231
    tokens.index = 0;
232
    return tokens;
233
};
234
235
Lexer.prototype.regs = {
236
    token: /\$?(?:(?![0-9-])[\w-]+:)?(?![0-9-])[\w-]+|\/\/|\.\.|::|\d+(?:\.\d*)?|\.\d+|"[^"]*"|'[^']*'|[!<>]=|(?![0-9-])[\w-]+:\*|\s+|./g,
237
    strip: /^\s/
238
};
239
240
Lexer.prototype.peek = function(i) {
241
    return this[this.index + (i||0)];
242
};
243
Lexer.prototype.next = function() {
244
    return this[this.index++];
245
};
246
Lexer.prototype.back = function() {
247
    this.index--;
248
};
249
Lexer.prototype.empty = function() {
250
    return this.length <= this.index;
251
};
252
253
254
/**
255
 * class: Ctx
256
 */
257
var Ctx = function(node, position, last) {
258
    this.node = node;
259
    this.position = position || 1;
260
    this.last = last || 1;
261
};
262
263
264
/**
265
 * abstract class: BaseExpr
266
 */
267
var BaseExpr = function() {};
268
269
BaseExpr.prototype.number = function(ctx) {
270
    var exrs = this.evaluate(ctx);
271
    if (exrs.isNodeSet) return exrs.number();
272
    return + exrs;
273
};
274
275
BaseExpr.prototype.string = function(ctx) {
276
    var exrs = this.evaluate(ctx);
277
    if (exrs.isNodeSet) return exrs.string();
278
    return '' + exrs;
279
};
280
281
BaseExpr.prototype.bool = function(ctx) {
282
    var exrs = this.evaluate(ctx);
283
    if (exrs.isNodeSet) return exrs.bool();
284
    return !! exrs;
285
};
286
287
288
/**
289
 * abstract class: BaseExprHasPredicates
290
 */
291
var BaseExprHasPredicates = function() {};
292
293
BaseExprHasPredicates.parsePredicates = function(lexer, expr) {
294
    while (lexer.peek() == '[') {
295
        lexer.next();
296
        if (lexer.empty()) {
297
            throw Error('missing predicate expr');
298
        }
299
        var predicate = BinaryExpr.parse(lexer);
300
        expr.predicate(predicate);
301
        if (lexer.empty()) {
302
            throw Error('unclosed predicate expr');
303
        }
304
        if (lexer.next() != ']') {
305
            lexer.back();
306
            throw Error('bad token: ' + lexer.next());
307
        }
308
    }
309
};
310
311
BaseExprHasPredicates.prototyps = new BaseExpr();
312
313
BaseExprHasPredicates.prototype.evaluatePredicates = function(nodeset, start) {
314
    var predicates, predicate, nodes, node, nodeset, position, reverse;
0 ignored issues
show
Unused Code introduced by
The variable node seems to be never used. Consider removing it.
Loading history...
315
316
    reverse = this.reverse;
317
    predicates = this.predicates;
318
319
    nodeset.sort();
320
321
    for (var i = start || 0, l0 = predicates.length; i < l0; i ++) {
322
        predicate = predicates[i];
323
324
        var deleteIndexes = [];
325
        var nodes = nodeset.list();
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodes already seems to be declared on line 314. 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...
326
327
        for (var j = 0, l1 = nodes.length; j < l1; j ++) {
328
329
            position = reverse ? (l1 - j) : (j + 1);
330
            exrs = predicate.evaluate(new Ctx(nodes[j], position, l1));
0 ignored issues
show
Bug introduced by
The variable exrs seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.exrs.
Loading history...
331
332
            switch (typeof exrs) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
333
                case 'number':
334
                    exrs = (position == exrs);
335
                    break;
336
                case 'string':
337
                    exrs = !!exrs;
338
                    break;
339
                case 'object':
340
                    exrs = exrs.bool();
341
                    break;
342
            }
343
344
            if (!exrs) {
345
                deleteIndexes.push(j);
346
            }
347
        }
348
349
        for (var j = deleteIndexes.length - 1, l1 = 0; j >= l1; j --) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable l1 already seems to be declared on line 327. 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...
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 327. 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...
350
            nodeset.del(deleteIndexes[j]);
351
        }
352
353
    }
354
355
    return nodeset;
356
};
357
358
359
/**
360
 * class: BinaryExpr
361
 */
362
if (!window.BinaryExpr && window.defaultConfig)
363
    window.BinaryExpr = null;
364
365
BinaryExpr = function(op, left, right, datatype) {
366
    this.op = op;
367
    this.left = left;
368
    this.right = right;
369
370
    this.datatype = BinaryExpr.ops[op][2];
371
372
    this.needContextPosition = left.needContextPosition || right.needContextPosition;
373
    this.needContextNode = left.needContextNode || right.needContextNode;
374
375
    // Optimize [@id="foo"] and [@name="bar"]
376
    if (this.op == '=') {
377
        if (!right.needContextNode && !right.needContextPosition && 
378
            right.datatype != 'nodeset' && right.datatype != 'void' && left.quickAttr) {
379
            this.quickAttr = true;
380
            this.attrName = left.attrName;
381
            this.attrValueExpr = right;
382
        }
383
        else if (!left.needContextNode && !left.needContextPosition && 
384
            left.datatype != 'nodeset' && left.datatype != 'void' && right.quickAttr) {
385
            this.quickAttr = true;
386
            this.attrName = right.attrName;
387
            this.attrValueExpr = left;
388
        }
389
    }
390
};
391
392
BinaryExpr.compare = function(op, comp, left, right, ctx) {
393
    var type, lnodes, rnodes, nodes, nodeset, primitive;
394
395
    left = left.evaluate(ctx);
396
    right = right.evaluate(ctx);
397
398
    if (left.isNodeSet && right.isNodeSet) {
399
        lnodes = left.list();
400
        rnodes = right.list();
401
        for (var i = 0, l0 = lnodes.length; i < l0; i ++)
402
            for (var j = 0, l1 = rnodes.length; j < l1; j ++)
403
                if (comp(NodeUtil.to('string', lnodes[i]), NodeUtil.to('string', rnodes[j])))
404
                    return true;
405
        return false;
406
    }
407
408
    if (left.isNodeSet || right.isNodeSet) {
409
        if (left.isNodeSet)
410
            nodeset = left, primitive = right;
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
411
        else
412
            nodeset = right, primitive = left;
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
413
414
        nodes = nodeset.list();
415
        type = typeof primitive;
416
        for (var i = 0, l = nodes.length; i < l; i ++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 401. 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...
417
            if (comp(NodeUtil.to(type, nodes[i]), primitive))
418
                return true;
419
        }
420
        return false;
421
    }
422
423
    if (op == '=' || op == '!=') {
424
        if (typeof left == 'boolean' || typeof right == 'boolean') {
425
            return comp(!!left, !!right);
426
        }
427
        if (typeof left == 'number' || typeof right == 'number') {
428
            return comp(+left, +right);
429
        }
430
        return comp(left, right);
431
    }
432
433
    return comp(+left, +right);
434
};
435
436
437
BinaryExpr.ops = {
438
    'div': [6, function(left, right, ctx) {
439
        return left.number(ctx) / right.number(ctx);
440
    }, 'number'],
441
    'mod': [6, function(left, right, ctx) {
442
        return left.number(ctx) % right.number(ctx);
443
    }, 'number'],
444
    '*': [6, function(left, right, ctx) {
445
        return left.number(ctx) * right.number(ctx);
446
    }, 'number'],
447
    '+': [5, function(left, right, ctx) {
448
        return left.number(ctx) + right.number(ctx);
449
    }, 'number'],
450
    '-': [5, function(left, right, ctx) {
451
        return left.number(ctx) - right.number(ctx);
452
    }, 'number'],
453
    '<': [4, function(left, right, ctx) {
454
        return BinaryExpr.compare('<',
455
                    function(a, b) { return a < b }, left, right, ctx);
456
    }, 'boolean'],
457
    '>': [4, function(left, right, ctx) {
458
        return BinaryExpr.compare('>',
459
                    function(a, b) { return a > b }, left, right, ctx);
460
    }, 'boolean'],
461
    '<=': [4, function(left, right, ctx) {
462
        return BinaryExpr.compare('<=',
463
                    function(a, b) { return a <= b }, left, right, ctx);
464
    }, 'boolean'],
465
    '>=': [4, function(left, right, ctx) {
466
        return BinaryExpr.compare('>=',
467
                    function(a, b) { return a >= b }, left, right, ctx);
468
    }, 'boolean'],
469
    '=': [3, function(left, right, ctx) {
470
        return BinaryExpr.compare('=',
471
                    function(a, b) { return a == b }, left, right, ctx);
472
    }, 'boolean'],
473
    '!=': [3, function(left, right, ctx) {
474
        return BinaryExpr.compare('!=',
475
                    function(a, b) { return a != b }, left, right, ctx);
476
    }, 'boolean'],
477
    'and': [2, function(left, right, ctx) {
478
        return left.bool(ctx) && right.bool(ctx);
479
    }, 'boolean'],
480
    'or': [1, function(left, right, ctx) {
481
        return left.bool(ctx) || right.bool(ctx);
482
    }, 'boolean']
483
};
484
485
486
BinaryExpr.parse = function(lexer) {
487
    var op, precedence, info, expr, stack = [], index = lexer.index;
0 ignored issues
show
Unused Code introduced by
The assignment to variable index seems to be never used. Consider removing it.
Loading history...
488
489
    while (true) {
490
491
        if (lexer.empty()) {
492
            throw Error('missing right expression');
493
        }
494
        expr = UnaryExpr.parse(lexer);
495
496
        op = lexer.next();
497
        if (!op) {
498
            break;
499
        }
500
501
        info = this.ops[op];
502
        precedence = info && info[0];
503
        if (!precedence) {
504
            lexer.back();
505
            break;
506
        }
507
508
        while (stack.length && precedence <= this.ops[stack[stack.length-1]][0]) {
509
            expr = new BinaryExpr(stack.pop(), stack.pop(), expr);
510
        }
511
512
        stack.push(expr, op);
513
    }
514
515
    while (stack.length) {
516
        expr = new BinaryExpr(stack.pop(), stack.pop(), expr);
0 ignored issues
show
Comprehensibility Bug introduced by
The variable expr does not seem to be initialized in case the while loop on line 489 is not entered. Are you sure this can never be the case?
Loading history...
517
    }
518
519
    return expr;
520
};
521
522
BinaryExpr.prototype = new BaseExpr();
523
524
BinaryExpr.prototype.evaluate = function(ctx) {
525
    return BinaryExpr.ops[this.op][1](this.left, this.right, ctx);
526
};
527
528
BinaryExpr.prototype.show = function(indent) {
529
    indent = indent || '';
530
    var t = '';
531
    t += indent + 'binary: ' + this.op + '\n';
532
    indent += '    ';
533
    t += this.left.show(indent);
534
    t += this.right.show(indent);
535
    return t;
536
};
537
538
539
/**
540
 * class: UnaryExpr
541
 */
542
if (!window.UnaryExpr && window.defaultConfig)
543
    window.UnaryExpr = null;
544
545
UnaryExpr = function(op, expr) {
546
    this.op = op;
547
    this.expr = expr;
548
549
    this.needContextPosition = expr.needContextPosition;
550
    this.needContextNode = expr.needContextNode;
551
};
552
553
UnaryExpr.ops = { '-': 1 };
554
555
UnaryExpr.parse = function(lexer) {
556
    var token;
0 ignored issues
show
Unused Code introduced by
The variable token seems to be never used. Consider removing it.
Loading history...
557
    if (this.ops[lexer.peek()])
558
        return new UnaryExpr(lexer.next(), UnaryExpr.parse(lexer));
559
    else
560
        return UnionExpr.parse(lexer);
561
};
562
563
UnaryExpr.prototype = new BaseExpr();
564
565
UnaryExpr.prototype.datatype = 'number';
566
567
UnaryExpr.prototype.evaluate = function(ctx) {
568
    return - this.expr.number(ctx);
569
};
570
571
UnaryExpr.prototype.show = function(indent) {
572
    indent = indent || '';
573
    var t = '';
574
    t += indent + 'unary: ' + this.op + '\n';
575
    indent += '    ';
576
    t += this.expr.show(indent);
577
    return t;
578
};
579
580
581
/**
582
 * class: UnionExpr
583
 */
584
if (!window.UnionExpr && window.defaultConfig)
585
    window.UnionExpr = null;
586
587
UnionExpr = function() {
588
    this.paths = [];
589
};
590
591
UnionExpr.ops = { '|': 1 };
592
593
594
UnionExpr.parse = function(lexer) {
595
    var union, expr;
596
597
    expr = PathExpr.parse(lexer);
598
    if (!this.ops[lexer.peek()])
599
        return expr;
600
601
    union = new UnionExpr();
602
    union.path(expr);
603
604
    while (true) {
605
        if (!this.ops[lexer.next()]) break;
606
        if (lexer.empty()) {
607
            throw Error('missing next union location path');
608
        }
609
        union.path(PathExpr.parse(lexer));
610
    }
611
612
613
614
    lexer.back();
615
    return union;
616
};
617
618
UnionExpr.prototype = new BaseExpr();
619
620
UnionExpr.prototype.datatype = 'nodeset';
621
622
UnionExpr.prototype.evaluate = function(ctx) {
623
    var paths = this.paths;
624
    var nodeset = new NodeSet();
625
    for (var i = 0, l = paths.length; i < l; i ++) {
626
        var exrs = paths[i].evaluate(ctx);
627
        if (!exrs.isNodeSet) throw Error('PathExpr must be nodeset');
628
        nodeset.merge(exrs);
629
    }
630
    return nodeset;
631
};
632
633
UnionExpr.prototype.path = function(path) {
634
    this.paths.push(path);
635
636
    if (path.needContextPosition) {
637
        this.needContextPosition = true;
638
    }
639
    if (path.needContextNode) {
640
        this.needContextNode = true;
641
    }
642
}
643
UnionExpr.prototype.show = function(indent) {
644
    indent = indent || '';
645
    var t = '';
646
    t += indent + 'union:' + '\n';
647
    indent += '    ';
648
    for (var i = 0; i < this.paths.length; i ++) {
649
        t += this.paths[i].show(indent);
650
    }
651
    return t;
652
};
653
654
655
/**
656
 * class: PathExpr
657
 */
658
if (!window.PathExpr && window.defaultConfig)
659
    window.PathExpr = null;
660
661
PathExpr = function(filter) {
662
    this.filter = filter;
663
    this.steps = [];
664
665
    this.datatype = filter.datatype;
666
667
    this.needContextPosition = filter.needContextPosition;
668
    this.needContextNode = filter.needContextNode;
669
};
670
671
PathExpr.ops = { '//': 1, '/': 1 };
672
673
PathExpr.parse = function(lexer) {
674
    var op, expr, path, token;
675
676
    if (this.ops[lexer.peek()]) {
677
        op = lexer.next();
678
        token = lexer.peek();
679
680
        if (op == '/' && (lexer.empty() || 
681
                (token != '.' && token != '..' && token != '@' && token != '*' && 
682
                !/(?![0-9])[\w]/.test(token)))) { 
683
            return FilterExpr.root();
684
        }
685
686
        path = new PathExpr(FilterExpr.root()); // RootExpr
687
688
        if (lexer.empty()) {
689
            throw Error('missing next location step');
690
        }
691
        expr = Step.parse(lexer);
692
        path.step(op, expr);
693
    }
694
    else {
695
        expr = FilterExpr.parse(lexer);
696
        if (!expr) {
697
            expr = Step.parse(lexer);
698
            path = new PathExpr(FilterExpr.context());
699
            path.step('/', expr);
700
        }
701
        else if (!this.ops[lexer.peek()])
702
            return expr;
703
        else
704
            path = new PathExpr(expr);
705
    }
706
707
    while (true) {
708
        if (!this.ops[lexer.peek()]) break;
709
        op = lexer.next();
710
        if (lexer.empty()) {
711
            throw Error('missing next location step');
712
        }
713
        path.step(op, Step.parse(lexer));
714
    }
715
716
    return path;
717
};
718
719
PathExpr.prototype = new BaseExpr();
720
721
PathExpr.prototype.evaluate = function(ctx) {
722
    var nodeset = this.filter.evaluate(ctx);
723
    if (!nodeset.isNodeSet) throw Exception('Filter nodeset must be nodeset type');
724
725
    var steps = this.steps;
726
727
    for (var i = 0, l0 = steps.length; i < l0 && nodeset.length; i ++) {
728
        var step = steps[i][1];
729
        var reverse = step.reverse;
730
        var iter = nodeset.iterator(reverse);
731
        var prevNodeset = nodeset;
732
        nodeset = null;
0 ignored issues
show
Unused Code introduced by
The assignment to nodeset seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
733
        var node, next;
734
        if (!step.needContextPosition && step.axis == 'following') {
735
            for (node = iter(); next = iter(); node = next) {
736
737
                // Safari 2 node.contains problem
738
                if (uai.applewebkit2) {
739
                    var contains = false;
740
                    var ancestor = next;
741
                    do {
742
                        if (ancestor == node) {
743
                            contains = true;
744
                            break;
745
                        }
746
                    } while (ancestor = ancestor.parentNode);
747
                    if (!contains) break;
748
                }
749
                else {
750
                    try { if (!node.contains(next)) break }
751
                    catch(e) { if (!(next.compareDocumentPosition(node) & 8)) break }
752
                }
753
            }
754
            nodeset = step.evaluate(new Ctx(node));
755
        }
756
        else if (!step.needContextPosition && step.axis == 'preceding') {
757
            node = iter();
758
            nodeset = step.evaluate(new Ctx(node));
759
        }
760
        else {
761
            node = iter();
762
            var j = 0;
763
            nodeset = step.evaluate(new Ctx(node), false, prevNodeset, j);
764
            while (node = iter()) {
765
                j ++;
766
                nodeset.merge(step.evaluate(new Ctx(node), false, prevNodeset, j));
767
            }
768
        }
769
    }
770
771
    return nodeset;
772
};
773
774
PathExpr.prototype.step = function(op, step) {
775
    step.op = op;
776
    this.steps.push([op, step]);
777
778
    this.quickAttr = false;
779
780
    if (this.steps.length == 1) {
781
        if (op == '/' && step.axis == 'attribute') {
782
            var test = step.test;
783
            if (!test.notOnlyElement && test.name != '*') {
784
                this.quickAttr = true;
785
                this.attrName = test.name;
786
            }
787
        }
788
    }
789
};
790
791
PathExpr.prototype.show = function(indent) {
792
    indent = indent || '';
793
    var t = '';
794
    t += indent + 'path:' + '\n';
795
    indent += '    ';
796
    t += indent + 'filter:' + '\n';
797
    t += this.filter.show(indent + '    ');
798
    if (this.steps.length) {
799
        t += indent + 'steps:' + '\n';
800
        indent += '    ';
801
        for (var i = 0; i < this.steps.length; i ++) {
802
            var step = this.steps[i];
803
            t += indent + 'operator: ' + step[0] + '\n';
804
            t += step[1].show(indent);
805
        }
806
    }
807
    return t;
808
};
809
810
811
/**
812
 * class: FilterExpr
813
 */
814
if (!window.FilterExpr && window.defaultConfig)
815
    window.FilterExpr = null;
816
817
FilterExpr = function(primary) {
818
    this.primary = primary;
819
    this.predicates = [];
820
821
    this.datatype = primary.datatype;
822
823
    this.needContextPosition = primary.needContextPosition;
824
825
    this.needContextNode = primary.needContextNode;
826
};
827
828
FilterExpr.parse = function(lexer) {
829
    var expr, filter, token, ch;
830
831
    token = lexer.peek();
832
    ch = token.charAt(0);
833
834
    switch (ch) {
835
        case '$':
836
            expr = VariableReference.parse(lexer);
837
            break;
838
839
        case '(':
840
            lexer.next();
841
            expr = BinaryExpr.parse(lexer);
842
            if (lexer.empty()) {
843
                throw Error('unclosed "("');
844
            }
845
            if (lexer.next() != ')') {
846
                lexer.back();
847
                throw Error('bad token: ' + lexer.next());
848
            }
849
            break;
850
851
        case '"':
852
        case "'":
853
            expr = Literal.parse(lexer);
854
            break;
855
856
        default:
857
            if (!isNaN(+token)) {
858
                expr = Number.parse(lexer);
859
            }
860
861
            else if (NodeType.types[token]) {
862
                return null;
863
            }
864
865
            else if (/(?![0-9])[\w]/.test(ch) && lexer.peek(1) == '(') {
866
                expr = FunctionCall.parse(lexer);
867
            }
868
            else {
869
                return null;
870
            }
871
            break;
872
    }
873
874
    if (lexer.peek() != '[') return expr;
875
876
    filter = new FilterExpr(expr);
877
878
    BaseExprHasPredicates.parsePredicates(lexer, filter);
879
880
    return filter;
881
};
882
883
FilterExpr.root = function() {
884
    return new FunctionCall('root-node');
885
};
886
FilterExpr.context = function() {
887
    return new FunctionCall('context-node');
888
};
889
890
FilterExpr.prototype = new BaseExprHasPredicates();
891
892
FilterExpr.prototype.evaluate = function(ctx) {
893
    var nodeset = this.primary.evaluate(ctx);
894
    if(!nodeset.isNodeSet) {
895
        if (this.predicates.length)
896
            throw Error(
897
                'Primary result must be nodeset type ' +
898
                'if filter have predicate expression');
899
        return nodeset;
900
    }
901
902
    return  this.evaluatePredicates(nodeset);
903
};
904
905
FilterExpr.prototype.predicate = function(predicate) {
906
    this.predicates.push(predicate);
907
};
908
909
FilterExpr.prototype.show = function(indent) {
910
    indent = indent || '';
911
    var t = '';
912
    t += indent + 'filter: ' + '\n';
913
    indent += '    ';
914
    t += this.primary.show(indent);
915
    if (this.predicates.length) {
916
        t += indent + 'predicates: ' + '\n';
917
        indent += '    ';
918
        for (var i = 0; i < this.predicates.length; i ++) {
919
            t += this.predicates[i].show(indent);
920
        }
921
    }
922
    return t;
923
};
924
925
926
if (!window.NodeUtil && window.defaultConfig)
927
    window.NodeUtil = null;
928
929
NodeUtil = {
930
    to: function(valueType, node) {
931
        var t, type = node.nodeType;
932
        // Safari2: innerText contains some bugs
933
        if (type == 1 && config.useInnerText && !uai.applewebkit2) {
934
            t = node.textContent;
935
            t = (t == undefined || t == null) ? node.innerText : t;
936
            t = (t == undefined || t == null) ? '' : t;
937
        }
938
        if (typeof t != 'string') {
0 ignored issues
show
Bug introduced by
The variable t does not seem to be initialized in case type == 1 && config.useI...xt && !uai.applewebkit2 on line 933 is false. Are you sure this can never be the case?
Loading history...
939
/*@cc_on
940
            if (type == 1 && node.nodeName.toLowerCase() == 'title') {
941
                t = node.text;
942
            }
943
            else
944
@*/
945
            if (type == 9 || type == 1) {
946
                if (type == 9) {
947
                    node =  node.documentElement;
948
                }
949
                else {
950
                    node = node.firstChild;
951
                }
952
                for (t = '', stack = [], i = 0; node;) {
0 ignored issues
show
Bug introduced by
The variable stack seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.stack.
Loading history...
953
                    do {
954
                        if (node.nodeType != 1) {
955
                            t += node.nodeValue;
956
                        }
957
/*@cc_on
958
                        else if (node.nodeName.toLowerCase() == 'title') {
959
                            t += node.text;
960
                        }
961
@*/
962
                        stack[i++] = node; // push
0 ignored issues
show
Bug introduced by
The variable i is changed as part of the loop loop for example by i++ on line 962. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
963
                    } while (node = node.firstChild);
964
                    while (i && !(node = stack[--i].nextSibling)) {}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
965
                }
966
            }
967
            else {
968
                t = node.nodeValue;
969
            }
970
        }
971
        switch (valueType) {
972
            case 'number':
973
                return + t;
974
            case 'boolean':
975
                return !! t;
976
            default:
977
                return t;
978
        }
979
    },
980
    attrPropMap: {
981
        name: 'name',
0 ignored issues
show
Unused Code Bug introduced by
The key name is used more than once in this object expression.
Loading history...
982
        'class': 'className',
983
        dir: 'dir',
984
        id: 'id',
985
        name: 'name',
986
        title: 'title'
987
    },
988
    attrMatch: function(node, attrName, attrValue) {
989
/*@cc_on @if (@_jscript)
990
        var propName = NodeUtil.attrPropMap[attrName];
991
        if (!attrName ||
992
            attrValue == null && (
993
                propName && node[propName] ||
994
                !propName && node.getAttribute && node.getAttribute(attrName, 2)
995
            ) ||
996
            attrValue != null && (
997
                propName && node[propName] == attrValue ||
998
                !propName && node.getAttribute && node.getAttribute(attrName, 2) == attrValue
999
            )) {
1000
@else @*/
1001
        if (!attrName ||
1002
            attrValue == null && node.hasAttribute && node.hasAttribute(attrName) ||
1003
            attrValue != null && node.getAttribute && node.getAttribute(attrName) == attrValue) {
1004
/*@end @*/
1005
            return true;
1006
        }
1007
        else {
1008
            return false;
1009
        }
1010
    },
1011
    getDescendantNodes: function(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex) {
1012
        if (prevNodeset) {
1013
            prevNodeset.delDescendant(node, prevIndex);
1014
        }
1015
/*@cc_on
1016
        try {
1017
            if (!test.notOnlyElement || test.type == 8 || (attrName && test.type == 0)) {
1018
1019
                var all = node.all;
1020
                if (!all) {
1021
                    return nodeset;
1022
                }
1023
1024
                var name = test.name;
1025
                if (test.type == 8) name = '!';
1026
                else if (test.type == 0) name = '*';
1027
1028
                if (name != '*') {
1029
                    all = all.tags(name);
1030
                    if (!all) {
1031
                        return nodeset;
1032
                    }
1033
                }
1034
1035
                if (attrName) {
1036
                    var result = []
1037
                    var i = 0;
1038
                    if (attrValue != null && (attrName == 'id' || attrName == 'name')) {
1039
                        all = all[attrValue];
1040
                        if (!all) {
1041
                            return nodeset;
1042
                        }
1043
                        if (!all.length || all.nodeType) {
1044
                            all = [all];
1045
                        }
1046
                    }
1047
        
1048
                    while (node = all[i++]) {
1049
                        if (NodeUtil.attrMatch(node, attrName, attrValue)) result.push(node);
1050
                    }
1051
1052
                    all = result;
1053
                }
1054
1055
                var i = 0;
1056
                while (node = all[i++]) {
1057
                    if (name != '*' || node.tagName != '!') {
1058
                        nodeset.push(node);
1059
                    }
1060
                }
1061
1062
                return nodeset;
1063
            }
1064
1065
            (function (parent) {
1066
                var g = arguments.callee;
1067
                var node = parent.firstChild;
1068
                if (node) {
1069
                    for (; node; node = node.nextSibling) {
1070
                        if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1071
                            if (test.match(node)) nodeset.push(node);
1072
                        }
1073
                        g(node);
1074
                    }
1075
                }
1076
            })(node);
1077
1078
            return nodeset;
1079
        }
1080
        catch(e) {
1081
@*/
1082
        if (attrValue && attrName == 'id' && node.getElementById) {
1083
            node = node.getElementById(attrValue);
1084
            if (node && test.match(node)) {
1085
                nodeset.push(node);
1086
            }
1087
        }
1088
        else if (attrValue && attrName == 'name' && node.getElementsByName) {
1089
            var nodes = node.getElementsByName(attrValue);
1090
            for (var i = 0, l = nodes.length; i < l; i ++) {
1091
                node = nodes[i];
1092
                if (uai.opera ? (node.name == attrValue && test.match(node)) : test.match(node)) {
1093
                    nodeset.push(node);
1094
                }
1095
            }
1096
        }
1097
        else if (attrValue && attrName == 'class' && node.getElementsByClassName) {
1098
            var nodes = node.getElementsByClassName(attrValue);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodes already seems to be declared on line 1089. 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...
1099
            for (var i = 0, l = nodes.length; i < l; i ++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 1090. 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...
Comprehensibility Naming Best Practice introduced by
The variable l already seems to be declared on line 1090. 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...
1100
                node = nodes[i];
1101
                if (node.className == attrValue && test.match(node)) {
1102
                    nodeset.push(node);
1103
                }
1104
            }
1105
        }
1106
        else if (test.notOnlyElement) {
1107
            (function (parent) {
1108
                var f = arguments.callee;
1109
                for (var node = parent.firstChild; node; node = node.nextSibling) {
1110
                    if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1111
                        if (test.match(node.nodeType)) nodeset.push(node);
1112
                    }
1113
                    f(node);
1114
                }
1115
            })(node);
1116
        }
1117
        else {
1118
            var name = test.name;
1119
            if (node.getElementsByTagName) {
1120
                var nodes = node.getElementsByTagName(name);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodes already seems to be declared on line 1089. 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...
1121
                if (nodes) {
1122
                    var i = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 1090. 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...
1123
                    while (node = nodes[i++]) {
1124
                        if (NodeUtil.attrMatch(node, attrName, attrValue)) nodeset.push(node);
1125
                    }
1126
                }
1127
            }
1128
        }
1129
        return nodeset;
1130
/*@cc_on
1131
        }
1132
@*/
1133
    },
1134
1135
    getChildNodes: function(test, node, nodeset, attrName, attrValue) {
1136
1137
/*@cc_on
1138
        try {
1139
            var children;
1140
1141
            if ((!test.notOnlyElement || test.type == 8 || (attrName && test.type == 0)) && (children = node.children)) {
1142
                var name, elm;
1143
1144
                name = test.name;
1145
                if (test.type == 8) name = '!';
1146
                else if (test.type == 0) name = '*';
1147
1148
                if (name != '*') {
1149
                    children = children.tags(name);
1150
                    if (!children) {
1151
                        return nodeset;
1152
                    }
1153
                }
1154
1155
                if (attrName) {
1156
                    var result = []
1157
                    var i = 0;
1158
                    if (attrName == 'id' || attrName == 'name') {
1159
                        children = children[attrValue];
1160
        
1161
                        if (!children) {
1162
                            return nodeset;
1163
                        }
1164
        
1165
                        if (!children.length || children.nodeType) {
1166
                            children = [children];
1167
                        }
1168
                    }
1169
        
1170
                    while (node = children[i++]) {
1171
                        if (NodeUtil.attrMatch(node, attrName, attrValue)) result.push(node);
1172
                    }
1173
                    children = result;
1174
                }
1175
1176
                var i = 0;
1177
                while (node = children[i++]) {
1178
                    if (name != '*' || node.tagName != '!') {
1179
                        nodeset.push(node);
1180
                    }
1181
                }
1182
1183
                return nodeset;
1184
            }
1185
1186
            for (var i = 0, node = node.firstChild; node; i++, node = node.nextSibling) {
1187
                if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1188
                    if (test.match(node)) nodeset.push(node);
1189
                }
1190
            }
1191
1192
            return nodeset;
1193
        } catch(e) {
1194
@*/
1195
        for (var node = node.firstChild; node; node = node.nextSibling) {
1196
            if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1197
                if (test.match(node)) nodeset.push(node);
1198
            }
1199
        }
1200
        return nodeset;
1201
/*@cc_on
1202
        }
1203
@*/
1204
    }
1205
};
1206
1207
/*@cc_on
1208
var AttributeWrapper = function(node, parent, sourceIndex) {
1209
    this.node = node;
1210
    this.nodeType = 2;
1211
    this.nodeValue = node.nodeValue;
1212
    this.nodeName = node.nodeName;
1213
    this.parentNode = parent;
1214
    this.ownerElement = parent;
1215
    this.parentSourceIndex = sourceIndex;
1216
};
1217
1218
@*/
1219
1220
1221
/**
1222
 * class: Step
1223
 */
1224
if (!window.Step && window.defaultConfig)
1225
    window.Step = null;
1226
1227
Step = function(axis, test) {
1228
    // TODO check arguments and throw axis error
1229
    this.axis = axis;
1230
    this.reverse = Step.axises[axis][0];
1231
    this.func = Step.axises[axis][1];
1232
    this.test = test;
1233
    this.predicates = [];
1234
    this._quickAttr = Step.axises[axis][2]
1235
};
1236
1237
Step.axises = {
1238
1239
    ancestor: [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) {
1240
        while (node = node.parentNode) {
1241
            if (prevNodeset && node.nodeType == 1) {
1242
                prevNodeset.reserveDelByNode(node, prevIndex, true);
1243
            }
1244
            if (test.match(node)) nodeset.unshift(node);
1245
        }
1246
        return nodeset;
1247
    }],
1248
1249
    'ancestor-or-self': [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) {
1250
        do {
1251
            if (prevNodeset && node.nodeType == 1) {
1252
                prevNodeset.reserveDelByNode(node, prevIndex, true);
1253
            }
1254
            if (test.match(node)) nodeset.unshift(node);
1255
        } while (node = node.parentNode)
1256
        return nodeset;
1257
    }],
1258
1259
    attribute: [false, function(test, node, nodeset) {
1260
        var attrs = node.attributes;
1261
        if (attrs) {
1262
/*@cc_on
1263
            var sourceIndex = node.sourceIndex;
1264
@*/
1265
            if ((test.notOnlyElement && test.type == 0) || test.name == '*') {
1266
                for (var i = 0, attr; attr = attrs[i]; i ++) {
1267
/*@cc_on @if (@_jscript)
1268
                    if (attr.nodeValue) {
1269
                        nodeset.push(new AttributeWrapper(attr, node, sourceIndex));
1270
                    }
1271
@else @*/
1272
                    nodeset.push(attr);
1273
/*@end @*/
1274
                }
1275
            }
1276
            else {
1277
                var attr = attrs.getNamedItem(test.name);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable attr already seems to be declared on line 1266. 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...
1278
                
1279
/*@cc_on @if (@_jscript)
1280
                if (attr && attr.nodeValue) {
1281
                    attr = new AttributeWrapper(attr, node, sourceIndex);;
1282
@else @*/
1283
                if (attr) {
1284
/*@end @*/
1285
                    nodeset.push(attr);
1286
                }
1287
            }
1288
        }
1289
        return nodeset;
1290
    }],
1291
1292
    child: [false, NodeUtil.getChildNodes, true],
1293
1294
    descendant: [false, NodeUtil.getDescendantNodes, true],
1295
1296
    'descendant-or-self': [false, function(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex) {
1297
        if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1298
            if (test.match(node)) nodeset.push(node);
1299
        }
1300
        return NodeUtil.getDescendantNodes(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex);
1301
    }, true],
1302
1303
    following: [false, function(test, node, nodeset, attrName, attrValue) {
1304
        do {
1305
            var child = node;
1306
            while (child = child.nextSibling) {
1307
                if (NodeUtil.attrMatch(child, attrName, attrValue)) {
1308
                    if (test.match(child)) nodeset.push(child);
1309
                }
1310
                nodeset = NodeUtil.getDescendantNodes(test, child, nodeset, attrName, attrValue);
1311
            }
1312
        } while (node = node.parentNode);
1313
        return nodeset;
1314
    }, true],
1315
1316
    'following-sibling': [false, function(test, node, nodeset, _, __, prevNodeset, prevIndex) {
1317
        while (node = node.nextSibling) {
1318
1319
            if (prevNodeset && node.nodeType == 1) {
1320
                prevNodeset.reserveDelByNode(node, prevIndex);
1321
            }
1322
1323
            if (test.match(node)) {
1324
                nodeset.push(node);
1325
            }
1326
        }
1327
        return nodeset;
1328
    }],
1329
1330
    namespace: [false, function(test, node, nodeset) {
1331
        // not implemented
1332
        return nodeset;
1333
    }],
1334
1335
    parent: [false, function(test, node, nodeset) {
1336
        if (node.nodeType == 9) {
1337
            return nodeset;
1338
        }
1339
        if (node.nodeType == 2) {
1340
            nodeset.push(node.ownerElement);
1341
            return nodeset;
1342
        }
1343
        var node = node.parentNode;
1344
        if (test.match(node)) nodeset.push(node);
1345
        return nodeset;
1346
    }],
1347
1348
    preceding: [true, function(test, node, nodeset, attrName, attrValue) {
1349
        var parents = [];
1350
        do {
1351
            parents.unshift(node);
1352
        } while (node = node.parentNode);
1353
1354
        for (var i = 1, l0 = parents.length; i < l0; i ++) {
1355
            var siblings = [];
1356
            node = parents[i];
1357
            while (node = node.previousSibling) {
1358
                siblings.unshift(node);
1359
            }
1360
1361
            for (var j = 0, l1 = siblings.length; j < l1; j ++) {
1362
                node = siblings[j];
1363
                if (NodeUtil.attrMatch(node, attrName, attrValue)) {
1364
                    if (test.match(node)) nodeset.push(node);
1365
                }
1366
                nodeset = NodeUtil.getDescendantNodes(test, node, nodeset, attrName, attrValue);
1367
            }
1368
        }
1369
        return nodeset;
1370
    }, true],
1371
1372
    'preceding-sibling': [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) {
1373
        while (node = node.previousSibling) {
1374
1375
            if (prevNodeset && node.nodeType == 1) {
1376
                prevNodeset.reserveDelByNode(node, prevIndex, true);
1377
            }
1378
1379
            if (test.match(node)) {
1380
                nodeset.unshift(node)
1381
            }
1382
        }
1383
        return nodeset;
1384
    }],
1385
1386
    self: [false, function(test, node, nodeset) {
1387
        if (test.match(node)) nodeset.push(node);
1388
        return nodeset;
1389
    }]
1390
};
1391
1392
Step.parse = function(lexer) {
1393
    var axis, test, step, token;
1394
1395
    if (lexer.peek() == '.') {
1396
        step = this.self();
1397
        lexer.next();
1398
    }
1399
    else if (lexer.peek() == '..') {
1400
        step = this.parent();
1401
        lexer.next();
1402
    }
1403
    else {
1404
        if (lexer.peek() == '@') {
1405
            axis = 'attribute';
1406
            lexer.next();
1407
            if (lexer.empty()) {
1408
                throw Error('missing attribute name');
1409
            }
1410
        }
1411
        else {
1412
            if (lexer.peek(1) == '::') {
1413
                
1414
                if (!/(?![0-9])[\w]/.test(lexer.peek().charAt(0))) {
1415
                    throw Error('bad token: ' + lexer.next());
1416
                }
1417
        
1418
                axis = lexer.next();
1419
                lexer.next();
1420
1421
                if (!this.axises[axis]) {
1422
                    throw Error('invalid axis: ' + axis);
1423
                }
1424
                if (lexer.empty()) {
1425
                    throw Error('missing node name');
1426
                }
1427
            }
1428
            else {
1429
                axis = 'child';
1430
            }
1431
        }
1432
    
1433
        token = lexer.peek();
1434
        if (!/(?![0-9])[\w]/.test(token.charAt(0))) {
1435
            if (token == '*') {
1436
                test = NameTest.parse(lexer)
1437
            }
1438
            else {
1439
                throw Error('bad token: ' + lexer.next());
1440
            }
1441
        }
1442
        else {
1443
            if (lexer.peek(1) == '(') {
1444
                if (!NodeType.types[token]) {
1445
                    throw Error('invalid node type: ' + token);
1446
                }
1447
                test = NodeType.parse(lexer)
1448
            }
1449
            else {
1450
                test = NameTest.parse(lexer);
1451
            }
1452
        }
1453
        step = new Step(axis, test);
1454
    }
1455
1456
    BaseExprHasPredicates.parsePredicates(lexer, step);
1457
1458
    return step;
1459
};
1460
1461
Step.self = function() {
1462
    return new Step('self', new NodeType('node'));
1463
};
1464
1465
Step.parent = function() {
1466
    return new Step('parent', new NodeType('node'));
1467
};
1468
1469
Step.prototype = new BaseExprHasPredicates();
1470
1471
Step.prototype.evaluate = function(ctx, special, prevNodeset, prevIndex) {
1472
    var node = ctx.node;
1473
    var reverse = false;
0 ignored issues
show
Unused Code introduced by
The variable reverse seems to be never used. Consider removing it.
Loading history...
1474
1475
    if (!special && this.op == '//') {
1476
1477
        if (!this.needContextPosition && this.axis == 'child') {
1478
            if (this.quickAttr) {
1479
                var attrValue = this.attrValueExpr ? this.attrValueExpr.string(ctx) : null;
1480
                var nodeset = NodeUtil.getDescendantNodes(this.test, node, new NodeSet(), this.attrName, attrValue, prevNodeset, prevIndex);
1481
                nodeset = this.evaluatePredicates(nodeset, 1);
1482
            }
1483
            else {
1484
                var nodeset = NodeUtil.getDescendantNodes(this.test, node, new NodeSet(), null, null, prevNodeset, prevIndex);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodeset already seems to be declared on line 1480. 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...
1485
                nodeset = this.evaluatePredicates(nodeset);
1486
            }
1487
        }
1488
        else {
1489
            var step = new Step('descendant-or-self', new NodeType('node'));
1490
            var nodes = step.evaluate(ctx, false, prevNodeset, prevIndex).list();
1491
            var nodeset = null;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodeset already seems to be declared on line 1480. 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...
1492
            step.op = '/';
1493
            for (var i = 0, l = nodes.length; i < l; i ++) {
1494
                if (!nodeset) {
1495
                    nodeset = this.evaluate(new Ctx(nodes[i]), true);
1496
                }
1497
                else {
1498
                    nodeset.merge(this.evaluate(new Ctx(nodes[i]), true));
1499
                }
1500
            }
1501
            nodeset = nodeset || new NodeSet();
1502
        }
1503
    }
1504
    else {
1505
1506
        if (this.needContextPosition) {
1507
            prevNodeset = null;
1508
            prevIndex = null;
1509
        }
1510
1511
        if (this.quickAttr) {
1512
            var attrValue = this.attrValueExpr ? this.attrValueExpr.string(ctx) : null;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable attrValue already seems to be declared on line 1479. 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...
1513
            var nodeset = this.func(this.test, node, new NodeSet(), this.attrName, attrValue, prevNodeset, prevIndex);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodeset already seems to be declared on line 1480. 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...
1514
            nodeset = this.evaluatePredicates(nodeset, 1);
1515
        }
1516
        else {
1517
            var nodeset = this.func(this.test, node, new NodeSet(), null, null, prevNodeset, prevIndex);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nodeset already seems to be declared on line 1480. 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...
1518
            nodeset = this.evaluatePredicates(nodeset);
1519
        }
1520
        if (prevNodeset) {
1521
            prevNodeset.doDel();
1522
        }
1523
    }
1524
    return nodeset;
1525
};
1526
1527
Step.prototype.predicate = function(predicate) {
1528
    this.predicates.push(predicate);
1529
1530
    if (predicate.needContextPosition ||
1531
        predicate.datatype == 'number'||
1532
        predicate.datatype == 'void') {
1533
        this.needContextPosition = true;
1534
    }
1535
1536
    if (this._quickAttr && this.predicates.length == 1 && predicate.quickAttr) {
1537
        var attrName = predicate.attrName;
1538
/*@cc_on @if (@_jscript)
1539
        this.attrName = attrName.toLowerCase();
1540
@else @*/
1541
        this.attrName = attrName;
1542
/*@end @*/
1543
        this.attrValueExpr = predicate.attrValueExpr;
1544
        this.quickAttr = true;
1545
    }
1546
};
1547
1548
Step.prototype.show = function(indent) {
1549
    indent = indent || '';
1550
    var t = '';
1551
    t += indent + 'step: ' + '\n';
1552
    indent += '    ';
1553
    if (this.axis) t += indent + 'axis: ' + this.axis + '\n';
1554
    t += this.test.show(indent);
1555
    if (this.predicates.length) {
1556
        t += indent + 'predicates: ' + '\n';
1557
        indent += '    ';
1558
        for (var i = 0; i < this.predicates.length; i ++) {
1559
            t += this.predicates[i].show(indent);
1560
        }
1561
    }
1562
    return t;
1563
};
1564
1565
1566
1567
/**
1568
 * NodeType
1569
 */
1570
if (!window.NodeType && window.defaultConfig)
1571
    window.NodeType = null;
1572
    
1573
NodeType = function(name, literal) {
1574
    this.name = name;
1575
    this.literal = literal;
1576
1577
    switch (name) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
1578
        case 'comment':
1579
            this.type = 8;
1580
            break;
1581
        case 'text':
1582
            this.type = 3;
1583
            break;
1584
        case 'processing-instruction':
1585
            this.type = 7;
1586
            break;
1587
        case 'node':
1588
            this.type = 0;
1589
            break;
1590
    }
1591
};
1592
1593
NodeType.types = {
1594
    'comment':1, 'text':1, 'processing-instruction':1, 'node':1
1595
};
1596
1597
NodeType.parse = function(lexer) {
1598
    var type, literal, ch;
1599
    type = lexer.next();
1600
    lexer.next();
1601
    if (lexer.empty()) {
1602
        throw Error('bad nodetype');
1603
    }
1604
    ch = lexer.peek().charAt(0);
1605
    if (ch == '"' || ch == "'") {
1606
        literal = Literal.parse(lexer);
1607
    }
1608
    if (lexer.empty()) {
1609
        throw Error('bad nodetype');
1610
    }
1611
    if (lexer.next() != ')') {
1612
        lexer.back();
1613
        throw Error('bad token ' + lexer.next());
1614
    }
1615
    return new NodeType(type, literal);
0 ignored issues
show
Bug introduced by
The variable literal does not seem to be initialized in case ch == """ || ch == "'" on line 1605 is false. Are you sure this can never be the case?
Loading history...
1616
};
1617
1618
NodeType.prototype = new BaseExpr();
1619
1620
NodeType.prototype.notOnlyElement = true;
1621
1622
NodeType.prototype.match = function(node) {
1623
    return !this.type || this.type == node.nodeType;
1624
};
1625
1626
NodeType.prototype.show = function(indent) {
1627
    indent = indent || '';
1628
    var t = '';
1629
    t += indent + 'nodetype: ' + this.type + '\n';
1630
    if (this.literal) {
1631
        indent += '    ';
1632
        t += this.literal.show(indent);
1633
    }
1634
    return t;
1635
};
1636
1637
1638
/**
1639
 * NodeType
1640
 */
1641
if (!window.NameTest && window.defaultConfig)
1642
    window.NameTest = null;
1643
1644
NameTest = function(name) {
1645
    this.name = name.toLowerCase();
1646
};
1647
1648
NameTest.parse = function(lexer) {
1649
    if (lexer.peek() != '*' &&  lexer.peek(1) == ':' && lexer.peek(2) == '*') {
1650
        return new NameTest(lexer.next() + lexer.next() + lexer.next());
1651
    }
1652
    return new NameTest(lexer.next());
1653
};
1654
1655
NameTest.prototype = new BaseExpr();
1656
1657
NameTest.prototype.match = function(node) {
1658
    var type = node.nodeType;
1659
1660
    if (type == 1 || type == 2) {
1661
        if (this.name == '*' || this.name == node.nodeName.toLowerCase()) {
1662
            return true;
1663
        }
1664
    }
1665
    return false;
1666
};
1667
1668
NameTest.prototype.show = function(indent) {
1669
    indent = indent || '';
1670
    var t = '';
1671
    t += indent + 'nametest: ' + this.name + '\n';
1672
    return t;
1673
};
1674
1675
1676
/**
1677
 * class: VariableRefernce
1678
 */
1679
if (!window.VariableReference && window.defaultConfig)
1680
    window.VariableReference = null;
1681
    
1682
VariableReference = function(name) {
1683
    this.name = name.substring(1);
1684
};
1685
1686
1687
VariableReference.parse = function(lexer) {
1688
    var token = lexer.next();
1689
    if (token.length < 2) {
1690
        throw Error('unnamed variable reference');
1691
    }
1692
    return new VariableReference(token)
1693
};
1694
1695
VariableReference.prototype = new BaseExpr();
1696
1697
VariableReference.prototype.datatype = 'void';
1698
1699
VariableReference.prototype.show = function(indent) {
1700
    indent = indent || '';
1701
    var t = '';
1702
    t += indent + 'variable: ' + this.name + '\n';
1703
    return t;
1704
};
1705
1706
1707
/**
1708
 * class: Literal
1709
 */
1710
if (!window.Literal && window.defaultConfig)
1711
    window.Literal = null;
1712
1713
Literal = function(text) {
1714
    this.text = text.substring(1, text.length - 1);
1715
};
1716
1717
Literal.parse = function(lexer) {
1718
    var token = lexer.next();
1719
    if (token.length < 2) {
1720
        throw Error('unclosed literal string');
1721
    }
1722
    return new Literal(token)
1723
};
1724
1725
Literal.prototype = new BaseExpr();
1726
1727
Literal.prototype.datatype = 'string';
1728
1729
Literal.prototype.evaluate = function(ctx) {
0 ignored issues
show
Unused Code introduced by
The parameter ctx 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...
1730
    return this.text;
1731
};
1732
1733
Literal.prototype.show = function(indent) {
1734
    indent = indent || '';
1735
    var t = '';
1736
    t += indent + 'literal: ' + this.text + '\n';
1737
    return t;
1738
};
1739
1740
1741
/**
1742
 * class: Number
1743
 */
1744
if (!window.Number && window.defaultConfig)
1745
    window.Number = null;
1746
1747
Number = function(digit) {
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Number. This makes code hard to read, consider using a different name.
Loading history...
1748
    this.digit = +digit;
1749
};
1750
1751
1752
Number.parse = function(lexer) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Number. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
1753
    return new Number(lexer.next());
1754
};
1755
1756
Number.prototype = new BaseExpr();
1757
1758
Number.prototype.datatype = 'number';
1759
1760
Number.prototype.evaluate = function(ctx) {
0 ignored issues
show
Unused Code introduced by
The parameter ctx 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...
Compatibility Best Practice introduced by
You are extending the built-in type Number. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
1761
    return this.digit;
1762
};
1763
1764
Number.prototype.show = function(indent) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Number. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
1765
    indent = indent || '';
1766
    var t = '';
1767
    t += indent + 'number: ' + this.digit + '\n';
1768
    return t;
1769
};
1770
1771
1772
/**
1773
 * class: FunctionCall
1774
 */
1775
if (!window.FunctionCall && window.defaultConfig)
1776
    window.FunctionCall = null;
1777
1778
FunctionCall = function(name) {
1779
    var info = FunctionCall.funcs[name];
1780
    if (!info)
1781
        throw Error(name +' is not a function');
1782
1783
    this.name = name;
1784
    this.func = info[0];
1785
    this.args = [];
1786
1787
    this.datatype = info[1];
1788
1789
    if (info[2]) {
1790
        this.needContextPosition = true;
1791
    }
1792
1793
    this.needContextNodeInfo = info[3];
1794
    this.needContextNode = this.needContextNodeInfo[0]
1795
};
1796
1797
FunctionCall.funcs = {
1798
1799
    // Original Function
1800
    'context-node': [function() {
1801
        if (arguments.length != 0) {
1802
            throw Error('Function context-node expects ()');
1803
        }
1804
        var ns;
1805
        ns = new NodeSet();
1806
        ns.push(this.node);
1807
        return ns;
1808
    }, 'nodeset', false, [true]],
1809
1810
    // Original Function
1811
    'root-node': [function() {
1812
        if (arguments.length != 0) {
1813
            throw Error('Function root-node expects ()');
1814
        }
1815
        var ns, ctxn;
1816
        ns = new NodeSet();
1817
        ctxn = this.node;
1818
        if (ctxn.nodeType == 9)
1819
            ns.push(ctxn);
1820
        else
1821
            ns.push(ctxn.ownerDocument);
1822
        return ns;
1823
    }, 'nodeset', false, []],
1824
1825
    last: [function() {
1826
        if (arguments.length != 0) {
1827
            throw Error('Function last expects ()');
1828
        }
1829
        return this.last;
1830
    }, 'number', true, []],
1831
1832
    position: [function() {
1833
        if (arguments.length != 0) {
1834
            throw Error('Function position expects ()');
1835
        }
1836
        return this.position;
1837
    }, 'number', true, []],
1838
1839
    count: [function(ns) {
1840
        if (arguments.length != 1 || !(ns = ns.evaluate(this)).isNodeSet) {
1841
            throw Error('Function count expects (nodeset)');
1842
        }
1843
        return ns.length;
1844
    }, 'number', false, []],
1845
1846
    id: [function(s) {
1847
        var ids, ns, i, id, elm, ctxn, doc;
1848
        if (arguments.length != 1) {
1849
            throw Error('Function id expects (object)');
1850
        }
1851
        ctxn = this.node;
1852
        if (ctxn.nodeType == 9)
1853
            doc = ctxn;
1854
        else
1855
            doc = ctxn.ownerDocument;
1856
/*@cc_on
1857
        all = doc.all;
1858
@*/
1859
        s = s.string(this);
1860
        ids = s.split(/\s+/);
1861
        ns = new NodeSet();
1862
        for (i = 0, l = ids.length; i < l; i ++) {
0 ignored issues
show
Bug introduced by
The variable l seems to not be initialized for all possible execution paths.
Loading history...
1863
            id = ids[i];
1864
1865
/*@cc_on @if (@_jscript)
1866
            elm = all[id];
1867
            if (elm) {
1868
                if ((!elm.length || elm.nodeType) && id == elm.id) {
1869
                    ns.push(elm)
1870
                }
1871
                else if (elm.length) {
1872
                    var elms = elm;
1873
                    for (var j = 0, l0 = elms.length; j < l0; j ++) {
1874
                        var elem = elms[j];
1875
                        if (id == elem.id) {
1876
                            ns.push(elem);
1877
                            break;
1878
                        }
1879
                    }
1880
                }
1881
            }
1882
@else @*/
1883
            elm = doc.getElementById(id);
1884
            if (uai.opera && elm && elm.id != id) {
1885
                var elms = doc.getElementsByName(id);
1886
                for (var j = 0, l0 = elms.length; j < l0; j ++) {
1887
                    elm = elms[j];
1888
                    if (elm.id == id) {
1889
                        ns.push(elm);
1890
                    }
1891
                }
1892
            }
1893
            else {
1894
                if (elm) ns.push(elm)
1895
            }
1896
/*@end @*/
1897
1898
        }
1899
        ns.isSorted = false;
1900
        return ns;
1901
    }, 'nodeset', false, []],
1902
1903
    'local-name': [function(ns) {
1904
        var nd;
1905
        switch (arguments.length) {
1906
            case 0:
1907
                nd = this.node;
1908
                break;
1909
            case 1:
1910
                if ((ns = ns.evaluate(this)).isNodeSet) {
1911
                    nd = ns.first();
1912
                    break;
1913
                }
0 ignored issues
show
introduced by
This node falls through to the next case due to this statement. Please add a comment either directly below this line or between the cases to explain.
Loading history...
1914
            default:
1915
                throw Error('Function local-name expects (nodeset?)');
1916
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1917
        }
1918
        return '' + nd.nodeName.toLowerCase();
1919
    }, 'string', false, [true, false]],
1920
1921
    name: [function(ns) {
0 ignored issues
show
Unused Code introduced by
The parameter ns 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...
1922
        // not implemented
1923
        return FunctionCall.funcs['local-name'][0].apply(this, arguments);
1924
    }, 'string', false, [true, false]],
1925
1926
    'namespace-uri': [function(ns) {
0 ignored issues
show
Unused Code introduced by
The parameter ns 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...
1927
        // not implemented
1928
        return '';
1929
    }, 'string', false, [true, false]],
1930
1931
    string: [function(s) {
1932
        switch (arguments.length) {
1933
            case 0:
1934
                s = NodeUtil.to('string', this.node);
1935
                break;
1936
            case 1:
1937
                s = s.string(this);
1938
                break;
1939
            default:
1940
                throw Error('Function string expects (object?)');
1941
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1942
        }
1943
        return s;
1944
    }, 'string', false, [true, false]],
1945
1946
    concat: [function(s1, s2) {
0 ignored issues
show
Unused Code introduced by
The parameter s1 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...
Unused Code introduced by
The parameter s2 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...
1947
        if (arguments.length < 2) {
1948
            throw Error('Function concat expects (string, string[, ...])');
1949
        }
1950
        for (var t = '', i = 0, l = arguments.length; i < l; i ++) {
1951
            t += arguments[i].string(this);
1952
        }
1953
        return t;
1954
    }, 'string', false, []],
1955
1956
    'starts-with': [function(s1, s2) {
1957
        if (arguments.length != 2) {
1958
            throw Error('Function starts-with expects (string, string)');
1959
        }
1960
        s1 = s1.string(this);
1961
        s2 = s2.string(this);
1962
        return s1.indexOf(s2) == 0;
1963
    }, 'boolean', false, []],
1964
1965
    contains: [function(s1, s2) {
1966
        if (arguments.length != 2) {
1967
            throw Error('Function contains expects (string, string)');
1968
        }
1969
        s1 = s1.string(this);
1970
        s2 = s2.string(this);
1971
        return s1.indexOf(s2) != -1;
1972
    }, 'boolean', false, []],
1973
1974
    substring: [function(s, n1, n2) {
1975
        var a1, a2;
1976
        s = s.string(this);
1977
        n1 = n1.number(this);
1978
        switch (arguments.length) {
1979
            case 2:
1980
                n2 = s.length - n1 + 1;
1981
                break;
1982
            case 3:
1983
                n2 = n2.number(this);
1984
                break;
1985
            default:
1986
                throw Error('Function substring expects (string, string)');
1987
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1988
        }
1989
        n1 = Math.round(n1);
1990
        n2 = Math.round(n2);
1991
        a1 = n1 - 1;
1992
        a2 = n1 + n2 - 1;
1993
        if (a2 == Infinity) {
1994
            return s.substring(a1 < 0 ? 0 : a1);
1995
        }
1996
        else {
1997
            return s.substring(a1 < 0 ? 0 : a1, a2)
1998
        }
1999
    }, 'string', false, []],
2000
2001
    'substring-before': [function(s1, s2) {
2002
        var n;
2003
        if (arguments.length != 2) {
2004
            throw Error('Function substring-before expects (string, string)');
2005
        }
2006
        s1 = s1.string(this);
2007
        s2 = s2.string(this);
2008
        n = s1.indexOf(s2);
2009
        if (n == -1) return '';
2010
        return s1.substring(0, n);
2011
    }, 'string', false, []],
2012
2013
    'substring-after': [function(s1, s2) {
2014
        if (arguments.length != 2) {
2015
            throw Error('Function substring-after expects (string, string)');
2016
        }
2017
        s1 = s1.string(this);
2018
        s2 = s2.string(this);
2019
        var n = s1.indexOf(s2);
2020
        if (n == -1) return '';
2021
        return s1.substring(n + s2.length);
2022
    }, 'string', false, []],
2023
2024
    'string-length': [function(s) {
2025
        switch (arguments.length) {
2026
            case 0:
2027
                s = NodeUtil.to('string', this.node);
2028
                break;
2029
            case 1:
2030
                s = s.string(this);
2031
                break;
2032
            default:
2033
                throw Error('Function string-length expects (string?)');
2034
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
2035
        }
2036
        return s.length;
2037
    }, 'number', false, [true, false]],
2038
2039
    'normalize-space': [function(s) {
2040
        switch (arguments.length) {
2041
            case 0:
2042
                s = NodeUtil.to('string', this.node);
2043
                break;
2044
            case 1:
2045
                s = s.string(this);
2046
                break;
2047
            default:
2048
                throw Error('Function normalize-space expects (string?)');
2049
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
2050
        }
2051
        return s.replace(/\s+/g, ' ').replace(/^ /, '').replace(/ $/, '');
2052
    }, 'string', false, [true, false]],
2053
2054
    translate: [function(s1, s2, s3) {
2055
        if (arguments.length != 3) {
2056
            throw Error('Function translate expects (string, string, string)');
2057
        }
2058
        s1 = s1.string(this);
2059
        s2 = s2.string(this);
2060
        s3 = s3.string(this);
2061
2062
        var map = [];
2063
        for (var i = 0, l = s2.length; i < l; i ++) {
2064
            var ch = s2.charAt(i);
2065
            if (!map[ch]) map[ch] = s3.charAt(i) || '';
2066
        }
2067
        for (var t = '', i = 0, l = s1.length; i < l; i ++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2063. 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...
Comprehensibility Naming Best Practice introduced by
The variable l already seems to be declared on line 2063. 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...
2068
            var ch = s1.charAt(i);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable ch already seems to be declared on line 2064. 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...
2069
            var replace = map[ch]
2070
            t += (replace != undefined) ? replace : ch;
2071
        }
2072
        return t;
2073
    }, 'string', false, []],
2074
2075
    'boolean': [function(b) {
2076
        if (arguments.length != 1) {
2077
            throw Error('Function boolean expects (object)');
2078
        }
2079
        return b.bool(this)
2080
    }, 'boolean', false, []],
2081
2082
    not: [function(b) {
2083
        if (arguments.length != 1) {
2084
            throw Error('Function not expects (object)');
2085
        }
2086
        return !b.bool(this)
2087
    }, 'boolean', false, []],
2088
2089
    'true': [function() {
2090
        if (arguments.length != 0) {
2091
            throw Error('Function true expects ()');
2092
        }
2093
        return true;
2094
    }, 'boolean', false, []],
2095
2096
    'false': [function() {
2097
        if (arguments.length != 0) {
2098
            throw Error('Function false expects ()');
2099
        }
2100
        return false;
2101
    }, 'boolean', false, []],
2102
2103
    lang: [function(s) {
0 ignored issues
show
Unused Code introduced by
The parameter s 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...
2104
        // not implemented
2105
        return false;
2106
    }, 'boolean', false, []],
2107
2108
    number: [function(n) {
2109
        switch (arguments.length) {
2110
            case 0:
2111
                n = NodeUtil.to('number', this.node);
2112
                break;
2113
            case 1:
2114
                n = n.number(this);
2115
                break;
2116
            default:
2117
                throw Error('Function number expects (object?)');
2118
                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
2119
        }
2120
        return n;
2121
    }, 'number', false, [true, false]],
2122
2123
    sum: [function(ns) {
2124
        var nodes, n, i, l;
2125
        if (arguments.length != 1 || !(ns = ns.evaluate(this)).isNodeSet) {
2126
            throw Error('Function sum expects (nodeset)');
2127
        }
2128
        nodes = ns.list();
2129
        n = 0;
2130
        for (i = 0, l = nodes.length; i < l; i ++) {
2131
            n += NodeUtil.to('number', nodes[i]);
2132
        }
2133
        return n;
2134
    }, 'number', false, []],
2135
2136
    floor: [function(n) {
2137
        if (arguments.length != 1) {
2138
            throw Error('Function floor expects (number)');
2139
        }
2140
        n = n.number(this);
2141
        return Math.floor(n);
2142
    }, 'number', false, []],
2143
2144
    ceiling: [function(n) {
2145
        if (arguments.length != 1) {
2146
            throw Error('Function ceiling expects (number)');
2147
        }
2148
        n = n.number(this);
2149
        return Math.ceil(n);
2150
    }, 'number', false, []],
2151
2152
    round: [function(n) {
2153
        if (arguments.length != 1) {
2154
            throw Error('Function round expects (number)');
2155
        }
2156
        n = n.number(this);
2157
        return Math.round(n);
2158
    }, 'number', false, []]
2159
};
2160
2161
FunctionCall.parse = function(lexer) {
2162
    var expr, func = new FunctionCall(lexer.next());
2163
    lexer.next();
2164
    while (lexer.peek() != ')') {
2165
        if (lexer.empty()) {
2166
            throw Error('missing function argument list');
2167
        }
2168
        expr = BinaryExpr.parse(lexer);
2169
        func.arg(expr);
2170
        if (lexer.peek() != ',') break;
2171
        lexer.next();
2172
    }
2173
    if (lexer.empty()) {
2174
        throw Error('unclosed function argument list');
2175
    }
2176
    if (lexer.next() != ')') {
2177
        lexer.back();
2178
        throw Error('bad token: ' + lexer.next());
2179
    }
2180
    return func
2181
};
2182
2183
FunctionCall.prototype = new BaseExpr();
2184
2185
FunctionCall.prototype.evaluate = function (ctx) {
2186
    return this.func.apply(ctx, this.args);
2187
};
2188
2189
FunctionCall.prototype.arg = function(arg) {
2190
    this.args.push(arg);
2191
2192
    if (arg.needContextPosition) {
2193
        this.needContextPosition = true;
2194
    }
2195
2196
    var args = this.args;
2197
    if (arg.needContextNode) {
2198
        args.needContexNode = true;
2199
    }
2200
    this.needContextNode = args.needContextNode ||
2201
                            this.needContextNodeInfo[args.length];
2202
};
2203
2204
FunctionCall.prototype.show = function(indent) {
2205
    indent = indent || '';
2206
    var t = '';
2207
    t += indent + 'function: ' + this.name + '\n';
2208
    indent += '    ';
2209
2210
    if (this.args.length) {
2211
        t += indent + 'arguments: ' + '\n';
2212
        indent += '    ';
2213
        for (var i = 0; i < this.args.length; i ++) {
2214
            t += this.args[i].show(indent);
2215
        }
2216
    }
2217
2218
    return t;
2219
};
2220
2221
2222
/*@cc_on @if (@_jscript)
2223
var NodeWrapper = function(node, sourceIndex, subIndex, attributeName) {
2224
    this.node = node;
2225
    this.nodeType = node.nodeType;
2226
    this.sourceIndex = sourceIndex;
2227
    this.subIndex = subIndex;
2228
    this.attributeName = attributeName || '';
2229
    this.order = String.fromCharCode(sourceIndex) + String.fromCharCode(subIndex) + attributeName;
2230
};
2231
2232
NodeWrapper.prototype.toString = function() {
2233
    return this.order;
2234
};
2235
@else @*/
2236
var NodeID = {
2237
    uuid: 1,
2238
    get: function(node) {
2239
        return node.__jsxpath_id__ || (node.__jsxpath_id__ = this.uuid++);
2240
    }
2241
};
2242
/*@end @*/
2243
2244
if (!window.NodeSet && window.defaultConfig)
2245
    window.NodeSet = null;
2246
    
2247
NodeSet = function() {
2248
    this.length = 0;
2249
    this.nodes = [];
2250
    this.seen = {};
2251
    this.idIndexMap = null;
2252
    this.reserveDels = [];
2253
};
2254
2255
NodeSet.prototype.isNodeSet = true;
2256
NodeSet.prototype.isSorted = true;
2257
2258
/*@_cc_on
2259
NodeSet.prototype.shortcut = true;
2260
@*/
2261
2262
NodeSet.prototype.merge = function(nodeset) {
2263
    this.isSorted = false;
2264
    if (nodeset.only) {
2265
        return this.push(nodeset.only);
2266
    }
2267
2268
    if (this.only){
2269
        var only = this.only;
2270
        delete this.only;
2271
        this.push(only);
2272
        this.length --;
2273
    }
2274
2275
    var nodes = nodeset.nodes;
2276
    for (var i = 0, l = nodes.length; i < l; i ++) {
2277
        this._add(nodes[i]);
2278
    }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2279
};
2280
2281
NodeSet.prototype.sort = function() {
2282
    if (this.only) return;
2283
    if (this.sortOff) return;
2284
2285
    if (!this.isSorted) {
2286
        this.isSorted = true;
2287
        this.idIndexMap = null;
2288
2289
/*@cc_on
2290
        if (this.shortcut) {
2291
            this.nodes.sort();
2292
        }
2293
        else {
2294
            this.nodes.sort(function(a, b) {
2295
                var result;
2296
                result = a.sourceIndex - b.sourceIndex;
2297
                if (result == 0)
2298
                    return a.subIndex - a.subIndex;
2299
                else
2300
                    return result;
2301
            });
2302
        }
2303
        return;
2304
@*/
2305
        var nodes = this.nodes;
2306
        nodes.sort(function(a, b) {
2307
            if (a == b) return 0;
2308
2309
            if (a.compareDocumentPosition) {
2310
                var result = a.compareDocumentPosition(b);
2311
                if (result & 2) return 1;
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
2312
                if (result & 4) return -1;
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
2313
                return 0;
2314
            }
2315
            else {
2316
                var node1 = a, node2 = b, ancestor1 = a, ancestor2 = b, deep1 = 0, deep2 = 0;
2317
2318
                while(ancestor1 = ancestor1.parentNode) deep1 ++;
2319
                while(ancestor2 = ancestor2.parentNode) deep2 ++;
2320
2321
                // same deep
2322
                if (deep1 > deep2) {
2323
                    while (deep1-- != deep2) node1 = node1.parentNode;
2324
                    if (node1 == node2) return 1;
2325
                }
2326
                else if (deep2 > deep1) {
2327
                    while (deep2-- != deep1) node2 = node2.parentNode;
2328
                    if (node1 == node2) return -1;
2329
                }
2330
2331
                while ((ancestor1 = node1.parentNode) != (ancestor2 = node2.parentNode)) {
2332
                    node1 = ancestor1;
2333
                    node2 = ancestor2;
2334
                }
2335
2336
                // node1 is node2's sibling
2337
                while (node1 = node1.nextSibling) if (node1 == node2) return -1;
2338
2339
                return 1;
2340
            }
2341
        });
2342
    }
2343
};
2344
2345
2346
/*@cc_on @if (@_jscript)
2347
NodeSet.prototype.sourceOffset = 1;
2348
NodeSet.prototype.subOffset = 2;
2349
NodeSet.prototype.createWrapper = function(node) {
2350
    var parent, child, attributes, attributesLength, sourceIndex, subIndex, attributeName;
2351
2352
    sourceIndex = node.sourceIndex;
2353
2354
    if (typeof sourceIndex != 'number') {
2355
        type = node.nodeType;
2356
        switch (type) {
2357
            case 2:
2358
                parent = node.parentNode;
2359
                sourceIndex = node.parentSourceIndex;
2360
                subIndex = -1;
2361
                attributeName = node.nodeName;
2362
                break;
2363
            case 9:
2364
                subIndex = -2;
2365
                sourceIndex = -1;
2366
                break;
2367
            default:
2368
                child = node;
2369
                subIndex = 0;
2370
                do {
2371
                    subIndex ++;
2372
                    sourceIndex = child.sourceIndex;
2373
                    if (sourceIndex) {
2374
                        parent = child;
2375
                        child = child.lastChild;
2376
                        if (!child) {
2377
                            child = parent;
2378
                            break;
2379
                        }
2380
                        subIndex ++;
2381
                    }
2382
                } while (child = child.previousSibling);
2383
                if (!sourceIndex) {
2384
                    sourceIndex = node.parentNode.sourceIndex;
2385
                }
2386
                break;
2387
        }
2388
    }
2389
    else {
2390
        subIndex = -2;
2391
    }
2392
2393
    sourceIndex += this.sourceOffset;
2394
    subIndex += this.subOffset;
2395
2396
    return new NodeWrapper(node, sourceIndex, subIndex, attributeName);
2397
};
2398
2399
NodeSet.prototype.reserveDelBySourceIndexAndSubIndex = function(sourceIndex, subIndex, offset, reverse) {
2400
    var map = this.createIdIndexMap();
2401
    var index;
2402
    if ((map = map[sourceIndex]) && (index = map[subIndex])) {
2403
        if (reverse && (this.length - offset - 1) > index || !reverse && offset < index) {
2404
            var obj = {
2405
                value: index,
2406
                order: String.fromCharCode(index),
2407
                toString: function() { return this.order },
2408
                valueOf: function() { return this.value }
2409
            };
2410
            this.reserveDels.push(obj);
2411
        }
2412
    }
2413
};
2414
@else @*/
2415
NodeSet.prototype.reserveDelByNodeID = function(id, offset, reverse) {
2416
    var map = this.createIdIndexMap();
2417
    var index;
2418
    if (index = map[id]) {
2419
        if (reverse && (this.length - offset - 1) > index || !reverse && offset < index) {
2420
            var obj = {
2421
                value: index,
2422
                order: String.fromCharCode(index),
2423
                toString: function() { return this.order },
2424
                valueOf: function() { return this.value }
2425
            };
2426
            this.reserveDels.push(obj);
2427
        }
2428
    }
2429
};
2430
/*@end @*/
2431
2432
NodeSet.prototype.reserveDelByNode = function(node, offset, reverse) {
2433
/*@cc_on @if (@_jscript)
2434
    node = this.createWrapper(node);
2435
    this.reserveDelBySourceIndexAndSubIndex(node.sourceIndex, node.subIndex, offset, reverse);
2436
@else @*/
2437
    this.reserveDelByNodeID(NodeID.get(node), offset, reverse);
2438
/*@end @*/
2439
};
2440
2441
NodeSet.prototype.doDel = function() {
2442
    if (!this.reserveDels.length) return;
2443
2444
    if (this.length < 0x10000) {
2445
        var dels = this.reserveDels.sort(function(a, b) { return b - a });
2446
    }
2447
    else {
2448
        var dels = this.reserveDels.sort(function(a, b) { return b - a });
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable dels already seems to be declared on line 2445. 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...
2449
    }
2450
    for (var i = 0, l = dels.length; i < l; i ++) {
2451
        this.del(dels[i]);
2452
    }
2453
    this.reserveDels = [];
2454
    this.idIndexMap = null;
2455
};
2456
2457
NodeSet.prototype.createIdIndexMap = function() {
2458
    if (this.idIndexMap) {
2459
        return this.idIndexMap;
2460
    }
2461
    else {
2462
        var map = this.idIndexMap = {};
2463
        var nodes = this.nodes;
2464
        for (var i = 0, l = nodes.length; i < l; i ++) {
2465
            var node = nodes[i];
2466
/*@cc_on @if (@_jscript)
2467
            var sourceIndex = node.sourceIndex;
2468
            var subIndex = node.subIndex;
2469
            if (!map[sourceIndex]) map[sourceIndex] = {};
2470
            map[sourceIndex][subIndex] = i;
2471
@else @*/
2472
            var id = NodeID.get(node);
2473
            map[id] = i;
2474
/*@end @*/
2475
        }
2476
        return map;
2477
    }
2478
};
2479
2480
NodeSet.prototype.del = function(index) {
2481
    this.length --;
2482
    if (this.only) {
2483
        delete this.only;
2484
    }
2485
    else {  
2486
        var node = this.nodes.splice(index, 1)[0];
2487
2488
        if (this._first == node) {
2489
            delete this._first;
2490
            delete this._firstSourceIndex;
2491
            delete this._firstSubIndex;
2492
        }
2493
2494
/*@cc_on @if (@_jscript)
2495
        delete this.seen[node.sourceIndex][node.subIndex];
2496
@else @*/
2497
        delete this.seen[NodeID.get(node)];
2498
/*@end @*/
2499
    }
2500
};
2501
2502
2503
NodeSet.prototype.delDescendant = function(elm, offset) {
2504
    if (this.only) return;
2505
    var nodeType = elm.nodeType;
2506
    if (nodeType != 1 && nodeType != 9) return;
2507
    if (uai.applewebkit2) return;
2508
2509
    // element || document
2510
    if (!elm.contains) {
2511
        if (nodeType == 1) {
2512
            var _elm = elm;
2513
            elm = {
2514
                contains: function(node) {
2515
                    return node.compareDocumentPosition(_elm) & 8;
2516
                }
2517
            };
2518
        }
2519
        else {
2520
            // document
2521
            elm = {
2522
                contains: function() {
2523
                    return true;
2524
                }
2525
            };
2526
        }
2527
    }
2528
2529
    var nodes = this.nodes;
2530
    for (var i = offset + 1; i < nodes.length; i ++) {
2531
2532
/*@cc_on @if (@_jscript)
2533
        if (nodes[i].node.nodeType == 1 && elm.contains(nodes[i].node)) {
2534
@else @*/
2535
        if (elm.contains(nodes[i])) {
2536
/*@end @*/
2537
            this.del(i);
2538
            i --;
2539
        }
2540
    }
2541
};
2542
2543
NodeSet.prototype._add = function(node, reverse) {
2544
2545
/*@cc_on @if (@_jscript)
2546
2547
    var first, firstSourceIndex, firstSubIndex, sourceIndex, subIndex, attributeName;
2548
2549
    sourceIndex = node.sourceIndex;
2550
    subIndex = node.subIndex;
2551
    attributeName = node.attributeName;
2552
    seen = this.seen;
2553
2554
    seen = seen[sourceIndex] || (seen[sourceIndex] = {});
2555
2556
    if (node.nodeType == 2) {
2557
        seen = seen[subIndex] || (seen[subIndex] = {});
2558
        if (seen[attributeName]) {
2559
            return true;
2560
        }
2561
        seen[attributeName] = true;
2562
    }
2563
    else {
2564
        if (seen[subIndex]) {
2565
            return true;
2566
        }
2567
        seen[subIndex] = true;
2568
    }
2569
2570
    if (sourceIndex >= 0x10000 || subIndex >= 0x10000) {
2571
        this.shortcut = false;
2572
    }
2573
2574
    // if this._first is undefined and this.nodes is not empty
2575
    // then first node shortcut is disabled.
2576
    if (this._first || this.nodes.length == 0) {
2577
        first = this._first;
2578
        firstSourceIndex = this._firstSourceIndex;
2579
        firstSubIndex = this._firstSubIndex;
2580
        if (!first || firstSourceIndex > sourceIndex || (firstSourceIndex == sourceIndex && firstSubIndex > subIndex)) {
2581
            this._first = node;
2582
            this._firstSourceIndex = sourceIndex;
2583
            this._firstSubIndex = subIndex
2584
        }
2585
    }
2586
2587
@else @*/
2588
2589
    var seen = this.seen;
2590
    var id = NodeID.get(node);
2591
    if (seen[id]) return true;
2592
    seen[id] = true;
2593
2594
/*@end @*/
2595
2596
    this.length++;
2597
    if (reverse) 
2598
        this.nodes.unshift(node);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2599
    else
2600
        this.nodes.push(node);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2601
};
2602
2603
2604
NodeSet.prototype.unshift = function(node) {
2605
    if (!this.length) {
2606
        this.length ++;
2607
        this.only = node;
2608
        return
2609
    }
2610
    if (this.only){
2611
        var only = this.only;
2612
        delete this.only;
2613
        this.unshift(only);
2614
        this.length --;
2615
    }
2616
/*@cc_on
2617
    node = this.createWrapper(node);
2618
@*/
2619
    return this._add(node, true);
2620
};
2621
2622
2623
NodeSet.prototype.push = function(node) {
2624
    if (!this.length) {
2625
        this.length ++;
2626
        this.only = node;
2627
        return;
2628
    }
2629
    if (this.only) {
2630
        var only = this.only;
2631
        delete this.only;
2632
        this.push(only);
2633
        this.length --;
2634
    }
2635
/*@cc_on
2636
    node = this.createWrapper(node);
2637
@*/
2638
    return this._add(node);
2639
};
2640
2641
NodeSet.prototype.first = function() {
2642
    if (this.only) return this.only;
2643
/*@cc_on
2644
    if (this._first) return this._first.node;
2645
    if (this.nodes.length > 1) this.sort();
2646
    var node = this.nodes[0];
2647
    return node ? node.node : undefined;
2648
@*/
2649
    if (this.nodes.length > 1) this.sort();
2650
    return this.nodes[0];
2651
};
2652
2653
NodeSet.prototype.list = function() {
2654
    if (this.only) return [this.only];
2655
    this.sort();
2656
/*@cc_on
2657
    var i, l, nodes, results;
2658
    nodes = this.nodes;
2659
    results = [];
2660
    for (i = 0, l = nodes.length; i < l; i ++) {
2661
        results.push(nodes[i].node);
2662
    }
2663
    return results;
2664
@*/
2665
    return this.nodes;
2666
};
2667
2668
NodeSet.prototype.string = function() {
2669
    var node = this.only || this.first();
2670
    return node ? NodeUtil.to('string', node) : '';
2671
};
2672
2673
NodeSet.prototype.bool = function() {
2674
    return !! (this.length || this.only);
2675
};
2676
2677
NodeSet.prototype.number = function() {
2678
    return + this.string();
2679
};
2680
2681
NodeSet.prototype.iterator = function(reverse) {
2682
    this.sort();
2683
    var nodeset = this;
2684
2685
    if (!reverse) {
2686
        var count = 0;
2687
        return function() {
2688
            if (nodeset.only && count++ == 0) return nodeset.only;
2689
/*@cc_on @if(@_jscript)
2690
            var wrapper = nodeset.nodes[count++];
2691
            if (wrapper) return wrapper.node;
2692
            return undefined;
2693
@else @*/
2694
            return nodeset.nodes[count++];
2695
/*@end @*/
2696
        };
2697
    }
2698
    else {
2699
        var count = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable count already seems to be declared on line 2686. 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...
2700
        return function() {
2701
            var index = nodeset.length - (count++) - 1;
2702
            if (nodeset.only && index == 0) return nodeset.only;
2703
/*@cc_on @if(@_jscript)
2704
            var wrapper = nodeset.nodes[index];
2705
            if (wrapper) return wrapper.node;
2706
            return undefined;
2707
@else @*/
2708
            return nodeset.nodes[index];
2709
/*@end @*/
2710
        };
2711
    }
2712
};
2713
2714
2715
var install = function(win) {
2716
2717
    win = win || this;
2718
    var doc = win.document;
2719
    var undefined = win.undefined;
0 ignored issues
show
Unused Code introduced by
The assignment to variable undefined seems to be never used. Consider removing it.
Loading history...
2720
2721
    win.XPathExpression = function(expr) {
2722
        if (!expr.length) {
2723
            throw win.Error('no expression');
2724
        }
2725
        var lexer = this.lexer = Lexer(expr);
2726
        if (lexer.empty()) {
2727
            throw win.Error('no expression');
2728
        }
2729
        this.expr = BinaryExpr.parse(lexer);
2730
        if (!lexer.empty()) {
2731
            throw win.Error('bad token: ' + lexer.next());
2732
        }
2733
    };
2734
    
2735
    win.XPathExpression.prototype.evaluate = function(node, type) {
2736
        return new win.XPathResult(this.expr.evaluate(new Ctx(node)), type);
2737
    };
2738
    
2739
    win.XPathResult = function (value, type) {
2740
        if (type == 0) {
2741
            switch (typeof value) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
2742
                case 'object':  type ++; // 4
2743
                case 'boolean': type ++; // 3
2744
                case 'string':  type ++; // 2
2745
                case 'number':  type ++; // 1
2746
            }
2747
        }
2748
    
2749
        this.resultType = type;
2750
    
2751
        switch (type) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
2752
            case 1:
2753
                this.numberValue = value.isNodeSet ? value.number() : +value;
2754
                return;
2755
            case 2:
2756
                this.stringValue = value.isNodeSet ? value.string() : '' + value;
2757
                return;
2758
            case 3:
2759
                this.booleanValue = value.isNodeSet ? value.bool() : !! value;
2760
                return;
2761
            case 4: case 5: case 6: case 7:
2762
                this.nodes = value.list();
2763
                this.snapshotLength = value.length;
2764
                this.index = 0;
2765
                this.invalidIteratorState = false;
2766
                break;
2767
            case 8: case 9:
2768
                this.singleNodeValue = value.first();
2769
                return;
2770
        }
2771
    };
2772
    
2773
    win.XPathResult.prototype.iterateNext = function() { return this.nodes[this.index++] };
2774
    win.XPathResult.prototype.snapshotItem = function(i) { return this.nodes[i] };
2775
    
2776
    win.XPathResult.ANY_TYPE = 0;
2777
    win.XPathResult.NUMBER_TYPE = 1;
2778
    win.XPathResult.STRING_TYPE = 2;
2779
    win.XPathResult.BOOLEAN_TYPE = 3;
2780
    win.XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
2781
    win.XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
2782
    win.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6;
2783
    win.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7;
2784
    win.XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
2785
    win.XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
2786
    
2787
    
2788
    doc.createExpression = function(expr) {
2789
        return new win.XPathExpression(expr, null);
2790
    };
2791
    
2792
    doc.evaluate = function(expr, context, _, type) {
2793
        return doc.createExpression(expr, null).evaluate(context, type);
2794
    };
2795
};
2796
2797
var win;
2798
2799
if (config.targetFrame) {
2800
    var frame = document.getElementById(config.targetFrame);
2801
    if (frame) win = frame.contentWindow;
2802
}
2803
2804
if (config.exportInstaller) {
2805
    window.install = install;
2806
}
2807
2808
if (!config.hasNative || !config.useNative) {
2809
    install(win || window);
2810
}
2811
2812
2813
})();
2814
2815
// Thanks for reading this source code. We love JavaScript.
2816
2817