mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-01 02:12:58 +08:00
400282282f
This closes #226
344 lines
6.6 KiB
JavaScript
344 lines
6.6 KiB
JavaScript
/**
|
|
* Copyright 2011 Rackspace
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
var sprintf = require('./sprintf').sprintf;
|
|
|
|
var utils = require('./utils');
|
|
var SyntaxError = require('./errors').SyntaxError;
|
|
|
|
var _cache = {};
|
|
|
|
var RE = new RegExp(
|
|
"(" +
|
|
"'[^']*'|\"[^\"]*\"|" +
|
|
"::|" +
|
|
"//?|" +
|
|
"\\.\\.|" +
|
|
"\\(\\)|" +
|
|
"[/.*:\\[\\]\\(\\)@=])|" +
|
|
"((?:\\{[^}]+\\})?[^/\\[\\]\\(\\)@=\\s]+)|" +
|
|
"\\s+", 'g'
|
|
);
|
|
|
|
var xpath_tokenizer = utils.findall.bind(null, RE);
|
|
|
|
function prepare_tag(next, token) {
|
|
var tag = token[0];
|
|
|
|
function select(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
elem._children.forEach(function(e) {
|
|
if (e.tag === tag) {
|
|
rv.push(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
function prepare_star(next, token) {
|
|
function select(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
elem._children.forEach(function(e) {
|
|
rv.push(e);
|
|
});
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
function prepare_dot(next, token) {
|
|
function select(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
rv.push(elem);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
function prepare_iter(next, token) {
|
|
var tag;
|
|
token = next();
|
|
|
|
if (token[1] === '*') {
|
|
tag = '*';
|
|
}
|
|
else if (!token[1]) {
|
|
tag = token[0] || '';
|
|
}
|
|
else {
|
|
throw new SyntaxError(token);
|
|
}
|
|
|
|
function select(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
elem.iter(tag, function(e) {
|
|
if (e !== elem) {
|
|
rv.push(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
function prepare_dot_dot(next, token) {
|
|
function select(context, result) {
|
|
var i, len, elem, rv = [], parent_map = context.parent_map;
|
|
|
|
if (!parent_map) {
|
|
context.parent_map = parent_map = {};
|
|
|
|
context.root.iter(null, function(p) {
|
|
p._children.forEach(function(e) {
|
|
parent_map[e] = p;
|
|
});
|
|
});
|
|
}
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
|
|
if (parent_map.hasOwnProperty(elem)) {
|
|
rv.push(parent_map[elem]);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
|
|
function prepare_predicate(next, token) {
|
|
var tag, key, value, select;
|
|
token = next();
|
|
|
|
if (token[1] === '@') {
|
|
// attribute
|
|
token = next();
|
|
|
|
if (token[1]) {
|
|
throw new SyntaxError(token, 'Invalid attribute predicate');
|
|
}
|
|
|
|
key = token[0];
|
|
token = next();
|
|
|
|
if (token[1] === ']') {
|
|
select = function(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
|
|
if (elem.get(key)) {
|
|
rv.push(elem);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
};
|
|
}
|
|
else if (token[1] === '=') {
|
|
value = next()[1];
|
|
|
|
if (value[0] === '"' || value[value.length - 1] === '\'') {
|
|
value = value.slice(1, value.length - 1);
|
|
}
|
|
else {
|
|
throw new SyntaxError(token, 'Ivalid comparison target');
|
|
}
|
|
|
|
token = next();
|
|
select = function(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
|
|
if (elem.get(key) === value) {
|
|
rv.push(elem);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
};
|
|
}
|
|
|
|
if (token[1] !== ']') {
|
|
throw new SyntaxError(token, 'Invalid attribute predicate');
|
|
}
|
|
}
|
|
else if (!token[1]) {
|
|
tag = token[0] || '';
|
|
token = next();
|
|
|
|
if (token[1] !== ']') {
|
|
throw new SyntaxError(token, 'Invalid node predicate');
|
|
}
|
|
|
|
select = function(context, result) {
|
|
var i, len, elem, rv = [];
|
|
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
elem = result[i];
|
|
|
|
if (elem.find(tag)) {
|
|
rv.push(elem);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
};
|
|
}
|
|
else {
|
|
throw new SyntaxError(null, 'Invalid predicate');
|
|
}
|
|
|
|
return select;
|
|
}
|
|
|
|
|
|
|
|
var ops = {
|
|
"": prepare_tag,
|
|
"*": prepare_star,
|
|
".": prepare_dot,
|
|
"..": prepare_dot_dot,
|
|
"//": prepare_iter,
|
|
"[": prepare_predicate,
|
|
};
|
|
|
|
function _SelectorContext(root) {
|
|
this.parent_map = null;
|
|
this.root = root;
|
|
}
|
|
|
|
function findall(elem, path) {
|
|
var selector, result, i, len, token, value, select, context;
|
|
|
|
if (_cache.hasOwnProperty(path)) {
|
|
selector = _cache[path];
|
|
}
|
|
else {
|
|
// TODO: Use smarter cache purging approach
|
|
if (Object.keys(_cache).length > 100) {
|
|
_cache = {};
|
|
}
|
|
|
|
if (path.charAt(0) === '/') {
|
|
throw new SyntaxError(null, 'Cannot use absolute path on element');
|
|
}
|
|
|
|
result = xpath_tokenizer(path);
|
|
selector = [];
|
|
|
|
function getToken() {
|
|
return result.shift();
|
|
}
|
|
|
|
token = getToken();
|
|
while (true) {
|
|
var c = token[1] || '';
|
|
value = ops[c](getToken, token);
|
|
|
|
if (!value) {
|
|
throw new SyntaxError(null, sprintf('Invalid path: %s', path));
|
|
}
|
|
|
|
selector.push(value);
|
|
token = getToken();
|
|
|
|
if (!token) {
|
|
break;
|
|
}
|
|
else if (token[1] === '/') {
|
|
token = getToken();
|
|
}
|
|
|
|
if (!token) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
_cache[path] = selector;
|
|
}
|
|
|
|
// Execute slector pattern
|
|
result = [elem];
|
|
context = new _SelectorContext(elem);
|
|
|
|
for (i = 0, len = selector.length; i < len; i++) {
|
|
select = selector[i];
|
|
result = select(context, result);
|
|
}
|
|
|
|
return result || [];
|
|
}
|
|
|
|
function find(element, path) {
|
|
var resultElements = findall(element, path);
|
|
|
|
if (resultElements && resultElements.length > 0) {
|
|
return resultElements[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function findtext(element, path, defvalue) {
|
|
var resultElements = findall(element, path);
|
|
|
|
if (resultElements && resultElements.length > 0) {
|
|
return resultElements[0].text;
|
|
}
|
|
|
|
return defvalue;
|
|
}
|
|
|
|
|
|
exports.find = find;
|
|
exports.findall = findall;
|
|
exports.findtext = findtext;
|