// Copyright 2007 The Closure Library Authors. All Rights Reserved. // // 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. /** * @fileoverview DOM pattern matcher. Allows for simple searching of DOM * using patterns descended from {@link goog.dom.pattern.AbstractPattern}. * * @author robbyw@google.com (Robby Walker) */ goog.provide('goog.dom.pattern.Matcher'); goog.require('goog.dom.TagIterator'); goog.require('goog.dom.pattern.MatchType'); goog.require('goog.iter'); // TODO(robbyw): Allow for backtracks of size > 1. /** * Given a set of patterns and a root node, this class tests the patterns in * parallel. * * It is not (yet) a smart matcher - it doesn't do any advanced backtracking. * Given the pattern DIV, SPAN the matcher will not match * DIV, DIV, SPAN because it starts matching at the first * DIV, fails to match SPAN at the second, and never * backtracks to try again. * * It is also possible to have a set of complex patterns that when matched in * parallel will miss some possible matches. Running multiple times will catch * all matches eventually. * * @constructor */ goog.dom.pattern.Matcher = function() { this.patterns_ = []; this.callbacks_ = []; }; /** * Array of patterns to attempt to match in parallel. * * @type {Array.} * @private */ goog.dom.pattern.Matcher.prototype.patterns_; /** * Array of callbacks to call when a pattern is matched. The indexing is the * same as the {@link #patterns_} array. * * @type {Array.} * @private */ goog.dom.pattern.Matcher.prototype.callbacks_; /** * Adds a pattern to be matched. The callback can return an object whose keys * are processing instructions. * * @param {goog.dom.pattern.AbstractPattern} pattern The pattern to add. * @param {Function} callback Function to call when a match is found. Uses * the above semantics. */ goog.dom.pattern.Matcher.prototype.addPattern = function(pattern, callback) { this.patterns_.push(pattern); this.callbacks_.push(callback); }; /** * Resets all the patterns. * * @private */ goog.dom.pattern.Matcher.prototype.reset_ = function() { for (var i = 0, len = this.patterns_.length; i < len; i++) { this.patterns_[i].reset(); } }; /** * Test the given node against all patterns. * * @param {goog.dom.TagIterator} position A position in a node walk that is * located at the token to process. * @return {boolean} Whether a pattern modified the position or tree * and its callback resulted in DOM structure or position modification. * @private */ goog.dom.pattern.Matcher.prototype.matchToken_ = function(position) { for (var i = 0, len = this.patterns_.length; i < len; i++) { var pattern = this.patterns_[i]; switch (pattern.matchToken(position.node, position.tagType)) { case goog.dom.pattern.MatchType.MATCH: case goog.dom.pattern.MatchType.BACKTRACK_MATCH: var callback = this.callbacks_[i]; // Callbacks are allowed to modify the current position, but must // return true if the do. if (callback(pattern.matchedNode, position, pattern)) { return true; } default: // Do nothing. break; } } return false; }; /** * Match the set of patterns against a match tree. * * @param {Node} node The root node of the tree to match. */ goog.dom.pattern.Matcher.prototype.match = function(node) { var position = new goog.dom.TagIterator(node); this.reset_(); goog.iter.forEach(position, function() { while (this.matchToken_(position)) { // Since we've moved, our old pattern statuses don't make sense any more. // Reset them. this.reset_(); } }, this); };