/* jQuery.hoke
 * simple page level filtering search
 * Hugo Bonacci
 * http://creativecommons.org/licenses/by/3.0/
 */

$.fn.hoke = function(options) {
    
    //default settings
    var settings = $.extend({
        cache:true,
        sort:false,
        fields:{},
        score:{},
        highlight:true,
        highlightCss:"hoke-highlight",
        onActivate:function() { },
        onUpdate:function() { },
        onEmpty:function() { }
    }, options);

    //handles setting up a search container
    var __init_hoke = function(target) {
        var instance = $(target);
        
        //create the functionality
        var self = {
            ui:{
                container:instance,
                filter:$(options.search)
            },
            
            //current phrase to search by
            getPhrase:function() {
                return $.trim(self.ui.filter.val());
            },
        
            //returns the records used for the filter
            records:function() {
                if (self._records) return self._records;
            
                //find all values that match
                var records = [];
                self.ui.container.find(settings.node).each(function(index, node) {
                    
                    //create the default value
                    var record = { node:$(node), fields:{}, values:{} };
                    
                    //get each of the field values
                    $.each(settings.fields, function(name, selector) {
                        var detail = record.node.find(selector);
                        record.fields[name] = detail;
                        record.values[name] = detail.text();
                    });
    
                    //add it to the collection
                    records.push(record);
                    
                });
                
                //save for later if needed
                if (settings.cache) self._records = records;
                return records;
            },
            
            //find all of the matches to display
            filter:function(phrase) {
                phrase = $.trim(phrase);
                self._previous = phrase;
            
                //format the phrase to use in searching
                var search = (phrase || "").replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&").split(/\s+/g);
                search.sort(function(a, b) { return a.length > b.length });
                
                //update each of the phrases
                $.each(search, function(i, v) { search[i] = new RegExp($.trim(v), "gi"); });
                
                //perform the query to find these values
                var matches = [];
                $.each(self.records(), function(index, record) {
                    var score = 0;
                    
                    //check if this is a match or not
                    $.each(record.values, function(name, value) {
                        
                        //perform highlighting/matching in same call
                        $.each(search, function(j, part) {       
                            value = value.replace(part, function(match) {
                            
                                //add up the score for this match
                                if (name in settings.score) score += (settings.score[name] * match.length);
                                else score += match.length;
                                
                                //return the correct markup depending on settings
                                if (!settings.highlight) return match;
                                
                                //highlight the match
                                return ["<span class='", settings.highlightCss, "'>", match, "</span>"].join("");
                                
                            });
                        });
                        
                        //apply highlighting if needed
                        if (settings.highlight) record.fields[name].html(value);
                        
                    });
                    
                    //check the score if this needs to be removed or not
                    if (score == 0 && phrase.length > 0) {
                        record.node.hide();
                        return;
                    }
                    
                    //save the match
                    matches.push({match:record, score:score});
                    
                });
                
                //determine if this needs to sort or not
                if (settings.sort && phrase.length > 0) matches.sort(self.sort);
                
                //display the correct result
                self.display(matches);
                if (matches.length == 0) settings.onEmpty.apply(instance);
            },
            
            //displays results into the view
            display:function(records) {
                $.each(records, function(index, record) {
                    var record = record.match ? record.match.node : record.node;
                    self.ui.container.append(record.show());
                });
                settings.onUpdate.apply(instance);
            },
            
            //handles sorting by score
            sort:function(a, b) {
                return a.score > b.score ? -1
                    : a.score < b.score ? 1
                    : 0
            },
            
            //prepares the control
            init:{
                
                //starts preparing the control
                start:function() {
                    if (settings.cache) self.records();
                    self.init._filter();
                    self.init._activate();
                    
                    //lastly, perform the filtering if any
                    var phrase = self.getPhrase();
                    if (phrase.length > 0) self.filter(phrase);
                },
                
                //prepares the search input
                _filter:function() {
                    self.ui.filter.click(function() { self.ui.filter.select(); });
                    self.ui.filter.keyup(function() {
                        var value = self.getPhrase();
                        if (value == self._previous) return;
                        
                        window.setTimeout(function() {
                            if (value != self.getPhrase()) return;
                            self.filter(value);
                        }, 20);
                    });
                },
                
                //activates the control for use
                _activate:function() {
                    settings.onActivate.apply(instance);
                }
            }
            
        };
        
        //prepare the control
        self.init.start();
    
    };
    
    //initialize each of the targets
    $.each(this, function(i, target) { __init_hoke(target); })
    
};
