123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /**
- * 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;
|