/* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
   ••  Project: jQuery LazyLoad For Advertisement                   ••
   ••  Author:  delarueguillaume@gmail.com                          ••
   ••  WebSite : http://www.web2ajax.fr/                            ••
   ••  Date:    2010                                                ••
   ••  Version: 1.2 (30 march 2010) : Hight Imporvements of code    ••   
   ••  Version: 1.1 (25 march 2010) : Improve IE compatibility      ••
   ••  Version: 1.0 (24 march 2010)                                 ••
   •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••   
   
    Defer Advertisement loading and load only if the ad is in the visible part of
    the page like lazyLoad for pictures.
    -> Highly improve general page load
    -> Improve cpu load of visitor in case of displaying flash banners (only displayed if necessary)
    –> Compatibility with AdSense and many other advertisers
    -> Cross Browser suupprt (IE5.5+, Firefox, Opera, Chrome, Safari)
    
    A big thanks to Mika Tuupola for the Lazy load part
    -> http://www.appelsiini.net/projects/lazyload 
    
    Another big thanks to Thomas Aylott and his MooTools based document.write replacement
    -> http://SubtleGradient.com/
    
---------------------------------------------------------------------- */
 
(function($) { 

	$.lazyLoadAdRunning = false ;
	$.lazyLoadAdTimers = [] ;
	
    $.fn.lazyLoadAd = function(options) {
        var settings = {
            threshold    : 0,
            failurelimit : 1,
            forceLoad	 : false,
            event        : "scroll",
            viewport	 : window,
            onComplete	 : false,
            timeout		 : 2000,
            debug		 : true
        };
                
        if(options) {
            $.extend(settings, options);
        }
        
        /* Write logs if possible in console */
        function _debug() {
        	if ( typeof console != 'undefined' && settings.debug ) {	
        		var args = [] ;
        		for ( var i = 0 ; i < arguments.length ; i++ ) args.push(arguments[i]) ;
        		try { console.log('LazyLoadAD |', args) ; } catch(e) {} ;
        	}
        }

        /* Fire one scroll event per scroll. Not one scroll event per image. */
        var elements = this;
        
        $(settings.viewport).bind("checkLazyLoadAd", function() {
        	var counter = 0;
        	elements.each(function() {                	
        		if ( $.lazyLoadAdRunning ) {
        			if ( $.lazyLoadAdTimers['runTimeOut'] ) clearTimeout( $.lazyLoadAdTimers['runTimeOut'] ) ;
        			 $.lazyLoadAdTimers['runTimeOut'] = setTimeout(function() {
        				$(settings.viewport).trigger("checkLazyLoadAd") ; 
        			}, 800) ;
        			return false ;
        		} else if ( settings.forceLoad == true ) {
        			$(this).trigger("load");
        		} else if (!$.belowthefold(this, settings) && !$.abovethetop(this, settings)) {
        	        $(this).trigger("load");
        	    } else {
        	        if (counter++ > settings.failurelimit) {
        	            return false;
        	        }
        	    }
        	});
        	/* Remove image from array so it is not looped next time. */
        	var temp = $.grep(elements, function(element) {
        	    return !element.loaded;
        	});
        	elements = $(temp);
        }) ; 
        
        
        if ("scroll" == settings.event) {
            $(settings.viewport).bind("scroll", function(event) {
               $(settings.viewport).trigger("checkLazyLoadAd") ;                
            });
        }
        
        this.each(function() {
            var self = this;
            
            /* Save original only if it is not defined in HTML. */
            if (undefined == $(self).attr("original")) {
                $(self).attr("original", $(self).attr("src"));     
            }

            if ("scroll" != settings.event || 
                    undefined == $(self).attr("src") || 
                    ($.abovethetop(self, settings) ||
                     $.belowthefold(self, settings) )) {
                $(self).removeAttr("src");
                self.loaded = false;
            } else {
                self.loaded = true;
            }
            
            /* When appear is triggered load original image. */
            $(self).one("load", function() {
            	
                if (!this.loaded) {
                
                	// Lock other ads load
                	$.lazyLoadAdRunning = true ;
                	
                	// Bind an ad load complete
                	$(self).one('lazyLoadComplete', function() {
                		$.lazyLoadAdRunning = false ;
                		$(self).attr("src", $(self).attr("original")) ;
                		$(self).removeAttr("original") ;
                		self.loaded = true;
                		if ( typeof settings.onComplete == 'function') {
                			try { settings.onComplete() } catch(e) {} ;
                		}
                	}) ;
                	
                	// Eval script into pub_container for adSense by example
					var scripts = [],
						script,
						regexp = /<code[^>]*>([\s\S]*?)<\/code>/gi;
						
					while ((script = regexp.exec($(self).html()))) {
						scripts.push(script[1].replace('<!--', '').replace('//-->', ''));
					}
					
					if ( scripts.length ) scripts = scripts.join('\n');
					else scripts = '' ;
					
					// -- Eval param code before calling ad script					
					try {
						if ( scripts != '' ) eval(scripts); 
					} catch(e) {} ;
					
					// -- Override the document.write defaults
					var wrapper  = $('<div>'),
						fragment = document.createDocumentFragment(),
						numWrappers = 0,
						isDocumentWriteOverload = false;
					
					document._writeOriginal = document.write;
					document.write = function(){
						var args = arguments, id = 'document_write' + new Date().getTime().toString(36);
						id = $('<span>',{id:id}) ; $(document.write.context).append(id);
						
						_debug('Overload document.write : ', id, document.write.context, args) ;
						isDocumentWriteOverload = true ;
						numWrappers++ ;
						
						function documentWrite(){
							numWrappers-- ;
							var html=''; for(var i=0;i<args.length;i++) html+=args[i] ;
							_debug("Fragment append : ", numWrappers, id.attr('id'), html);
							fragment.appendChild($(wrapper.html(html))[0]);
							id.replaceWith(fragment) ;
							if ( numWrappers == 0 ) $(document.write.context).trigger('lazyLoadComplete') ;
						}
						
						setTimeout(documentWrite, 0);
					};
					
					// -- Define the context
					document.write.context = $(self)[0];
					
					// -- Call script and let's dance
					_debug('------------------------------  Lazy Load Ad CALL ----') ;
					_debug('Context : '+ document.write.context ) ;	
					
					// -- Append adScript after context and bind load
					var script = document.createElement("script"), url =  $(self).attr("original") ;
					script.src = url;
					script.type = "text/javascript";
					document.write.context.appendChild(script);  // add script tag to head element
					
					// -- Set the callback function, when script is loaded
					var callback = function() {
						_debug('Load Ad Code', url ) ;
						
						// -- In case of no use of 'document.write' in the JS file (standalone JS)
						setTimeout(function() {
							if ( ! isDocumentWriteOverload ) {
								_debug('... is considred as injected') ;
								$(document.write.context).trigger('lazyLoadComplete') ;
							}
							$(script).remove() ;
						}, 300) ;
						
					} ;
					
					// test for onreadystatechange to trigger callback
					script.onreadystatechange = function () {
						if (script.readyState == 'loaded' || script.readyState == 'complete') {
							callback();
						}
					}                            
					// test for onload to trigger callback
					script.onload = function () {
						callback();
						return;
					}
					// safari doesn't support either onload or readystate, create a timer
					// only way to do this in safari
					try {
						if ((Prototype.Browser.WebKit && !navigator.userAgent.match(/Version\/3/)) || Prototype.Browser.Opera) { // sniff
							$.lazyLoadAdTimers[url] = setInterval(function() {
								if (/loaded|complete/.test(document.readyState)) {
									clearInterval($.lazyLoadAdTimers[url]);
									callback(); // call the callback handler
								}
							}, 10);
						}
					}catch(e) {};
					
					// In case of error, set a timer to simulate a good load and call next ad
					if ( $.lazyLoadAdTimers['failBack'] ) clearTimeout($.lazyLoadAdTimers['failBack']) ;
					$.lazyLoadAdTimers['failBack'] = setTimeout(function() {
						if ( $.lazyLoadAdRunning ) {
							_debug('Fail back actioned after '+settings.timeout+' ms');
							callback() ;
						}
					}, settings.timeout) ;
                };
            });

            /* When wanted event is triggered load ad */
            /* by triggering appear.                              */
            if ("scroll" != settings.event) {
                $(self).bind(settings.event, function(event) {
                    if (!self.loaded) {
                        $(self).trigger("load");
                    }
                });
            }
        });
        
        /* Force initial check if images should appear. */
        $(settings.viewport).trigger('checkLazyLoadAd');
        
        return this;

    };

    /* Convenience methods in jQuery namespace.           */
    /* Use as  $.belowthefold(element, {threshold : 100, container : window}) */
    
    $.belowthefold = function(element, settings) {
        if (settings.viewport === undefined || settings.viewport === window) {
            var fold = $(window).height() + $(window).scrollTop();
        } else {
            var fold = $(settings.viewport).offset().top + $(settings.viewport).height();
        }
        return fold <= $(element).offset().top - settings.threshold;
    };
        
    $.abovethetop = function(element, settings) {
        if (settings.viewport === undefined || settings.viewport === window) {
            var fold = $(window).scrollTop();
        } else {
            var fold = $(settings.viewport).offset().top;
        }
        return fold >= $(element).offset().top + settings.threshold  + $(element).height();
    };
    
})(jQuery);
