/* JavaScript-XPath 0.1.11 * (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.prototyps = 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.