/**
 * Database created from elements with an extra "searchdata" attribute
 * that contains JSON. The database can be used to selectively show and
 * hide the elements in the database, or just find elements to call
 * methods on.
 *
 * Requires: jQuery 1.3
 */
var ElementDatabase = function(id) {
    var element = $(id);
    var records = $([]);
    var objects = $([]);
    var loaded = false;
        
    function load() {
        if( ! loaded ) {
            element.children().each(function(i,child) {
                var data = child.getAttribute("searchdata");
                if( data ) {
                    records.push({object:child,properties:eval("(" + data + ")")});
                    objects.push(child);
                }
            });
        }
        loaded = true;
    }
    
    function matchKeyPath(wildcards,target,path,value) {
        var part = path.shift();
        
        if( target == null )
            return false;
        
        if( path.length == 0 )
            return target[part] == value;
        
        if( part == "*" ) {
            if(wildcards.hasOwnProperty(path.length)) {
                var keys = wildcards[path.length];
                for( var i=0; i < keys.length; i++ ) {
                    if( matchKeyPath(wildcards,target[keys[i]],[].concat(path),value) )
                        return true;
                }
            } else {
                keys = [];
                for( var key in target ) {
                    if( target[key] != null ) {
                        if( matchKeyPath(wildcards,target[key],[].concat(path),value) ) {
                            keys.push(key);
                        }
                    }
                }
                wildcards[path.length] = keys;
                return keys.length;
            }
        } else {
            return matchKeyPath(wildcards,target[part],[].concat(path),value);
        }
        
        return false;
    }
    
    this.find = function( expr ) {
        var result = $([]);

        load();

        records.each(function(i,record) {
            if( expr(record.properties) ) {
                result.push(record.object);
            }
        });
                
        return result;
    }
    
    this.findByPropertyMap = function( query ) {
        return this.find(function(properties) {
            var wildcards = {};
            
            for( var path in query ) {
                var value = String(query[path]).toLowerCase();
                if( ! matchKeyPath(wildcards,properties,path.split("."),value) )
                    return false;
            }
            
            return true;
        });
    }
    
    this.show = function( expr ) {
        var result = this.find(expr);
        result.show();
        return result;
    }
    
    this.showByPropertyMap = function( query ) {
        var result = this.findByPropertyMap(query);
        result.show();
        return result;
    }
    
    this.showAll = function() {
        load();
        objects.show();
    }
    
    this.hideAll = function() {
        load();
        objects.hide();
    }
};
