151 lines
4.2 KiB
JavaScript
151 lines
4.2 KiB
JavaScript
// 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 <code>DIV, SPAN</code> the matcher will not match
|
|
* <code>DIV, DIV, SPAN</code> because it starts matching at the first
|
|
* <code>DIV</code>, fails to match <code>SPAN</code> 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.<goog.dom.pattern.AbstractPattern>}
|
|
* @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.<Function>}
|
|
* @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);
|
|
};
|