Created
October 7, 2013 12:36
-
-
Save pid/6867155 to your computer and use it in GitHub Desktop.
JavaScript-XPath 0.1.12
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* JavaScript-XPath 0.1.12 | |
* (c) 2007 Cybozu Labs, Inc. | |
* | |
* JavaScript-XPath is freely distributable under the terms of an MIT-style license. | |
* For details, see the JavaScript-XPath web site: http://coderepos.org/share/wiki/JavaScript-XPath | |
* | |
/*--------------------------------------------------------------------------*/ | |
(function () { | |
var undefined = void(0); | |
var defaultConfig = { | |
targetFrame: undefined, | |
exportInstaller: false, | |
useNative: true, | |
useInnerText: true | |
}; | |
var config; | |
if (window.jsxpath) { | |
config = window.jsxpath; | |
} | |
else { | |
var scriptElms = document.getElementsByTagName('script'); | |
var scriptElm = scriptElms[scriptElms.length - 1]; | |
var scriptSrc = scriptElm.src; | |
config = {}; | |
var scriptSrcMatchResult = scriptSrc.match(/\?(.*)$/); | |
if (scriptSrcMatchResult) { | |
var configStrings = scriptSrcMatchResult[1].split('&'); | |
for (var i = 0, l = configStrings.length; i < l; i ++) { | |
var configString = configStrings[i]; | |
var configStringSplited = configString.split('='); | |
var configName = configStringSplited[0]; | |
var configValue = configStringSplited[1]; | |
if (configValue == undefined) { | |
configValue == true; | |
} | |
else if (configValue == 'false' || /^-?\d+$/.test(configValue)) { | |
configValue = eval(configValue); | |
} | |
config[configName] = configValue; | |
} | |
} | |
} | |
for (var n in defaultConfig) { | |
if (!(n in config)) config[n] = defaultConfig[n]; | |
} | |
config.hasNative = !!(document.implementation | |
&& document.implementation.hasFeature | |
&& document.implementation.hasFeature("XPath", null)); | |
if (config.hasNative && config.useNative && !config.exportInstaller) { | |
return; | |
} | |
var BinaryExpr; | |
var FilterExpr; | |
var FunctionCall; | |
var Literal; | |
var NameTest; | |
var NodeSet; | |
var NodeType; | |
var NodeUtil; | |
var Number; | |
var PathExpr; | |
var Step; | |
var UnaryExpr; | |
var UnionExpr; | |
var VariableReference; | |
/* | |
* object: user agent identifier | |
*/ | |
var uai = new function() { | |
var ua = navigator.userAgent; | |
if (RegExp == undefined) { | |
if (ua.indexOf("Opera") >= 0) { | |
this.opera = true; | |
} else if (ua.indexOf("Netscape") >= 0) { | |
this.netscape = true; | |
} else if (ua.indexOf("Mozilla/") == 0) { | |
this.mozilla = true; | |
} else { | |
this.unknown = true; | |
} | |
if (ua.indexOf("Gecko/") >= 0) { | |
this.gecko = true; | |
} | |
if (ua.indexOf("Win") >= 0) { | |
this.windows = true; | |
} else if (ua.indexOf("Mac") >= 0) { | |
this.mac = true; | |
} else if (ua.indexOf("Linux") >= 0) { | |
this.linux = true; | |
} else if (ua.indexOf("BSD") >= 0) { | |
this.bsd = true; | |
} else if (ua.indexOf("SunOS") >= 0) { | |
this.sunos = true; | |
} | |
} | |
else { | |
/* for Trident/Tasman */ | |
/*@cc_on | |
@if (@_jscript) | |
function jscriptVersion() { | |
switch (@_jscript_version) { | |
case 3.0: return "4.0"; | |
case 5.0: return "5.0"; | |
case 5.1: return "5.01"; | |
case 5.5: return "5.5"; | |
case 5.6: | |
if ("XMLHttpRequest" in window) return "7.0"; | |
return "6.0"; | |
case 5.7: | |
return "7.0"; | |
default: return true; | |
} | |
} | |
if (@_win16 || @_win32 || @_win64) { | |
this.windows = true; | |
this.trident = jscriptVersion(); | |
} else if (@_mac || navigator.platform.indexOf("Mac") >= 0) { | |
// '@_mac' may be 'NaN' even if the platform is Mac, | |
// so we check 'navigator.platform', too. | |
this.mac = true; | |
this.tasman = jscriptVersion(); | |
} | |
if (/MSIE (\d+\.\d+)b?;/.test(ua)) { | |
this.ie = RegExp.$1; | |
this['ie' + RegExp.$1.charAt(0)] = true; | |
} | |
@else @*/ | |
/* for AppleWebKit */ | |
if (/AppleWebKit\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.applewebkit = RegExp.$1; | |
if (RegExp.$1.charAt(0) == 4) { | |
this.applewebkit2 = true; | |
} | |
else { | |
this.applewebkit3 = true; | |
} | |
} | |
/* for Gecko */ | |
else if (typeof Components == "object" && | |
(/Gecko\/(\d{8})/.test(ua) || | |
navigator.product == "Gecko" && /^(\d{8})$/.test(navigator.productSub))) { | |
this.gecko = RegExp.$1; | |
} | |
/*@end @*/ | |
if (typeof(opera) == "object" && typeof(opera.version) == "function") { | |
this.opera = opera.version(); | |
this['opera' + this.opera[0] + this.opera[2]] = true; | |
} else if (typeof opera == "object" | |
&& (/Opera[\/ ](\d+\.\d+)/.test(ua))) { | |
this.opera = RegExp.$1; | |
} else if (this.ie) { | |
} else if (/Safari\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.safari = RegExp.$1; | |
} else if (/NetFront\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.netfront = RegExp.$1; | |
} else if (/Konqueror\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.konqueror = RegExp.$1; | |
} else if (ua.indexOf("(compatible;") < 0 | |
&& (/^Mozilla\/(\d+\.\d+)/.test(ua))) { | |
this.mozilla = RegExp.$1; | |
if (/\([^(]*rv:(\d+(?:\.\d+)*).*?\)/.test(ua)) | |
this.mozillarv = RegExp.$1; | |
if (/Firefox\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.firefox = RegExp.$1; | |
} else if (/Netscape\d?\/(\d+(?:\.\d+)*)/.test(ua)) { | |
this.netscape = RegExp.$1; | |
} | |
} else { | |
this.unknown = true; | |
} | |
if (ua.indexOf("Win 9x 4.90") >= 0) { | |
this.windows = "ME"; | |
} else if (/Win(?:dows)? ?(NT ?(\d+\.\d+)?|\d+|ME|Vista|XP)/.test(ua)) { | |
this.windows = RegExp.$1; | |
if (RegExp.$2) { | |
this.winnt = RegExp.$2; | |
} else switch (RegExp.$1) { | |
case "2000": this.winnt = "5.0"; break; | |
case "XP": this.winnt = "5.1"; break; | |
case "Vista": this.winnt = "6.0"; break; | |
} | |
} else if (ua.indexOf("Mac") >= 0) { | |
this.mac = true; | |
} else if (ua.indexOf("Linux") >= 0) { | |
this.linux = true; | |
} else if (/(\w*BSD)/.test(ua)) { | |
this.bsd = RegExp.$1; | |
} else if (ua.indexOf("SunOS") >= 0) { | |
this.sunos = true; | |
} | |
} | |
}; | |
/** | |
* pseudo class: Lexer | |
*/ | |
var Lexer = function(source) { | |
var proto = Lexer.prototype; | |
var tokens = source.match(proto.regs.token); | |
for (var i = 0, l = tokens.length; i < l; i ++) { | |
if (proto.regs.strip.test(tokens[i])) { | |
tokens.splice(i, 1); | |
} | |
} | |
for (var n in proto) tokens[n] = proto[n]; | |
tokens.index = 0; | |
return tokens; | |
}; | |
Lexer.prototype.regs = { | |
token: /\$?(?:(?![0-9-])[\w-]+:)?(?![0-9-])[\w-]+|\/\/|\.\.|::|\d+(?:\.\d*)?|\.\d+|"[^"]*"|'[^']*'|[!<>]=|(?![0-9-])[\w-]+:\*|\s+|./g, | |
strip: /^\s/ | |
}; | |
Lexer.prototype.peek = function(i) { | |
return this[this.index + (i||0)]; | |
}; | |
Lexer.prototype.next = function() { | |
return this[this.index++]; | |
}; | |
Lexer.prototype.back = function() { | |
this.index--; | |
}; | |
Lexer.prototype.empty = function() { | |
return this.length <= this.index; | |
}; | |
/** | |
* class: Ctx | |
*/ | |
var Ctx = function(node, position, last) { | |
this.node = node; | |
this.position = position || 1; | |
this.last = last || 1; | |
}; | |
/** | |
* abstract class: BaseExpr | |
*/ | |
var BaseExpr = function() {}; | |
BaseExpr.prototype.number = function(ctx) { | |
var exrs = this.evaluate(ctx); | |
if (exrs.isNodeSet) return exrs.number(); | |
return + exrs; | |
}; | |
BaseExpr.prototype.string = function(ctx) { | |
var exrs = this.evaluate(ctx); | |
if (exrs.isNodeSet) return exrs.string(); | |
return '' + exrs; | |
}; | |
BaseExpr.prototype.bool = function(ctx) { | |
var exrs = this.evaluate(ctx); | |
if (exrs.isNodeSet) return exrs.bool(); | |
return !! exrs; | |
}; | |
/** | |
* abstract class: BaseExprHasPredicates | |
*/ | |
var BaseExprHasPredicates = function() {}; | |
BaseExprHasPredicates.parsePredicates = function(lexer, expr) { | |
while (lexer.peek() == '[') { | |
lexer.next(); | |
if (lexer.empty()) { | |
throw Error('missing predicate expr'); | |
} | |
var predicate = BinaryExpr.parse(lexer); | |
expr.predicate(predicate); | |
if (lexer.empty()) { | |
throw Error('unclosed predicate expr'); | |
} | |
if (lexer.next() != ']') { | |
lexer.back(); | |
throw Error('bad token: ' + lexer.next()); | |
} | |
} | |
}; | |
BaseExprHasPredicates.prototype = new BaseExpr(); | |
BaseExprHasPredicates.prototype.evaluatePredicates = function(nodeset, start) { | |
var predicates, predicate, nodes, node, nodeset, position, reverse; | |
reverse = this.reverse; | |
predicates = this.predicates; | |
nodeset.sort(); | |
for (var i = start || 0, l0 = predicates.length; i < l0; i ++) { | |
predicate = predicates[i]; | |
var deleteIndexes = []; | |
var nodes = nodeset.list(); | |
for (var j = 0, l1 = nodes.length; j < l1; j ++) { | |
position = reverse ? (l1 - j) : (j + 1); | |
exrs = predicate.evaluate(new Ctx(nodes[j], position, l1)); | |
switch (typeof exrs) { | |
case 'number': | |
exrs = (position == exrs); | |
break; | |
case 'string': | |
exrs = !!exrs; | |
break; | |
case 'object': | |
exrs = exrs.bool(); | |
break; | |
} | |
if (!exrs) { | |
deleteIndexes.push(j); | |
} | |
} | |
for (var j = deleteIndexes.length - 1, l1 = 0; j >= l1; j --) { | |
nodeset.del(deleteIndexes[j]); | |
} | |
} | |
return nodeset; | |
}; | |
/** | |
* class: BinaryExpr | |
*/ | |
if (!window.BinaryExpr && window.defaultConfig) | |
window.BinaryExpr = null; | |
BinaryExpr = function(op, left, right, datatype) { | |
this.op = op; | |
this.left = left; | |
this.right = right; | |
this.datatype = BinaryExpr.ops[op][2]; | |
this.needContextPosition = left.needContextPosition || right.needContextPosition; | |
this.needContextNode = left.needContextNode || right.needContextNode; | |
// Optimize [@id="foo"] and [@name="bar"] | |
if (this.op == '=') { | |
if (!right.needContextNode && !right.needContextPosition && | |
right.datatype != 'nodeset' && right.datatype != 'void' && left.quickAttr) { | |
this.quickAttr = true; | |
this.attrName = left.attrName; | |
this.attrValueExpr = right; | |
} | |
else if (!left.needContextNode && !left.needContextPosition && | |
left.datatype != 'nodeset' && left.datatype != 'void' && right.quickAttr) { | |
this.quickAttr = true; | |
this.attrName = right.attrName; | |
this.attrValueExpr = left; | |
} | |
} | |
}; | |
BinaryExpr.compare = function(op, comp, left, right, ctx) { | |
var type, lnodes, rnodes, nodes, nodeset, primitive; | |
left = left.evaluate(ctx); | |
right = right.evaluate(ctx); | |
if (left.isNodeSet && right.isNodeSet) { | |
lnodes = left.list(); | |
rnodes = right.list(); | |
for (var i = 0, l0 = lnodes.length; i < l0; i ++) | |
for (var j = 0, l1 = rnodes.length; j < l1; j ++) | |
if (comp(NodeUtil.to('string', lnodes[i]), NodeUtil.to('string', rnodes[j]))) | |
return true; | |
return false; | |
} | |
if (left.isNodeSet || right.isNodeSet) { | |
if (left.isNodeSet) | |
nodeset = left, primitive = right; | |
else | |
nodeset = right, primitive = left; | |
nodes = nodeset.list(); | |
type = typeof primitive; | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
if (comp(NodeUtil.to(type, nodes[i]), primitive)) | |
return true; | |
} | |
return false; | |
} | |
if (op == '=' || op == '!=') { | |
if (typeof left == 'boolean' || typeof right == 'boolean') { | |
return comp(!!left, !!right); | |
} | |
if (typeof left == 'number' || typeof right == 'number') { | |
return comp(+left, +right); | |
} | |
return comp(left, right); | |
} | |
return comp(+left, +right); | |
}; | |
BinaryExpr.ops = { | |
'div': [6, function(left, right, ctx) { | |
return left.number(ctx) / right.number(ctx); | |
}, 'number'], | |
'mod': [6, function(left, right, ctx) { | |
return left.number(ctx) % right.number(ctx); | |
}, 'number'], | |
'*': [6, function(left, right, ctx) { | |
return left.number(ctx) * right.number(ctx); | |
}, 'number'], | |
'+': [5, function(left, right, ctx) { | |
return left.number(ctx) + right.number(ctx); | |
}, 'number'], | |
'-': [5, function(left, right, ctx) { | |
return left.number(ctx) - right.number(ctx); | |
}, 'number'], | |
'<': [4, function(left, right, ctx) { | |
return BinaryExpr.compare('<', | |
function(a, b) { return a < b }, left, right, ctx); | |
}, 'boolean'], | |
'>': [4, function(left, right, ctx) { | |
return BinaryExpr.compare('>', | |
function(a, b) { return a > b }, left, right, ctx); | |
}, 'boolean'], | |
'<=': [4, function(left, right, ctx) { | |
return BinaryExpr.compare('<=', | |
function(a, b) { return a <= b }, left, right, ctx); | |
}, 'boolean'], | |
'>=': [4, function(left, right, ctx) { | |
return BinaryExpr.compare('>=', | |
function(a, b) { return a >= b }, left, right, ctx); | |
}, 'boolean'], | |
'=': [3, function(left, right, ctx) { | |
return BinaryExpr.compare('=', | |
function(a, b) { return a == b }, left, right, ctx); | |
}, 'boolean'], | |
'!=': [3, function(left, right, ctx) { | |
return BinaryExpr.compare('!=', | |
function(a, b) { return a != b }, left, right, ctx); | |
}, 'boolean'], | |
'and': [2, function(left, right, ctx) { | |
return left.bool(ctx) && right.bool(ctx); | |
}, 'boolean'], | |
'or': [1, function(left, right, ctx) { | |
return left.bool(ctx) || right.bool(ctx); | |
}, 'boolean'] | |
}; | |
BinaryExpr.parse = function(lexer) { | |
var op, precedence, info, expr, stack = [], index = lexer.index; | |
while (true) { | |
if (lexer.empty()) { | |
throw Error('missing right expression'); | |
} | |
expr = UnaryExpr.parse(lexer); | |
op = lexer.next(); | |
if (!op) { | |
break; | |
} | |
info = this.ops[op]; | |
precedence = info && info[0]; | |
if (!precedence) { | |
lexer.back(); | |
break; | |
} | |
while (stack.length && precedence <= this.ops[stack[stack.length-1]][0]) { | |
expr = new BinaryExpr(stack.pop(), stack.pop(), expr); | |
} | |
stack.push(expr, op); | |
} | |
while (stack.length) { | |
expr = new BinaryExpr(stack.pop(), stack.pop(), expr); | |
} | |
return expr; | |
}; | |
BinaryExpr.prototype = new BaseExpr(); | |
BinaryExpr.prototype.evaluate = function(ctx) { | |
return BinaryExpr.ops[this.op][1](this.left, this.right, ctx); | |
}; | |
BinaryExpr.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'binary: ' + this.op + '\n'; | |
indent += ' '; | |
t += this.left.show(indent); | |
t += this.right.show(indent); | |
return t; | |
}; | |
/** | |
* class: UnaryExpr | |
*/ | |
if (!window.UnaryExpr && window.defaultConfig) | |
window.UnaryExpr = null; | |
UnaryExpr = function(op, expr) { | |
this.op = op; | |
this.expr = expr; | |
this.needContextPosition = expr.needContextPosition; | |
this.needContextNode = expr.needContextNode; | |
}; | |
UnaryExpr.ops = { '-': 1 }; | |
UnaryExpr.parse = function(lexer) { | |
var token; | |
if (this.ops[lexer.peek()]) | |
return new UnaryExpr(lexer.next(), UnaryExpr.parse(lexer)); | |
else | |
return UnionExpr.parse(lexer); | |
}; | |
UnaryExpr.prototype = new BaseExpr(); | |
UnaryExpr.prototype.datatype = 'number'; | |
UnaryExpr.prototype.evaluate = function(ctx) { | |
return - this.expr.number(ctx); | |
}; | |
UnaryExpr.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'unary: ' + this.op + '\n'; | |
indent += ' '; | |
t += this.expr.show(indent); | |
return t; | |
}; | |
/** | |
* class: UnionExpr | |
*/ | |
if (!window.UnionExpr && window.defaultConfig) | |
window.UnionExpr = null; | |
UnionExpr = function() { | |
this.paths = []; | |
}; | |
UnionExpr.ops = { '|': 1 }; | |
UnionExpr.parse = function(lexer) { | |
var union, expr; | |
expr = PathExpr.parse(lexer); | |
if (!this.ops[lexer.peek()]) | |
return expr; | |
union = new UnionExpr(); | |
union.path(expr); | |
while (true) { | |
if (!this.ops[lexer.next()]) break; | |
if (lexer.empty()) { | |
throw Error('missing next union location path'); | |
} | |
union.path(PathExpr.parse(lexer)); | |
} | |
lexer.back(); | |
return union; | |
}; | |
UnionExpr.prototype = new BaseExpr(); | |
UnionExpr.prototype.datatype = 'nodeset'; | |
UnionExpr.prototype.evaluate = function(ctx) { | |
var paths = this.paths; | |
var nodeset = new NodeSet(); | |
for (var i = 0, l = paths.length; i < l; i ++) { | |
var exrs = paths[i].evaluate(ctx); | |
if (!exrs.isNodeSet) throw Error('PathExpr must be nodeset'); | |
nodeset.merge(exrs); | |
} | |
return nodeset; | |
}; | |
UnionExpr.prototype.path = function(path) { | |
this.paths.push(path); | |
if (path.needContextPosition) { | |
this.needContextPosition = true; | |
} | |
if (path.needContextNode) { | |
this.needContextNode = true; | |
} | |
} | |
UnionExpr.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'union:' + '\n'; | |
indent += ' '; | |
for (var i = 0; i < this.paths.length; i ++) { | |
t += this.paths[i].show(indent); | |
} | |
return t; | |
}; | |
/** | |
* class: PathExpr | |
*/ | |
if (!window.PathExpr && window.defaultConfig) | |
window.PathExpr = null; | |
PathExpr = function(filter) { | |
this.filter = filter; | |
this.steps = []; | |
this.datatype = filter.datatype; | |
this.needContextPosition = filter.needContextPosition; | |
this.needContextNode = filter.needContextNode; | |
}; | |
PathExpr.ops = { '//': 1, '/': 1 }; | |
PathExpr.parse = function(lexer) { | |
var op, expr, path, token; | |
if (this.ops[lexer.peek()]) { | |
op = lexer.next(); | |
token = lexer.peek(); | |
if (op == '/' && (lexer.empty() || | |
(token != '.' && token != '..' && token != '@' && token != '*' && | |
!/(?![0-9])[\w]/.test(token)))) { | |
return FilterExpr.root(); | |
} | |
path = new PathExpr(FilterExpr.root()); // RootExpr | |
if (lexer.empty()) { | |
throw Error('missing next location step'); | |
} | |
expr = Step.parse(lexer); | |
path.step(op, expr); | |
} | |
else { | |
expr = FilterExpr.parse(lexer); | |
if (!expr) { | |
expr = Step.parse(lexer); | |
path = new PathExpr(FilterExpr.context()); | |
path.step('/', expr); | |
} | |
else if (!this.ops[lexer.peek()]) | |
return expr; | |
else | |
path = new PathExpr(expr); | |
} | |
while (true) { | |
if (!this.ops[lexer.peek()]) break; | |
op = lexer.next(); | |
if (lexer.empty()) { | |
throw Error('missing next location step'); | |
} | |
path.step(op, Step.parse(lexer)); | |
} | |
return path; | |
}; | |
PathExpr.prototype = new BaseExpr(); | |
PathExpr.prototype.evaluate = function(ctx) { | |
var nodeset = this.filter.evaluate(ctx); | |
if (!nodeset.isNodeSet) throw Exception('Filter nodeset must be nodeset type'); | |
var steps = this.steps; | |
for (var i = 0, l0 = steps.length; i < l0 && nodeset.length; i ++) { | |
var step = steps[i][1]; | |
var reverse = step.reverse; | |
var iter = nodeset.iterator(reverse); | |
var prevNodeset = nodeset; | |
nodeset = null; | |
var node, next; | |
if (!step.needContextPosition && step.axis == 'following') { | |
for (node = iter(); next = iter(); node = next) { | |
// Safari 2 node.contains problem | |
if (uai.applewebkit2) { | |
var contains = false; | |
var ancestor = next; | |
do { | |
if (ancestor == node) { | |
contains = true; | |
break; | |
} | |
} while (ancestor = ancestor.parentNode); | |
if (!contains) break; | |
} | |
else { | |
try { if (!node.contains(next)) break } | |
catch(e) { if (!(next.compareDocumentPosition(node) & 8)) break } | |
} | |
} | |
nodeset = step.evaluate(new Ctx(node)); | |
} | |
else if (!step.needContextPosition && step.axis == 'preceding') { | |
node = iter(); | |
nodeset = step.evaluate(new Ctx(node)); | |
} | |
else { | |
node = iter(); | |
var j = 0; | |
nodeset = step.evaluate(new Ctx(node), false, prevNodeset, j); | |
while (node = iter()) { | |
j ++; | |
nodeset.merge(step.evaluate(new Ctx(node), false, prevNodeset, j)); | |
} | |
} | |
} | |
return nodeset; | |
}; | |
PathExpr.prototype.step = function(op, step) { | |
step.op = op; | |
this.steps.push([op, step]); | |
this.quickAttr = false; | |
if (this.steps.length == 1) { | |
if (op == '/' && step.axis == 'attribute') { | |
var test = step.test; | |
if (!test.notOnlyElement && test.name != '*') { | |
this.quickAttr = true; | |
this.attrName = test.name; | |
} | |
} | |
} | |
}; | |
PathExpr.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'path:' + '\n'; | |
indent += ' '; | |
t += indent + 'filter:' + '\n'; | |
t += this.filter.show(indent + ' '); | |
if (this.steps.length) { | |
t += indent + 'steps:' + '\n'; | |
indent += ' '; | |
for (var i = 0; i < this.steps.length; i ++) { | |
var step = this.steps[i]; | |
t += indent + 'operator: ' + step[0] + '\n'; | |
t += step[1].show(indent); | |
} | |
} | |
return t; | |
}; | |
/** | |
* class: FilterExpr | |
*/ | |
if (!window.FilterExpr && window.defaultConfig) | |
window.FilterExpr = null; | |
FilterExpr = function(primary) { | |
this.primary = primary; | |
this.predicates = []; | |
this.datatype = primary.datatype; | |
this.needContextPosition = primary.needContextPosition; | |
this.needContextNode = primary.needContextNode; | |
}; | |
FilterExpr.parse = function(lexer) { | |
var expr, filter, token, ch; | |
token = lexer.peek(); | |
ch = token.charAt(0); | |
switch (ch) { | |
case '$': | |
expr = VariableReference.parse(lexer); | |
break; | |
case '(': | |
lexer.next(); | |
expr = BinaryExpr.parse(lexer); | |
if (lexer.empty()) { | |
throw Error('unclosed "("'); | |
} | |
if (lexer.next() != ')') { | |
lexer.back(); | |
throw Error('bad token: ' + lexer.next()); | |
} | |
break; | |
case '"': | |
case "'": | |
expr = Literal.parse(lexer); | |
break; | |
default: | |
if (!isNaN(+token)) { | |
expr = Number.parse(lexer); | |
} | |
else if (NodeType.types[token]) { | |
return null; | |
} | |
else if (/(?![0-9])[\w]/.test(ch) && lexer.peek(1) == '(') { | |
expr = FunctionCall.parse(lexer); | |
} | |
else { | |
return null; | |
} | |
break; | |
} | |
if (lexer.peek() != '[') return expr; | |
filter = new FilterExpr(expr); | |
BaseExprHasPredicates.parsePredicates(lexer, filter); | |
return filter; | |
}; | |
FilterExpr.root = function() { | |
return new FunctionCall('root-node'); | |
}; | |
FilterExpr.context = function() { | |
return new FunctionCall('context-node'); | |
}; | |
FilterExpr.prototype = new BaseExprHasPredicates(); | |
FilterExpr.prototype.evaluate = function(ctx) { | |
var nodeset = this.primary.evaluate(ctx); | |
if(!nodeset.isNodeSet) { | |
if (this.predicates.length) | |
throw Error( | |
'Primary result must be nodeset type ' + | |
'if filter have predicate expression'); | |
return nodeset; | |
} | |
return this.evaluatePredicates(nodeset); | |
}; | |
FilterExpr.prototype.predicate = function(predicate) { | |
this.predicates.push(predicate); | |
}; | |
FilterExpr.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'filter: ' + '\n'; | |
indent += ' '; | |
t += this.primary.show(indent); | |
if (this.predicates.length) { | |
t += indent + 'predicates: ' + '\n'; | |
indent += ' '; | |
for (var i = 0; i < this.predicates.length; i ++) { | |
t += this.predicates[i].show(indent); | |
} | |
} | |
return t; | |
}; | |
if (!window.NodeUtil && window.defaultConfig) | |
window.NodeUtil = null; | |
NodeUtil = { | |
to: function(valueType, node) { | |
var t, type = node.nodeType; | |
// Safari2: innerText contains some bugs | |
if (type == 1 && config.useInnerText && !uai.applewebkit2) { | |
t = node.textContent; | |
t = (t == undefined || t == null) ? node.innerText : t; | |
t = (t == undefined || t == null) ? '' : t; | |
} | |
if (typeof t != 'string') { | |
/*@cc_on | |
if (type == 1 && node.nodeName.toLowerCase() == 'title') { | |
t = node.text; | |
} | |
else | |
@*/ | |
if (type == 9 || type == 1) { | |
if (type == 9) { | |
node = node.documentElement; | |
} | |
else { | |
node = node.firstChild; | |
} | |
for (t = '', stack = [], i = 0; node;) { | |
do { | |
if (node.nodeType != 1) { | |
t += node.nodeValue; | |
} | |
/*@cc_on | |
else if (node.nodeName.toLowerCase() == 'title') { | |
t += node.text; | |
} | |
@*/ | |
stack[i++] = node; // push | |
} while (node = node.firstChild); | |
while (i && !(node = stack[--i].nextSibling)) {} | |
} | |
} | |
else { | |
t = node.nodeValue; | |
} | |
} | |
switch (valueType) { | |
case 'number': | |
return + t; | |
case 'boolean': | |
return !! t; | |
default: | |
return t; | |
} | |
}, | |
attrPropMap: { | |
name: 'name', | |
'class': 'className', | |
dir: 'dir', | |
id: 'id', | |
name: 'name', | |
title: 'title' | |
}, | |
attrMatch: function(node, attrName, attrValue) { | |
/*@cc_on @if (@_jscript) | |
var propName = NodeUtil.attrPropMap[attrName]; | |
if (!attrName || | |
attrValue == null && ( | |
propName && node[propName] || | |
!propName && node.getAttribute && node.getAttribute(attrName, 2) | |
) || | |
attrValue != null && ( | |
propName && node[propName] == attrValue || | |
!propName && node.getAttribute && node.getAttribute(attrName, 2) == attrValue | |
)) { | |
@else @*/ | |
if (!attrName || | |
attrValue == null && node.hasAttribute && node.hasAttribute(attrName) || | |
attrValue != null && node.getAttribute && node.getAttribute(attrName) == attrValue) { | |
/*@end @*/ | |
return true; | |
} | |
else { | |
return false; | |
} | |
}, | |
getDescendantNodes: function(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex) { | |
if (prevNodeset) { | |
prevNodeset.delDescendant(node, prevIndex); | |
} | |
/*@cc_on | |
try { | |
if (!test.notOnlyElement || test.type == 8 || (attrName && test.type == 0)) { | |
var all = node.all; | |
if (!all) { | |
return nodeset; | |
} | |
var name = test.name; | |
if (test.type == 8) name = '!'; | |
else if (test.type == 0) name = '*'; | |
if (name != '*') { | |
all = all.tags(name); | |
if (!all) { | |
return nodeset; | |
} | |
} | |
if (attrName) { | |
var result = [] | |
var i = 0; | |
if (attrValue != null && (attrName == 'id' || attrName == 'name')) { | |
all = all[attrValue]; | |
if (!all) { | |
return nodeset; | |
} | |
if (!all.length || all.nodeType) { | |
all = [all]; | |
} | |
} | |
while (node = all[i++]) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) result.push(node); | |
} | |
all = result; | |
} | |
var i = 0; | |
while (node = all[i++]) { | |
if (name != '*' || node.tagName != '!') { | |
nodeset.push(node); | |
} | |
} | |
return nodeset; | |
} | |
(function (parent) { | |
var g = arguments.callee; | |
var node = parent.firstChild; | |
if (node) { | |
for (; node; node = node.nextSibling) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node)) nodeset.push(node); | |
} | |
g(node); | |
} | |
} | |
})(node); | |
return nodeset; | |
} | |
catch(e) { | |
@*/ | |
if (attrValue && attrName == 'id' && node.getElementById) { | |
node = node.getElementById(attrValue); | |
if (node && test.match(node)) { | |
nodeset.push(node); | |
} | |
} | |
else if (attrValue && attrName == 'name' && node.getElementsByName) { | |
var nodes = node.getElementsByName(attrValue); | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
node = nodes[i]; | |
if (uai.opera ? (node.name == attrValue && test.match(node)) : test.match(node)) { | |
nodeset.push(node); | |
} | |
} | |
} | |
else if (attrValue && attrName == 'class' && node.getElementsByClassName) { | |
var nodes = node.getElementsByClassName(attrValue); | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
node = nodes[i]; | |
if (node.className == attrValue && test.match(node)) { | |
nodeset.push(node); | |
} | |
} | |
} | |
else if (test.notOnlyElement) { | |
(function (parent) { | |
var f = arguments.callee; | |
for (var node = parent.firstChild; node; node = node.nextSibling) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node.nodeType)) nodeset.push(node); | |
} | |
f(node); | |
} | |
})(node); | |
} | |
else { | |
var name = test.name; | |
if (node.getElementsByTagName) { | |
var nodes = node.getElementsByTagName(name); | |
if (nodes) { | |
var i = 0; | |
while (node = nodes[i++]) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) nodeset.push(node); | |
} | |
} | |
} | |
} | |
return nodeset; | |
/*@cc_on | |
} | |
@*/ | |
}, | |
getChildNodes: function(test, node, nodeset, attrName, attrValue) { | |
/*@cc_on | |
try { | |
var children; | |
if ((!test.notOnlyElement || test.type == 8 || (attrName && test.type == 0)) && (children = node.children)) { | |
var name, elm; | |
name = test.name; | |
if (test.type == 8) name = '!'; | |
else if (test.type == 0) name = '*'; | |
if (name != '*') { | |
children = children.tags(name); | |
if (!children) { | |
return nodeset; | |
} | |
} | |
if (attrName) { | |
var result = [] | |
var i = 0; | |
if (attrName == 'id' || attrName == 'name') { | |
children = children[attrValue]; | |
if (!children) { | |
return nodeset; | |
} | |
if (!children.length || children.nodeType) { | |
children = [children]; | |
} | |
} | |
while (node = children[i++]) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) result.push(node); | |
} | |
children = result; | |
} | |
var i = 0; | |
while (node = children[i++]) { | |
if (name != '*' || node.tagName != '!') { | |
nodeset.push(node); | |
} | |
} | |
return nodeset; | |
} | |
for (var i = 0, node = node.firstChild; node; i++, node = node.nextSibling) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node)) nodeset.push(node); | |
} | |
} | |
return nodeset; | |
} catch(e) { | |
@*/ | |
for (var node = node.firstChild; node; node = node.nextSibling) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node)) nodeset.push(node); | |
} | |
} | |
return nodeset; | |
/*@cc_on | |
} | |
@*/ | |
} | |
}; | |
/*@cc_on | |
var AttributeWrapper = function(node, parent, sourceIndex) { | |
this.node = node; | |
this.nodeType = 2; | |
this.nodeValue = node.nodeValue; | |
this.nodeName = node.nodeName; | |
this.parentNode = parent; | |
this.ownerElement = parent; | |
this.parentSourceIndex = sourceIndex; | |
}; | |
@*/ | |
/** | |
* class: Step | |
*/ | |
if (!window.Step && window.defaultConfig) | |
window.Step = null; | |
Step = function(axis, test) { | |
// TODO check arguments and throw axis error | |
this.axis = axis; | |
this.reverse = Step.axises[axis][0]; | |
this.func = Step.axises[axis][1]; | |
this.test = test; | |
this.predicates = []; | |
this._quickAttr = Step.axises[axis][2] | |
}; | |
Step.axises = { | |
ancestor: [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) { | |
while (node = node.parentNode) { | |
if (prevNodeset && node.nodeType == 1) { | |
prevNodeset.reserveDelByNode(node, prevIndex, true); | |
} | |
if (test.match(node)) nodeset.unshift(node); | |
} | |
return nodeset; | |
}], | |
'ancestor-or-self': [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) { | |
do { | |
if (prevNodeset && node.nodeType == 1) { | |
prevNodeset.reserveDelByNode(node, prevIndex, true); | |
} | |
if (test.match(node)) nodeset.unshift(node); | |
} while (node = node.parentNode) | |
return nodeset; | |
}], | |
attribute: [false, function(test, node, nodeset) { | |
var attrs = node.attributes; | |
if (attrs) { | |
/*@cc_on | |
var sourceIndex = node.sourceIndex; | |
@*/ | |
if ((test.notOnlyElement && test.type == 0) || test.name == '*') { | |
for (var i = 0, attr; attr = attrs[i]; i ++) { | |
/*@cc_on @if (@_jscript) | |
if (attr.nodeValue) { | |
nodeset.push(new AttributeWrapper(attr, node, sourceIndex)); | |
} | |
@else @*/ | |
nodeset.push(attr); | |
/*@end @*/ | |
} | |
} | |
else { | |
var attr = attrs.getNamedItem(test.name); | |
/*@cc_on @if (@_jscript) | |
if (attr && attr.nodeValue) { | |
attr = new AttributeWrapper(attr, node, sourceIndex);; | |
@else @*/ | |
if (attr) { | |
/*@end @*/ | |
nodeset.push(attr); | |
} | |
} | |
} | |
return nodeset; | |
}], | |
child: [false, NodeUtil.getChildNodes, true], | |
descendant: [false, NodeUtil.getDescendantNodes, true], | |
'descendant-or-self': [false, function(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex) { | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node)) nodeset.push(node); | |
} | |
return NodeUtil.getDescendantNodes(test, node, nodeset, attrName, attrValue, prevNodeset, prevIndex); | |
}, true], | |
following: [false, function(test, node, nodeset, attrName, attrValue) { | |
do { | |
var child = node; | |
while (child = child.nextSibling) { | |
if (NodeUtil.attrMatch(child, attrName, attrValue)) { | |
if (test.match(child)) nodeset.push(child); | |
} | |
nodeset = NodeUtil.getDescendantNodes(test, child, nodeset, attrName, attrValue); | |
} | |
} while (node = node.parentNode); | |
return nodeset; | |
}, true], | |
'following-sibling': [false, function(test, node, nodeset, _, __, prevNodeset, prevIndex) { | |
while (node = node.nextSibling) { | |
if (prevNodeset && node.nodeType == 1) { | |
prevNodeset.reserveDelByNode(node, prevIndex); | |
} | |
if (test.match(node)) { | |
nodeset.push(node); | |
} | |
} | |
return nodeset; | |
}], | |
namespace: [false, function(test, node, nodeset) { | |
// not implemented | |
return nodeset; | |
}], | |
parent: [false, function(test, node, nodeset) { | |
if (node.nodeType == 9) { | |
return nodeset; | |
} | |
if (node.nodeType == 2) { | |
nodeset.push(node.ownerElement); | |
return nodeset; | |
} | |
var node = node.parentNode; | |
if (test.match(node)) nodeset.push(node); | |
return nodeset; | |
}], | |
preceding: [true, function(test, node, nodeset, attrName, attrValue) { | |
var parents = []; | |
do { | |
parents.unshift(node); | |
} while (node = node.parentNode); | |
for (var i = 1, l0 = parents.length; i < l0; i ++) { | |
var siblings = []; | |
node = parents[i]; | |
while (node = node.previousSibling) { | |
siblings.unshift(node); | |
} | |
for (var j = 0, l1 = siblings.length; j < l1; j ++) { | |
node = siblings[j]; | |
if (NodeUtil.attrMatch(node, attrName, attrValue)) { | |
if (test.match(node)) nodeset.push(node); | |
} | |
nodeset = NodeUtil.getDescendantNodes(test, node, nodeset, attrName, attrValue); | |
} | |
} | |
return nodeset; | |
}, true], | |
'preceding-sibling': [true, function(test, node, nodeset, _, __, prevNodeset, prevIndex) { | |
while (node = node.previousSibling) { | |
if (prevNodeset && node.nodeType == 1) { | |
prevNodeset.reserveDelByNode(node, prevIndex, true); | |
} | |
if (test.match(node)) { | |
nodeset.unshift(node) | |
} | |
} | |
return nodeset; | |
}], | |
self: [false, function(test, node, nodeset) { | |
if (test.match(node)) nodeset.push(node); | |
return nodeset; | |
}] | |
}; | |
Step.parse = function(lexer) { | |
var axis, test, step, token; | |
if (lexer.peek() == '.') { | |
step = this.self(); | |
lexer.next(); | |
} | |
else if (lexer.peek() == '..') { | |
step = this.parent(); | |
lexer.next(); | |
} | |
else { | |
if (lexer.peek() == '@') { | |
axis = 'attribute'; | |
lexer.next(); | |
if (lexer.empty()) { | |
throw Error('missing attribute name'); | |
} | |
} | |
else { | |
if (lexer.peek(1) == '::') { | |
if (!/(?![0-9])[\w]/.test(lexer.peek().charAt(0))) { | |
throw Error('bad token: ' + lexer.next()); | |
} | |
axis = lexer.next(); | |
lexer.next(); | |
if (!this.axises[axis]) { | |
throw Error('invalid axis: ' + axis); | |
} | |
if (lexer.empty()) { | |
throw Error('missing node name'); | |
} | |
} | |
else { | |
axis = 'child'; | |
} | |
} | |
token = lexer.peek(); | |
if (!/(?![0-9])[\w]/.test(token.charAt(0))) { | |
if (token == '*') { | |
test = NameTest.parse(lexer) | |
} | |
else { | |
throw Error('bad token: ' + lexer.next()); | |
} | |
} | |
else { | |
if (lexer.peek(1) == '(') { | |
if (!NodeType.types[token]) { | |
throw Error('invalid node type: ' + token); | |
} | |
test = NodeType.parse(lexer) | |
} | |
else { | |
test = NameTest.parse(lexer); | |
} | |
} | |
step = new Step(axis, test); | |
} | |
BaseExprHasPredicates.parsePredicates(lexer, step); | |
return step; | |
}; | |
Step.self = function() { | |
return new Step('self', new NodeType('node')); | |
}; | |
Step.parent = function() { | |
return new Step('parent', new NodeType('node')); | |
}; | |
Step.prototype = new BaseExprHasPredicates(); | |
Step.prototype.evaluate = function(ctx, special, prevNodeset, prevIndex) { | |
var node = ctx.node; | |
var reverse = false; | |
if (!special && this.op == '//') { | |
if (!this.needContextPosition && this.axis == 'child') { | |
if (this.quickAttr) { | |
var attrValue = this.attrValueExpr ? this.attrValueExpr.string(ctx) : null; | |
var nodeset = NodeUtil.getDescendantNodes(this.test, node, new NodeSet(), this.attrName, attrValue, prevNodeset, prevIndex); | |
nodeset = this.evaluatePredicates(nodeset, 1); | |
} | |
else { | |
var nodeset = NodeUtil.getDescendantNodes(this.test, node, new NodeSet(), null, null, prevNodeset, prevIndex); | |
nodeset = this.evaluatePredicates(nodeset); | |
} | |
} | |
else { | |
var step = new Step('descendant-or-self', new NodeType('node')); | |
var nodes = step.evaluate(ctx, false, prevNodeset, prevIndex).list(); | |
var nodeset = null; | |
step.op = '/'; | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
if (!nodeset) { | |
nodeset = this.evaluate(new Ctx(nodes[i]), true); | |
} | |
else { | |
nodeset.merge(this.evaluate(new Ctx(nodes[i]), true)); | |
} | |
} | |
nodeset = nodeset || new NodeSet(); | |
} | |
} | |
else { | |
if (this.needContextPosition) { | |
prevNodeset = null; | |
prevIndex = null; | |
} | |
if (this.quickAttr) { | |
var attrValue = this.attrValueExpr ? this.attrValueExpr.string(ctx) : null; | |
var nodeset = this.func(this.test, node, new NodeSet(), this.attrName, attrValue, prevNodeset, prevIndex); | |
nodeset = this.evaluatePredicates(nodeset, 1); | |
} | |
else { | |
var nodeset = this.func(this.test, node, new NodeSet(), null, null, prevNodeset, prevIndex); | |
nodeset = this.evaluatePredicates(nodeset); | |
} | |
if (prevNodeset) { | |
prevNodeset.doDel(); | |
} | |
} | |
return nodeset; | |
}; | |
Step.prototype.predicate = function(predicate) { | |
this.predicates.push(predicate); | |
if (predicate.needContextPosition || | |
predicate.datatype == 'number'|| | |
predicate.datatype == 'void') { | |
this.needContextPosition = true; | |
} | |
if (this._quickAttr && this.predicates.length == 1 && predicate.quickAttr) { | |
var attrName = predicate.attrName; | |
/*@cc_on @if (@_jscript) | |
this.attrName = attrName.toLowerCase(); | |
@else @*/ | |
this.attrName = attrName; | |
/*@end @*/ | |
this.attrValueExpr = predicate.attrValueExpr; | |
this.quickAttr = true; | |
} | |
}; | |
Step.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'step: ' + '\n'; | |
indent += ' '; | |
if (this.axis) t += indent + 'axis: ' + this.axis + '\n'; | |
t += this.test.show(indent); | |
if (this.predicates.length) { | |
t += indent + 'predicates: ' + '\n'; | |
indent += ' '; | |
for (var i = 0; i < this.predicates.length; i ++) { | |
t += this.predicates[i].show(indent); | |
} | |
} | |
return t; | |
}; | |
/** | |
* NodeType | |
*/ | |
if (!window.NodeType && window.defaultConfig) | |
window.NodeType = null; | |
NodeType = function(name, literal) { | |
this.name = name; | |
this.literal = literal; | |
switch (name) { | |
case 'comment': | |
this.type = 8; | |
break; | |
case 'text': | |
this.type = 3; | |
break; | |
case 'processing-instruction': | |
this.type = 7; | |
break; | |
case 'node': | |
this.type = 0; | |
break; | |
} | |
}; | |
NodeType.types = { | |
'comment':1, 'text':1, 'processing-instruction':1, 'node':1 | |
}; | |
NodeType.parse = function(lexer) { | |
var type, literal, ch; | |
type = lexer.next(); | |
lexer.next(); | |
if (lexer.empty()) { | |
throw Error('bad nodetype'); | |
} | |
ch = lexer.peek().charAt(0); | |
if (ch == '"' || ch == "'") { | |
literal = Literal.parse(lexer); | |
} | |
if (lexer.empty()) { | |
throw Error('bad nodetype'); | |
} | |
if (lexer.next() != ')') { | |
lexer.back(); | |
throw Error('bad token ' + lexer.next()); | |
} | |
return new NodeType(type, literal); | |
}; | |
NodeType.prototype = new BaseExpr(); | |
NodeType.prototype.notOnlyElement = true; | |
NodeType.prototype.match = function(node) { | |
return !this.type || this.type == node.nodeType; | |
}; | |
NodeType.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'nodetype: ' + this.type + '\n'; | |
if (this.literal) { | |
indent += ' '; | |
t += this.literal.show(indent); | |
} | |
return t; | |
}; | |
/** | |
* NodeType | |
*/ | |
if (!window.NameTest && window.defaultConfig) | |
window.NameTest = null; | |
NameTest = function(name) { | |
this.name = name.toLowerCase(); | |
}; | |
NameTest.parse = function(lexer) { | |
if (lexer.peek() != '*' && lexer.peek(1) == ':' && lexer.peek(2) == '*') { | |
return new NameTest(lexer.next() + lexer.next() + lexer.next()); | |
} | |
return new NameTest(lexer.next()); | |
}; | |
NameTest.prototype = new BaseExpr(); | |
NameTest.prototype.match = function(node) { | |
var type = node.nodeType; | |
if (type == 1 || type == 2) { | |
if (this.name == '*' || this.name == node.nodeName.toLowerCase()) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
NameTest.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'nametest: ' + this.name + '\n'; | |
return t; | |
}; | |
/** | |
* class: VariableRefernce | |
*/ | |
if (!window.VariableReference && window.defaultConfig) | |
window.VariableReference = null; | |
VariableReference = function(name) { | |
this.name = name.substring(1); | |
}; | |
VariableReference.parse = function(lexer) { | |
var token = lexer.next(); | |
if (token.length < 2) { | |
throw Error('unnamed variable reference'); | |
} | |
return new VariableReference(token) | |
}; | |
VariableReference.prototype = new BaseExpr(); | |
VariableReference.prototype.datatype = 'void'; | |
VariableReference.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'variable: ' + this.name + '\n'; | |
return t; | |
}; | |
/** | |
* class: Literal | |
*/ | |
if (!window.Literal && window.defaultConfig) | |
window.Literal = null; | |
Literal = function(text) { | |
this.text = text.substring(1, text.length - 1); | |
}; | |
Literal.parse = function(lexer) { | |
var token = lexer.next(); | |
if (token.length < 2) { | |
throw Error('unclosed literal string'); | |
} | |
return new Literal(token) | |
}; | |
Literal.prototype = new BaseExpr(); | |
Literal.prototype.datatype = 'string'; | |
Literal.prototype.evaluate = function(ctx) { | |
return this.text; | |
}; | |
Literal.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'literal: ' + this.text + '\n'; | |
return t; | |
}; | |
/** | |
* class: Number | |
*/ | |
if (!window.Number && window.defaultConfig) | |
window.Number = null; | |
Number = function(digit) { | |
this.digit = +digit; | |
}; | |
Number.parse = function(lexer) { | |
return new Number(lexer.next()); | |
}; | |
Number.prototype = new BaseExpr(); | |
Number.prototype.datatype = 'number'; | |
Number.prototype.evaluate = function(ctx) { | |
return this.digit; | |
}; | |
Number.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'number: ' + this.digit + '\n'; | |
return t; | |
}; | |
/** | |
* class: FunctionCall | |
*/ | |
if (!window.FunctionCall && window.defaultConfig) | |
window.FunctionCall = null; | |
FunctionCall = function(name) { | |
var info = FunctionCall.funcs[name]; | |
if (!info) | |
throw Error(name +' is not a function'); | |
this.name = name; | |
this.func = info[0]; | |
this.args = []; | |
this.datatype = info[1]; | |
if (info[2]) { | |
this.needContextPosition = true; | |
} | |
this.needContextNodeInfo = info[3]; | |
this.needContextNode = this.needContextNodeInfo[0] | |
}; | |
FunctionCall.funcs = { | |
// Original Function | |
'context-node': [function() { | |
if (arguments.length != 0) { | |
throw Error('Function context-node expects ()'); | |
} | |
var ns; | |
ns = new NodeSet(); | |
ns.push(this.node); | |
return ns; | |
}, 'nodeset', false, [true]], | |
// Original Function | |
'root-node': [function() { | |
if (arguments.length != 0) { | |
throw Error('Function root-node expects ()'); | |
} | |
var ns, ctxn; | |
ns = new NodeSet(); | |
ctxn = this.node; | |
if (ctxn.nodeType == 9) | |
ns.push(ctxn); | |
else | |
ns.push(ctxn.ownerDocument); | |
return ns; | |
}, 'nodeset', false, []], | |
last: [function() { | |
if (arguments.length != 0) { | |
throw Error('Function last expects ()'); | |
} | |
return this.last; | |
}, 'number', true, []], | |
position: [function() { | |
if (arguments.length != 0) { | |
throw Error('Function position expects ()'); | |
} | |
return this.position; | |
}, 'number', true, []], | |
count: [function(ns) { | |
if (arguments.length != 1 || !(ns = ns.evaluate(this)).isNodeSet) { | |
throw Error('Function count expects (nodeset)'); | |
} | |
return ns.length; | |
}, 'number', false, []], | |
id: [function(s) { | |
var ids, ns, i, id, elm, ctxn, doc; | |
if (arguments.length != 1) { | |
throw Error('Function id expects (object)'); | |
} | |
ctxn = this.node; | |
if (ctxn.nodeType == 9) | |
doc = ctxn; | |
else | |
doc = ctxn.ownerDocument; | |
/*@cc_on | |
all = doc.all; | |
@*/ | |
s = s.string(this); | |
ids = s.split(/\s+/); | |
ns = new NodeSet(); | |
for (i = 0, l = ids.length; i < l; i ++) { | |
id = ids[i]; | |
/*@cc_on @if (@_jscript) | |
elm = all[id]; | |
if (elm) { | |
if ((!elm.length || elm.nodeType) && id == elm.id) { | |
ns.push(elm) | |
} | |
else if (elm.length) { | |
var elms = elm; | |
for (var j = 0, l0 = elms.length; j < l0; j ++) { | |
var elem = elms[j]; | |
if (id == elem.id) { | |
ns.push(elem); | |
break; | |
} | |
} | |
} | |
} | |
@else @*/ | |
elm = doc.getElementById(id); | |
if (uai.opera && elm && elm.id != id) { | |
var elms = doc.getElementsByName(id); | |
for (var j = 0, l0 = elms.length; j < l0; j ++) { | |
elm = elms[j]; | |
if (elm.id == id) { | |
ns.push(elm); | |
} | |
} | |
} | |
else { | |
if (elm) ns.push(elm) | |
} | |
/*@end @*/ | |
} | |
ns.isSorted = false; | |
return ns; | |
}, 'nodeset', false, []], | |
'local-name': [function(ns) { | |
var nd; | |
switch (arguments.length) { | |
case 0: | |
nd = this.node; | |
break; | |
case 1: | |
if ((ns = ns.evaluate(this)).isNodeSet) { | |
nd = ns.first(); | |
break; | |
} | |
default: | |
throw Error('Function local-name expects (nodeset?)'); | |
break; | |
} | |
return '' + nd.nodeName.toLowerCase(); | |
}, 'string', false, [true, false]], | |
name: [function(ns) { | |
// not implemented | |
return FunctionCall.funcs['local-name'][0].apply(this, arguments); | |
}, 'string', false, [true, false]], | |
'namespace-uri': [function(ns) { | |
// not implemented | |
return ''; | |
}, 'string', false, [true, false]], | |
string: [function(s) { | |
switch (arguments.length) { | |
case 0: | |
s = NodeUtil.to('string', this.node); | |
break; | |
case 1: | |
s = s.string(this); | |
break; | |
default: | |
throw Error('Function string expects (object?)'); | |
break; | |
} | |
return s; | |
}, 'string', false, [true, false]], | |
concat: [function(s1, s2) { | |
if (arguments.length < 2) { | |
throw Error('Function concat expects (string, string[, ...])'); | |
} | |
for (var t = '', i = 0, l = arguments.length; i < l; i ++) { | |
t += arguments[i].string(this); | |
} | |
return t; | |
}, 'string', false, []], | |
'starts-with': [function(s1, s2) { | |
if (arguments.length != 2) { | |
throw Error('Function starts-with expects (string, string)'); | |
} | |
s1 = s1.string(this); | |
s2 = s2.string(this); | |
return s1.indexOf(s2) == 0; | |
}, 'boolean', false, []], | |
contains: [function(s1, s2) { | |
if (arguments.length != 2) { | |
throw Error('Function contains expects (string, string)'); | |
} | |
s1 = s1.string(this); | |
s2 = s2.string(this); | |
return s1.indexOf(s2) != -1; | |
}, 'boolean', false, []], | |
substring: [function(s, n1, n2) { | |
var a1, a2; | |
s = s.string(this); | |
n1 = n1.number(this); | |
switch (arguments.length) { | |
case 2: | |
n2 = s.length - n1 + 1; | |
break; | |
case 3: | |
n2 = n2.number(this); | |
break; | |
default: | |
throw Error('Function substring expects (string, string)'); | |
break; | |
} | |
n1 = Math.round(n1); | |
n2 = Math.round(n2); | |
a1 = n1 - 1; | |
a2 = n1 + n2 - 1; | |
if (a2 == Infinity) { | |
return s.substring(a1 < 0 ? 0 : a1); | |
} | |
else { | |
return s.substring(a1 < 0 ? 0 : a1, a2) | |
} | |
}, 'string', false, []], | |
'substring-before': [function(s1, s2) { | |
var n; | |
if (arguments.length != 2) { | |
throw Error('Function substring-before expects (string, string)'); | |
} | |
s1 = s1.string(this); | |
s2 = s2.string(this); | |
n = s1.indexOf(s2); | |
if (n == -1) return ''; | |
return s1.substring(0, n); | |
}, 'string', false, []], | |
'substring-after': [function(s1, s2) { | |
if (arguments.length != 2) { | |
throw Error('Function substring-after expects (string, string)'); | |
} | |
s1 = s1.string(this); | |
s2 = s2.string(this); | |
var n = s1.indexOf(s2); | |
if (n == -1) return ''; | |
return s1.substring(n + s2.length); | |
}, 'string', false, []], | |
'string-length': [function(s) { | |
switch (arguments.length) { | |
case 0: | |
s = NodeUtil.to('string', this.node); | |
break; | |
case 1: | |
s = s.string(this); | |
break; | |
default: | |
throw Error('Function string-length expects (string?)'); | |
break; | |
} | |
return s.length; | |
}, 'number', false, [true, false]], | |
'normalize-space': [function(s) { | |
switch (arguments.length) { | |
case 0: | |
s = NodeUtil.to('string', this.node); | |
break; | |
case 1: | |
s = s.string(this); | |
break; | |
default: | |
throw Error('Function normalize-space expects (string?)'); | |
break; | |
} | |
return s.replace(/\s+/g, ' ').replace(/^ /, '').replace(/ $/, ''); | |
}, 'string', false, [true, false]], | |
translate: [function(s1, s2, s3) { | |
if (arguments.length != 3) { | |
throw Error('Function translate expects (string, string, string)'); | |
} | |
s1 = s1.string(this); | |
s2 = s2.string(this); | |
s3 = s3.string(this); | |
var map = []; | |
for (var i = 0, l = s2.length; i < l; i ++) { | |
var ch = s2.charAt(i); | |
if (!map[ch]) map[ch] = s3.charAt(i) || ''; | |
} | |
for (var t = '', i = 0, l = s1.length; i < l; i ++) { | |
var ch = s1.charAt(i); | |
var replace = map[ch] | |
t += (replace != undefined) ? replace : ch; | |
} | |
return t; | |
}, 'string', false, []], | |
'boolean': [function(b) { | |
if (arguments.length != 1) { | |
throw Error('Function boolean expects (object)'); | |
} | |
return b.bool(this) | |
}, 'boolean', false, []], | |
not: [function(b) { | |
if (arguments.length != 1) { | |
throw Error('Function not expects (object)'); | |
} | |
return !b.bool(this) | |
}, 'boolean', false, []], | |
'true': [function() { | |
if (arguments.length != 0) { | |
throw Error('Function true expects ()'); | |
} | |
return true; | |
}, 'boolean', false, []], | |
'false': [function() { | |
if (arguments.length != 0) { | |
throw Error('Function false expects ()'); | |
} | |
return false; | |
}, 'boolean', false, []], | |
lang: [function(s) { | |
// not implemented | |
return false; | |
}, 'boolean', false, []], | |
number: [function(n) { | |
switch (arguments.length) { | |
case 0: | |
n = NodeUtil.to('number', this.node); | |
break; | |
case 1: | |
n = n.number(this); | |
break; | |
default: | |
throw Error('Function number expects (object?)'); | |
break; | |
} | |
return n; | |
}, 'number', false, [true, false]], | |
sum: [function(ns) { | |
var nodes, n, i, l; | |
if (arguments.length != 1 || !(ns = ns.evaluate(this)).isNodeSet) { | |
throw Error('Function sum expects (nodeset)'); | |
} | |
nodes = ns.list(); | |
n = 0; | |
for (i = 0, l = nodes.length; i < l; i ++) { | |
n += NodeUtil.to('number', nodes[i]); | |
} | |
return n; | |
}, 'number', false, []], | |
floor: [function(n) { | |
if (arguments.length != 1) { | |
throw Error('Function floor expects (number)'); | |
} | |
n = n.number(this); | |
return Math.floor(n); | |
}, 'number', false, []], | |
ceiling: [function(n) { | |
if (arguments.length != 1) { | |
throw Error('Function ceiling expects (number)'); | |
} | |
n = n.number(this); | |
return Math.ceil(n); | |
}, 'number', false, []], | |
round: [function(n) { | |
if (arguments.length != 1) { | |
throw Error('Function round expects (number)'); | |
} | |
n = n.number(this); | |
return Math.round(n); | |
}, 'number', false, []] | |
}; | |
FunctionCall.parse = function(lexer) { | |
var expr, func = new FunctionCall(lexer.next()); | |
lexer.next(); | |
while (lexer.peek() != ')') { | |
if (lexer.empty()) { | |
throw Error('missing function argument list'); | |
} | |
expr = BinaryExpr.parse(lexer); | |
func.arg(expr); | |
if (lexer.peek() != ',') break; | |
lexer.next(); | |
} | |
if (lexer.empty()) { | |
throw Error('unclosed function argument list'); | |
} | |
if (lexer.next() != ')') { | |
lexer.back(); | |
throw Error('bad token: ' + lexer.next()); | |
} | |
return func | |
}; | |
FunctionCall.prototype = new BaseExpr(); | |
FunctionCall.prototype.evaluate = function (ctx) { | |
return this.func.apply(ctx, this.args); | |
}; | |
FunctionCall.prototype.arg = function(arg) { | |
this.args.push(arg); | |
if (arg.needContextPosition) { | |
this.needContextPosition = true; | |
} | |
var args = this.args; | |
if (arg.needContextNode) { | |
args.needContexNode = true; | |
} | |
this.needContextNode = args.needContextNode || | |
this.needContextNodeInfo[args.length]; | |
}; | |
FunctionCall.prototype.show = function(indent) { | |
indent = indent || ''; | |
var t = ''; | |
t += indent + 'function: ' + this.name + '\n'; | |
indent += ' '; | |
if (this.args.length) { | |
t += indent + 'arguments: ' + '\n'; | |
indent += ' '; | |
for (var i = 0; i < this.args.length; i ++) { | |
t += this.args[i].show(indent); | |
} | |
} | |
return t; | |
}; | |
/*@cc_on @if (@_jscript) | |
var NodeWrapper = function(node, sourceIndex, subIndex, attributeName) { | |
this.node = node; | |
this.nodeType = node.nodeType; | |
this.sourceIndex = sourceIndex; | |
this.subIndex = subIndex; | |
this.attributeName = attributeName || ''; | |
this.order = String.fromCharCode(sourceIndex) + String.fromCharCode(subIndex) + attributeName; | |
}; | |
NodeWrapper.prototype.toString = function() { | |
return this.order; | |
}; | |
@else @*/ | |
var NodeID = { | |
uuid: 1, | |
get: function(node) { | |
return node.__jsxpath_id__ || (node.__jsxpath_id__ = this.uuid++); | |
} | |
}; | |
/*@end @*/ | |
if (!window.NodeSet && window.defaultConfig) | |
window.NodeSet = null; | |
NodeSet = function() { | |
this.length = 0; | |
this.nodes = []; | |
this.seen = {}; | |
this.idIndexMap = null; | |
this.reserveDels = []; | |
}; | |
NodeSet.prototype.isNodeSet = true; | |
NodeSet.prototype.isSorted = true; | |
/*@_cc_on | |
NodeSet.prototype.shortcut = true; | |
@*/ | |
NodeSet.prototype.merge = function(nodeset) { | |
this.isSorted = false; | |
if (nodeset.only) { | |
return this.push(nodeset.only); | |
} | |
if (this.only){ | |
var only = this.only; | |
delete this.only; | |
this.push(only); | |
this.length --; | |
} | |
var nodes = nodeset.nodes; | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
this._add(nodes[i]); | |
} | |
}; | |
NodeSet.prototype.sort = function() { | |
if (this.only) return; | |
if (this.sortOff) return; | |
if (!this.isSorted) { | |
this.isSorted = true; | |
this.idIndexMap = null; | |
/*@cc_on | |
if (this.shortcut) { | |
this.nodes.sort(); | |
} | |
else { | |
this.nodes.sort(function(a, b) { | |
var result; | |
result = a.sourceIndex - b.sourceIndex; | |
if (result == 0) | |
return a.subIndex - a.subIndex; | |
else | |
return result; | |
}); | |
} | |
return; | |
@*/ | |
var nodes = this.nodes; | |
nodes.sort(function(a, b) { | |
if (a == b) return 0; | |
if (a.compareDocumentPosition) { | |
var result = a.compareDocumentPosition(b); | |
if (result & 2) return 1; | |
if (result & 4) return -1; | |
return 0; | |
} | |
else { | |
var node1 = a, node2 = b, ancestor1 = a, ancestor2 = b, deep1 = 0, deep2 = 0; | |
while(ancestor1 = ancestor1.parentNode) deep1 ++; | |
while(ancestor2 = ancestor2.parentNode) deep2 ++; | |
// same deep | |
if (deep1 > deep2) { | |
while (deep1-- != deep2) node1 = node1.parentNode; | |
if (node1 == node2) return 1; | |
} | |
else if (deep2 > deep1) { | |
while (deep2-- != deep1) node2 = node2.parentNode; | |
if (node1 == node2) return -1; | |
} | |
while ((ancestor1 = node1.parentNode) != (ancestor2 = node2.parentNode)) { | |
node1 = ancestor1; | |
node2 = ancestor2; | |
} | |
// node1 is node2's sibling | |
while (node1 = node1.nextSibling) if (node1 == node2) return -1; | |
return 1; | |
} | |
}); | |
} | |
}; | |
/*@cc_on @if (@_jscript) | |
NodeSet.prototype.sourceOffset = 1; | |
NodeSet.prototype.subOffset = 2; | |
NodeSet.prototype.createWrapper = function(node) { | |
var parent, child, attributes, attributesLength, sourceIndex, subIndex, attributeName; | |
sourceIndex = node.sourceIndex; | |
if (typeof sourceIndex != 'number') { | |
type = node.nodeType; | |
switch (type) { | |
case 2: | |
parent = node.parentNode; | |
sourceIndex = node.parentSourceIndex; | |
subIndex = -1; | |
attributeName = node.nodeName; | |
break; | |
case 9: | |
subIndex = -2; | |
sourceIndex = -1; | |
break; | |
default: | |
child = node; | |
subIndex = 0; | |
do { | |
subIndex ++; | |
sourceIndex = child.sourceIndex; | |
if (sourceIndex) { | |
parent = child; | |
child = child.lastChild; | |
if (!child) { | |
child = parent; | |
break; | |
} | |
subIndex ++; | |
} | |
} while (child = child.previousSibling); | |
if (!sourceIndex) { | |
sourceIndex = node.parentNode.sourceIndex; | |
} | |
break; | |
} | |
} | |
else { | |
subIndex = -2; | |
} | |
sourceIndex += this.sourceOffset; | |
subIndex += this.subOffset; | |
return new NodeWrapper(node, sourceIndex, subIndex, attributeName); | |
}; | |
NodeSet.prototype.reserveDelBySourceIndexAndSubIndex = function(sourceIndex, subIndex, offset, reverse) { | |
var map = this.createIdIndexMap(); | |
var index; | |
if ((map = map[sourceIndex]) && (index = map[subIndex])) { | |
if (reverse && (this.length - offset - 1) > index || !reverse && offset < index) { | |
var obj = { | |
value: index, | |
order: String.fromCharCode(index), | |
toString: function() { return this.order }, | |
valueOf: function() { return this.value } | |
}; | |
this.reserveDels.push(obj); | |
} | |
} | |
}; | |
@else @*/ | |
NodeSet.prototype.reserveDelByNodeID = function(id, offset, reverse) { | |
var map = this.createIdIndexMap(); | |
var index; | |
if (index = map[id]) { | |
if (reverse && (this.length - offset - 1) > index || !reverse && offset < index) { | |
var obj = { | |
value: index, | |
order: String.fromCharCode(index), | |
toString: function() { return this.order }, | |
valueOf: function() { return this.value } | |
}; | |
this.reserveDels.push(obj); | |
} | |
} | |
}; | |
/*@end @*/ | |
NodeSet.prototype.reserveDelByNode = function(node, offset, reverse) { | |
/*@cc_on @if (@_jscript) | |
node = this.createWrapper(node); | |
this.reserveDelBySourceIndexAndSubIndex(node.sourceIndex, node.subIndex, offset, reverse); | |
@else @*/ | |
this.reserveDelByNodeID(NodeID.get(node), offset, reverse); | |
/*@end @*/ | |
}; | |
NodeSet.prototype.doDel = function() { | |
if (!this.reserveDels.length) return; | |
if (this.length < 0x10000) { | |
var dels = this.reserveDels.sort(function(a, b) { return b - a }); | |
} | |
else { | |
var dels = this.reserveDels.sort(function(a, b) { return b - a }); | |
} | |
for (var i = 0, l = dels.length; i < l; i ++) { | |
this.del(dels[i]); | |
} | |
this.reserveDels = []; | |
this.idIndexMap = null; | |
}; | |
NodeSet.prototype.createIdIndexMap = function() { | |
if (this.idIndexMap) { | |
return this.idIndexMap; | |
} | |
else { | |
var map = this.idIndexMap = {}; | |
var nodes = this.nodes; | |
for (var i = 0, l = nodes.length; i < l; i ++) { | |
var node = nodes[i]; | |
/*@cc_on @if (@_jscript) | |
var sourceIndex = node.sourceIndex; | |
var subIndex = node.subIndex; | |
if (!map[sourceIndex]) map[sourceIndex] = {}; | |
map[sourceIndex][subIndex] = i; | |
@else @*/ | |
var id = NodeID.get(node); | |
map[id] = i; | |
/*@end @*/ | |
} | |
return map; | |
} | |
}; | |
NodeSet.prototype.del = function(index) { | |
this.length --; | |
if (this.only) { | |
delete this.only; | |
} | |
else { | |
var node = this.nodes.splice(index, 1)[0]; | |
if (this._first == node) { | |
delete this._first; | |
delete this._firstSourceIndex; | |
delete this._firstSubIndex; | |
} | |
/*@cc_on @if (@_jscript) | |
delete this.seen[node.sourceIndex][node.subIndex]; | |
@else @*/ | |
delete this.seen[NodeID.get(node)]; | |
/*@end @*/ | |
} | |
}; | |
NodeSet.prototype.delDescendant = function(elm, offset) { | |
if (this.only) return; | |
var nodeType = elm.nodeType; | |
if (nodeType != 1 && nodeType != 9) return; | |
if (uai.applewebkit2) return; | |
// element || document | |
if (!elm.contains) { | |
if (nodeType == 1) { | |
var _elm = elm; | |
elm = { | |
contains: function(node) { | |
return node.compareDocumentPosition(_elm) & 8; | |
} | |
}; | |
} | |
else { | |
// document | |
elm = { | |
contains: function() { | |
return true; | |
} | |
}; | |
} | |
} | |
var nodes = this.nodes; | |
for (var i = offset + 1; i < nodes.length; i ++) { | |
/*@cc_on @if (@_jscript) | |
if (nodes[i].node.nodeType == 1 && elm.contains(nodes[i].node)) { | |
@else @*/ | |
if (elm.contains(nodes[i])) { | |
/*@end @*/ | |
this.del(i); | |
i --; | |
} | |
} | |
}; | |
NodeSet.prototype._add = function(node, reverse) { | |
/*@cc_on @if (@_jscript) | |
var first, firstSourceIndex, firstSubIndex, sourceIndex, subIndex, attributeName; | |
sourceIndex = node.sourceIndex; | |
subIndex = node.subIndex; | |
attributeName = node.attributeName; | |
seen = this.seen; | |
seen = seen[sourceIndex] || (seen[sourceIndex] = {}); | |
if (node.nodeType == 2) { | |
seen = seen[subIndex] || (seen[subIndex] = {}); | |
if (seen[attributeName]) { | |
return true; | |
} | |
seen[attributeName] = true; | |
} | |
else { | |
if (seen[subIndex]) { | |
return true; | |
} | |
seen[subIndex] = true; | |
} | |
if (sourceIndex >= 0x10000 || subIndex >= 0x10000) { | |
this.shortcut = false; | |
} | |
// if this._first is undefined and this.nodes is not empty | |
// then first node shortcut is disabled. | |
if (this._first || this.nodes.length == 0) { | |
first = this._first; | |
firstSourceIndex = this._firstSourceIndex; | |
firstSubIndex = this._firstSubIndex; | |
if (!first || firstSourceIndex > sourceIndex || (firstSourceIndex == sourceIndex && firstSubIndex > subIndex)) { | |
this._first = node; | |
this._firstSourceIndex = sourceIndex; | |
this._firstSubIndex = subIndex | |
} | |
} | |
@else @*/ | |
var seen = this.seen; | |
var id = NodeID.get(node); | |
if (seen[id]) return true; | |
seen[id] = true; | |
/*@end @*/ | |
this.length++; | |
if (reverse) | |
this.nodes.unshift(node); | |
else | |
this.nodes.push(node); | |
}; | |
NodeSet.prototype.unshift = function(node) { | |
if (!this.length) { | |
this.length ++; | |
this.only = node; | |
return | |
} | |
if (this.only){ | |
var only = this.only; | |
delete this.only; | |
this.unshift(only); | |
this.length --; | |
} | |
/*@cc_on | |
node = this.createWrapper(node); | |
@*/ | |
return this._add(node, true); | |
}; | |
NodeSet.prototype.push = function(node) { | |
if (!this.length) { | |
this.length ++; | |
this.only = node; | |
return; | |
} | |
if (this.only) { | |
var only = this.only; | |
delete this.only; | |
this.push(only); | |
this.length --; | |
} | |
/*@cc_on | |
node = this.createWrapper(node); | |
@*/ | |
return this._add(node); | |
}; | |
NodeSet.prototype.first = function() { | |
if (this.only) return this.only; | |
/*@cc_on | |
if (this._first) return this._first.node; | |
if (this.nodes.length > 1) this.sort(); | |
var node = this.nodes[0]; | |
return node ? node.node : undefined; | |
@*/ | |
if (this.nodes.length > 1) this.sort(); | |
return this.nodes[0]; | |
}; | |
NodeSet.prototype.list = function() { | |
if (this.only) return [this.only]; | |
this.sort(); | |
/*@cc_on | |
var i, l, nodes, results; | |
nodes = this.nodes; | |
results = []; | |
for (i = 0, l = nodes.length; i < l; i ++) { | |
results.push(nodes[i].node); | |
} | |
return results; | |
@*/ | |
return this.nodes; | |
}; | |
NodeSet.prototype.string = function() { | |
var node = this.only || this.first(); | |
return node ? NodeUtil.to('string', node) : ''; | |
}; | |
NodeSet.prototype.bool = function() { | |
return !! (this.length || this.only); | |
}; | |
NodeSet.prototype.number = function() { | |
return + this.string(); | |
}; | |
NodeSet.prototype.iterator = function(reverse) { | |
this.sort(); | |
var nodeset = this; | |
if (!reverse) { | |
var count = 0; | |
return function() { | |
if (nodeset.only && count++ == 0) return nodeset.only; | |
/*@cc_on @if(@_jscript) | |
var wrapper = nodeset.nodes[count++]; | |
if (wrapper) return wrapper.node; | |
return undefined; | |
@else @*/ | |
return nodeset.nodes[count++]; | |
/*@end @*/ | |
}; | |
} | |
else { | |
var count = 0; | |
return function() { | |
var index = nodeset.length - (count++) - 1; | |
if (nodeset.only && index == 0) return nodeset.only; | |
/*@cc_on @if(@_jscript) | |
var wrapper = nodeset.nodes[index]; | |
if (wrapper) return wrapper.node; | |
return undefined; | |
@else @*/ | |
return nodeset.nodes[index]; | |
/*@end @*/ | |
}; | |
} | |
}; | |
var install = function(win) { | |
win = win || this; | |
var doc = win.document; | |
var undefined = win.undefined; | |
win.XPathExpression = function(expr) { | |
if (!expr.length) { | |
throw win.Error('no expression'); | |
} | |
var lexer = this.lexer = Lexer(expr); | |
if (lexer.empty()) { | |
throw win.Error('no expression'); | |
} | |
this.expr = BinaryExpr.parse(lexer); | |
if (!lexer.empty()) { | |
throw win.Error('bad token: ' + lexer.next()); | |
} | |
}; | |
win.XPathExpression.prototype.evaluate = function(node, type) { | |
return new win.XPathResult(this.expr.evaluate(new Ctx(node)), type); | |
}; | |
win.XPathResult = function (value, type) { | |
if (type == 0) { | |
switch (typeof value) { | |
case 'object': type ++; // 4 | |
case 'boolean': type ++; // 3 | |
case 'string': type ++; // 2 | |
case 'number': type ++; // 1 | |
} | |
} | |
this.resultType = type; | |
switch (type) { | |
case 1: | |
this.numberValue = value.isNodeSet ? value.number() : +value; | |
return; | |
case 2: | |
this.stringValue = value.isNodeSet ? value.string() : '' + value; | |
return; | |
case 3: | |
this.booleanValue = value.isNodeSet ? value.bool() : !! value; | |
return; | |
case 4: case 5: case 6: case 7: | |
this.nodes = value.list(); | |
this.snapshotLength = value.length; | |
this.index = 0; | |
this.invalidIteratorState = false; | |
break; | |
case 8: case 9: | |
this.singleNodeValue = value.first(); | |
return; | |
} | |
}; | |
win.XPathResult.prototype.iterateNext = function() { return this.nodes[this.index++] }; | |
win.XPathResult.prototype.snapshotItem = function(i) { return this.nodes[i] }; | |
win.XPathResult.ANY_TYPE = 0; | |
win.XPathResult.NUMBER_TYPE = 1; | |
win.XPathResult.STRING_TYPE = 2; | |
win.XPathResult.BOOLEAN_TYPE = 3; | |
win.XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4; | |
win.XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5; | |
win.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6; | |
win.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7; | |
win.XPathResult.ANY_UNORDERED_NODE_TYPE = 8; | |
win.XPathResult.FIRST_ORDERED_NODE_TYPE = 9; | |
doc.createExpression = function(expr) { | |
return new win.XPathExpression(expr, null); | |
}; | |
doc.evaluate = function(expr, context, _, type) { | |
return doc.createExpression(expr, null).evaluate(context, type); | |
}; | |
}; | |
var win; | |
if (config.targetFrame) { | |
var frame = document.getElementById(config.targetFrame); | |
if (frame) win = frame.contentWindow; | |
} | |
if (config.exportInstaller) { | |
window.install = install; | |
} | |
if (!config.hasNative || !config.useNative) { | |
install(win || window); | |
} | |
})(); | |
// Thanks for reading this source code. We love JavaScript. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment