/**
 * @fileOverview jQuery Listener Plugin, an alternative to .ready()
 * @author <a href="mailto:info@codymarquart.com">Cody Marquart</a>
 * @version 1.0
 *
 */
/*
 * Copyright (c) 2009, Cody L. Marquart
 * 
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that
 * the above copyright notice and this permission notice appear in all
 * copies.

 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
 * USE OR PERFORMANCE OF THIS SOFTWARE.
 */
(function($){	
  /** 
   * @name $
   * @namespace jQuery
   */ 
  var $ = $;
  
  /** 
   * @name fn
   */ 
  var fn = fn;
  
  /** 
   * @name $.fn
     @borrows fn
   * @namespace $.fn
     @private
   */
  $.fn = $.fn;
  
  /**
   * Holds all listener objects created
   *
   * @name listener
   * @public
   */
  var listener = {
        ears:[]
      };
  var defaults = {
        tolerance:10,
        limit:1000,
        readyProtect:true,
        passOff:true
      };
  
  
  /**
   * @description Used to override the default Listener settings.
   * @constructor
   * @param {JSON Object} ov Override and extend the default settings.
   */
  $.listen=function(ov){
    defaults=$.extend(defaults,ov);
  };
  
  /**
   * @constructor
   * @name $.fn.listen
   * @description Main Listen function
   *
   * @param {jQuery} sels Selectors
   * @param {JSON Object} opts Overrides default settings
   */
  $.fn.listen=function(sels,opts) {
    /**
     * @function
     * @name clear
     */
  	listener.clear=function(n) {
  	  if(typeof n!="number") {
  	    for(var e in listener.ears) {
  	      listener.ears[e].clear();
  	    }
  	  } else {
        listener.ears[n].clear();
      }
    };
    
	  var context=this.context||this.selector;
	  
	  return this.each(function(){
	    if (typeof sels=="string"){
	      sels=(sels.indexOf(",")>=0)?sels.split(","):[sels];
	    }
	    for(var s in sels){
	      listener.ears.push(new Listener(sels[s],context));
	     }
	  });
	  
	  /**
	   * Listener Object.  
	   * 
	   * @inner
     * @ignore
	   * @constructor Represents a new Listener object
	   * @param {jQuery selector} obj - DOM object the Listener object is waiting for.
	   * @param {jQuery selector} cntxt - Context within which the Listener is watching.
	   */
	  function Listener(obj,cntxt) {
      /**
       * Checks to see if the DOM element we're waiting for exists. If not, 
       * set a timeout accoring to 
       * @ignore
       */
	    this.wait=function(){
	      if(this.opts.readyProtect&&this.ready) {
	        this.block();
	      } else {
	        if((this.opts.limit!=-1)&&(this.count>=this.opts.limit)) {
	          this.clear();
	          if(this.opts.finish) {
	            this.opts.finish(1);
	          }
	        } else {	
	          var ob=$(this.cntxt).find(this.selector);
        		var r=[];
	          $(ob).each(function() {
        		  if($(this).data("heard")!=true) {
		            $(this).data("heard",true);
		            r.push(this);
		          }
	      	  });
		        if(r.length>0) {
		          this.opts.success(r);
		        } else {
		          this.clear();
		          this.opts.finish(1);
		        };
		        
  	        if((!this.opts.readyProtect)||(this.opts.readyProtect&&!this.ready)) {
              this.timer=setTimeout("listener.ears["+this.index+"].wait('"+this.selector+"',"+this.index+")",this.opts.tolerance);
            }
	        }
	        this.count++;
	      }
	    };
	    
      /**
       * @function
       * @ignore
       */
	    this.block=function() {
	      this.clear();
	      if(this.opts.finish) {
	        this.opts.finish(2);
	      }
	      if(this.opts.passOff) {
	        var ob=$(cntxt).find(this.selector);
	        if(ob.length>0) {
	          this.opts.success(ob);
	        }
	      }
	    };
	    
      /**
       * @function
       * @ignore
       */
	    this.clear=function() {
	      this.timer=clearTimeout(this.timer);
	    };
	    
  	  $.extend(this,{
  	    selector:obj,
  	    cntxt:context||"",
  	    count:0,
  	    timer:0,
  	    index:listener.ears.length,
  	    opts:$.extend(defaults,opts),
  	    ready:(typeof $().ready(function(){})=="object")?true:false
  	  });
  	  
	    this.opts["finish"]=this.opts.finish||function(){};
	    this.wait();
	  };
  };
})(jQuery);

