The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
if ( typeof DOM == "undefined") DOM = {};

DOM.Find = {

  VERSION: 1.00,

  EXPORT: [ 'checkAttributes','getElementsByAttributes', 'geba' ],

  checkAttributes: function(hash,el){

      // Check that passed arguments make sense

      if( el === undefined || el === null )
        throw("Second argument to checkAttributes should be a DOM node or the ID of a DOM Node");

      if( el.constructor === String )
        el = document.getElementById(el);

      if( el === null || !el.nodeType ) // Make sure el is a Node
        throw("Second argument to checkAttributes should be a DOM node or the ID of a DOM Node");

      if(! (hash instanceof Object))
        throw("First argument to checkAttributes should be an Object of attribute/test pairs. See the documentation for more information.");

      // If we're still here, check the test pairs

      for(key in hash){

        /*
          Prepare the "pointer"
        */

        // Check to make sure property chain is valled
        // Provides easy declaration of nested propteries
        // Example: {'style.position':'absolute'}

        var pointer = el      // pointer
        var last    = null;   // last pointer used to aplly() later

        var pieces  = key.split('.');                   // break up the property chain

        for(var i=0; i<pieces.length; i++){             // loop property chain
          // There can be no match
          // if the attribute does not exist
          if(!pointer[pieces[i]]) return false;         // test the pointer exists
          // Save the current pointer
          last    = pointer;                            // backup current pointer
          // Develope the pointer
          pointer = pointer[pieces[i]];                 // stack the pointer
        }

        // Check if the pointer is actually a function
        // Provides easy declaration of methods
        // Example: {'hasChildNodes':true}
        // Example: {'firstChild.hasChildNodes':true}

        // Does not work in IE
        // IE returns Object instead of Function
        if( pointer instanceof Function )
          try {
            pointer = pointer.apply(last);
          }catch(error){
            throw("First agrument to checkAttributes included a Function Refrence which caused an ERROR: " +  error);
          }

        /*
          Test "pointer" against "value"
        */

        // Perform one of 3 tests
        // Regex, Function, Scalar

        // Check against a regex
        if( hash[key] instanceof RegExp ){
          if( !hash[key].test( pointer ) )
             return false;

        // Check against a function
        }else if( hash[key] instanceof Function ){
          if( !hash[key]( pointer ) )
            return false;

        // Or check against a scalar value
        }else if( hash[key] != pointer ){
          return false;
        }

      }

      return true;
  },

  getElementsByAttributes: function( searchAttributes, startAt, resultsLimit, depthLimit ) {

     // if we haven't been deep enough yet
     if(depthLimit !== undefined && depthLimit <= 0) return [];

     // if no startAt is provided use document as default
     if(startAt === undefined){
       startAt = document;

     // if startAt is a string convert it to a domref
     }else if(typeof startAt == 'string'){
       startAt = document.getElementById(startAt);
     }

     // check the startAt element
     var results = DOM.Find.checkAttributes(searchAttributes, startAt) ? [ startAt ] : [];

     // return the results right away if they only want 1 result
     if(resultsLimit == 1 && results.length > 0) return results;

     // Scan the childNodes of startAt
     if (startAt.childNodes)
       for( var i = 0; i < startAt.childNodes.length; i++){
         // concat onto results any childNodes that match
         results = results.concat(
            DOM.Find.getElementsByAttributes( searchAttributes, startAt.childNodes[i], (resultsLimit) ? resultsLimit - results.length : undefined, (depthLimit) ? depthLimit -1 : undefined )
         )
         if (resultsLimit !== undefined && results.length >= resultsLimit) break;
       }

     return results;
  }

}

/*

=head1 AUTHOR

Daniel, Aquino <mr.danielaquino@gmail.com>.

=head1 COPYRIGHT

  Copyright (c) 2007 Daniel Aquino.
  Released under the Perl Licence:
  http://dev.perl.org/licenses/

*/