/**  
 *  Script lazy loader 0.5 - Modified By Web2ajaX
 *  Copyright (c) 2008 Bob Matsuoka
 *
 *  This program is free software; you can redistribute it and/or 
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  Lazyloader updated for a jQuery implementation and appendChild in context
 *  not only in document.body
 *  Add script remove on callback to clean space.
 *
 */

var LazyLoader = {}; //namespace
LazyLoader.timer = {}; // contains timers for scripts
LazyLoader.scripts = []; // contains called script references
LazyLoader.load = function (url, context, callback) {
    // handle object or path
    var classname = null,
        properties = null;
    try {

        // make sure we only load once
        // note that we loaded already
        LazyLoader.scripts.push(url);
        var script = document.createElement("script");
        script.src = url;
        script.type = "text/javascript";
        context.get(0).appendChild(script); // add script tag to head element
        // was a callback requested
        if (callback) {
            // test for onreadystatechange to trigger callback
            script.onreadystatechange = function () {
                if (script.readyState === 'loaded' || script.readyState === 'complete') {
                    callback();
                    jQuery(script).remove();
                }
            };
            // test for onload to trigger callback
            script.onload = function () {
                callback();
                jQuery(script).remove();
                return;
            };
            // safari doesn't support either onload or readystate, create a timer
            // only way to do this in safari
            try {
                if (($.browser.webkit && !navigator.userAgent.match(/Version\/3/)) || $.browser.opera) { // sniff
                    LazyLoader.timer[url] = setInterval(function () {
                        if (/loaded|complete/.test(document.readyState)) {
                            clearInterval(LazyLoader.timer[url]);
                            callback(); // call the callback handler
                        }
                    }, 10);
                }
            } catch (e) {}
        }
    } catch (er) {
        alert(er);
    }
};


/**
 *  Display an xray overlay to show Ad loading progression
 */

var xrayAd = {

    div: null,
    viewport: null,
    thresold: 200,
    elements: [],
    adBlockCount: 0,
    w: 160,
    h: 200,

    // -- Init XrayAd Div
    init: function () {
        this.div = $('#xrayAd');
        if (!this.div) {
            this.div = $('<div>', {
                id: 'xrayAd',
                css: {
                    position: 'fixed',
                    top: 10,
                    left: 10,
                    width: this.w,
                    height: this.h,
                    zIndex: 10000,
                    background: 'rgba(0,0,0, 0.5)'
                }
            });
            this.div.appendTo($('body'));
        }
    },

    // -- Update viewport div
    viewportUpdate: function () {

        // Create div if not exists
        if (!this.viewport) {
            this.viewport = $('<div>', {
                id: 'xrayAdViewport',
                css: {
                    position: 'absolute',
                    width: this.w,
                    height: 10,
                    zIndex: 10001,
                    background: 'rgba(255,255,255, 0.3)'
                }
            });
            this.viewport.appendTo(this.div);
        }

        // Create div if not exists
        if (!this.viewThresoldTop) {
            this.viewThresoldTop = $('<div>', {
                id: 'xrayAdThresold',
                css: {
                    position: 'absolute',
                    width: this.w,
                    height: 1,
                    zIndex: 10002,
                    background: 'rgba(255,0,0, 0.5)'
                }
            });
            this.viewThresoldTop.appendTo(this.div);
            this.viewThresoldBottom = this.viewThresoldTop.clone().appendTo(this.div);
        }

        // Update div size and position
        this.bodyHeight = $(document).height();
        this.bodyWidth = $(window).width();
        var vH = ($(window).height() / this.bodyHeight) * xrayAd.h,
            vT = ($(window).scrollTop() / this.bodyHeight) * xrayAd.h;
        this.viewport.css({
            height: vH,
            top: vT
        });

        // Update thresold size and position
        this.viewThresoldTop.css({
            top: (($(window).scrollTop() - xrayAd.thresold) / this.bodyHeight) * xrayAd.h
        });
        this.viewThresoldBottom.css({
            top: (($(window).scrollTop() + xrayAd.thresold) / this.bodyHeight) * xrayAd.h + vH - 1
        });

        // Refresh Ad blocks
        if (this.div && this.div.length) {
            var blocks = this.div.find('.xrayAdBlock');

            $.each(blocks, function (key, val) {

                // Get block id 
                var xrayBlock = $(this);
                var adBlock = $(xrayAd.elements[key]);

                if (xrayBlock.length && adBlock.length) {
                    // Get offset and size of the page ad
                    var size = {};
                    size.off = adBlock.offset();
                    if (size.off) {
                        size.top = (size.off.top / xrayAd.bodyHeight) * xrayAd.h;
                        size.left = (size.off.left / xrayAd.bodyWidth) * xrayAd.w;
                        size.w = (Math.max(adBlock.width(), 10) / xrayAd.bodyWidth) * xrayAd.w;
                        size.h = (Math.max(adBlock.height(), 10) / xrayAd.bodyHeight) * xrayAd.h;

                        // Get ad load status
                        var bgColor = '#FF0071';
                        bgColor = (adBlock.data('loading') === 'true' ? 'orange' : bgColor);
                        bgColor = (adBlock.data('loaded') === 'true' ? '#00FF00' : bgColor);

                        // Update xray Block
                        xrayBlock.css({
                            top: size.top,
                            left: size.left,
                            width: size.w,
                            height: size.h,
                            borderColor: bgColor
                        });
                    }
                }

            });
        }
    },

    // -- Load elemnts to xray
    load: function (el, thresold) {

        // Init thresold
        this.thresold = thresold || 0;

        // Init Overlay Div
        this.init();

        // Draw each elements on div
        var adBlock = $('<div>', {
            'class': 'xrayAdBlock',
            'css': {
                position: 'absolute',
                background: '#ffffff',
                border: '1px solid #FF0071',
                top: 0,
                left: 0,
                width: 0,
                height: 0,
                zIndex: 10003
            }
        });

        // Init elements
        $.each(el, function () {

            // Create adBlock
            adBlock.clone().attr('xrayblock', 'xrayAdBlock_' + (xrayAd.adBlockCount++)).appendTo(xrayAd.div);

            // Bind load and complete
            $(this).bind('onCompleteXray', function () {
                xrayAd.viewportUpdate();
            });
            $(this).bind('onLoadXray', function () {
                xrayAd.viewportUpdate();
            });

            // Push src
            xrayAd.elements.push(this);
        });


        // Init viewport Div
        xrayAd.viewportUpdate();

        // Bind scroll
        $(window).bind("scroll", function (event) {
            xrayAd.viewportUpdate();
        });
    }


};

(function ($) {

    $.lazyLoadAdRunning = false;
    $.lazyLoadAdTimers = [];

    $.fn.lazyLoadAd = function (options) {
        var settings = {
            threshold: 0,
            failurelimit: 1,
            forceLoad: false,
            event: "scroll",
            viewport: window,
            placeholder: false,
            // Can specify a picture to replace media while loading
            onLoad: false,
            onComplete: false,
            timeout: 1500,
            debug: false,
            xray: false
        };

        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) {}
            }
        }

        /* If xray display requested */
        if (settings.xray && (typeof xrayAd === 'object')) {
            xrayAd.load(this, settings.threshold);
        }

        /* 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");
                    }, 300);
                    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 element from array so it is not looped next time. */
            var temp = $.grep(elements, function (element) {
                return !(($(element).data('loaded') === 'true') ? true : false);
            });
            elements = $(temp);
        });

        if ("scroll" === settings.event) {
            $(settings.viewport).bind("scroll", function (event) {
                if (elements.length === 0) {
                    return false;
                }

                $(settings.viewport).trigger("checkLazyLoadAd");
            });
        }


        // -- Bind each element
        this.each(function (_index, _value) {

            var self = $(this);

            /* Save original only if it is not defined in HTML. */
            if (undefined === self.attr("original")) {
                self.attr("original", self.attr("src"));
            }

            /* Test if element is loaded */
            self.isLoaded = function () {
                return ((self.data('loaded') === 'true') ? true : false);
            };

            /* Trigger Debug Display */
            self.bind("debug", function (e, status) {

                status = status || 'start';

                // -- Trigger xRay if possible
                if (settings.xray) {
                    if (status === 'start') {
                        self.trigger('onLoadXray');
                    }
                    else if (status === 'error') {
                        self.trigger('onErrorXray');
                    }
                    else if (status === 'complete') {
                        self.trigger('onCompleteXray');
                    }
                }

                // -- Change border color of ad frame
                if (settings.debug) {
                    if (status === 'start') {
                        self.css({
                            border: '3px solid orange'
                        });
                    }
                    else if (status === 'error') {
                        self.css({
                            border: '3px solid red'
                        });
                    }
                    else if (status === 'complete') {
                        self.css({
                            border: '3px solid green'
                        });
                    }
                }

            });

            /* On ad Load successful */
            self.one('onComplete', function () {

                _debug('---> lazyLoadComplete');

                // -- Remove original attr
                $(self).removeAttr("original");

                // -- Set as loaded
                $.lazyLoadAdRunning = false;
                self.data('loaded', 'true');

                // -- Mark debug
                self.trigger('debug', 'complete');

                if (typeof settings.onComplete === 'function') {
                    try {
                        settings.onComplete();
                    } catch (e) {}
                }
            });


            /* Launch the makina !! */
            self.stack = [];
            self.makinaBlock = false;
            self.bind('makina_go', function () {

                if (self.makinaBlock) {
                    return false;
                }

                if (self.stack.length > 0) {
                    var el = self.stack.shift();

                    var wrapAd = self.find('.wrapAd');
                    if (!wrapAd.length) {
                        wrapAd = $('<div class="wrapAd"></div>').clone();
                        wrapAd.appendTo(self);
                    }

                    var wrap = $('<div>').clone().appendTo(wrapAd);

                    if (typeof el === 'string') {
                        wrap.replaceWith(el);
                    } else if (typeof el === 'object') {
                        if (el.is('script')) {

                            // -- Load JS and block makina until script is loaded
                            if (el.attr('src')) {
                                _debug('JS to load !! --> ' + el.attr('src'));
                                //self.makinaBlock = true ;
                                LazyLoader.load(el.attr('src'), self, function () {
                                    self.makinaBlock = false;
                                    _debug('JS to load !! ++> ' + el.attr('src'));
                                    self.trigger('makina_go');
                                });
                            }

                            // -- Write JS code in wrapper
                            else {
                                wrap.replaceWith(el);
                            }
                        } else {
                            wrap.replaceWith(el);
                        }
                    }

                    self.trigger('makina_go');
                } else {
                    if ($.lazyLoadAdTimers.loadJS) {
                        clearTimeout($.lazyLoadAdTimers.loadJS);
                    }
                    $.lazyLoadAdTimers.loadJS = setTimeout(function () {
                        self.trigger('onComplete');
                    }, settings.timeout);
                }

            });

            /* Write directly in DOM : tag is html valid */
            self.bind('docWrite_direct', function (e, html) {
                var el = $(html);
                _debug('Fragment Direct Write : ', el, el.length);
                $.each(el, function () {
                    self.stack.push($(this));
                });
                self.trigger('makina_go');
            });

            /* Write directly in DOM : tag is html valid */
            self.bind('docWrite_delayed', function (e, html) {
                _debug('Fragment Delayed Write : ', html);

                self.numWrappers--;
                _debug("Fragment append : ", self.numWrappers, html);
                self.docHtmlCurrent += html;
                if (self.numWrappers === 0) {
                    html = self.docHtmlCurrent;
                    self.docHtmlCurrent = '';
                    setTimeout(function () {
                        self.stack.push(html);
                        self.docHtmlCurrent = '';
                        self.trigger('makina_go');
                    }, 0);
                }
            });



            /* Overload the default document.write */
            self.numWrappers = 0;
            self.docHtmlCurrent = '';
            self.bind('docWrite_overload', function () {

                // -- Overload default document.write function
                document._writeOriginal = document.write;
                document.write = document.writeln = function () {

                    var args = arguments,
                        id = null;
                    var html = '';
                    for (var i = 0; i < args.length; i++) {
                        html += args[i];
                    }

                    // -- Check if html to write is valid
                    var testHTML = '',
                        directWrite = false;

                    try {
                        testHTML = $(html);
                        directWrite = ((testHTML.is('div') || testHTML.is('script')) ? true : false);
                    } catch (e) {}

                    self.history[self.fragmentId] = self.history[self.fragmentId] || {};
                    if (self.history[self.fragmentId][html] === undefined) {
                        self.history[self.fragmentId][html] = true;
                        if (directWrite) {
                            self.trigger('docWrite_direct', html);
                        } else {
                            self.numWrappers++;
                            setTimeout(function () {
                                self.trigger('docWrite_delayed', html);
                            }, 0);

                        }
                    }
                };
            });


            /* Eval Script into <code> tags */
            self.bind('evalCode', function () {
                var scripts = [],
                    script, regexp = /<code[^>]*>([\s\S]*?)<\/code>/gi;

                while ((script = regexp.exec(self.html()))) {
                    var _s = script[1];
                    _s = _s.replace('<!--//<![CDATA[', '').replace('//]]>-->', '').replace('<!--', '').replace('//-->', '');
                    _s = _s.replace(/\&gt\;/g, '>').replace(/\&lt\;/g, '<');
                    scripts.push($.trim(_s));
                }

                // -- Eval param code before calling ad script                    
                try {
                    scripts = (scripts.length ? scripts.join('\n') : '');
                    _debug('Script to eval : ', scripts);
                    if (scripts !== '') {
                        eval(scripts);
                    }
                } catch (e) {}
            });


            /* Load a JS and wait for callback */
            self.bind('loadJS', function (e, js2load) {

                var callback = null,
                    script = null;
                if (js2load.src) {
                    callback = js2load.callback ||  null;
                    js2load = js2load.src;
                }

                // Add an anticache 
                if (js2load.indexOf('?') === -1) {
                    js2load += '?_=' + (new Date().getTime());
                } else {
                    js2load += '&_=' + (new Date().getTime());
                }

                _debug('loadJS :: ', js2load);

                // Request JS
                LazyLoader.load(js2load, self, function () {
                    _debug('loadJS COMPLETE :: ' + js2load);
                    if (callback) {
                        callback();
                    } 
                    else {
                        $.lazyLoadAdTimers.loadJS = setTimeout(function () {
                            self.trigger('onComplete');
                        }, settings.timeout);
                    }
                });

            });


            /* When appear is triggered load ad. */
            self.one("load", function () {

                // Detect if element is already loaded
                if (!self.isLoaded()) {

                    // Lock other adverts load
                    $.lazyLoadAdRunning = true;

                    // Have to load the current ad...
                    self.data('loading', 'true');
                    self.trigger('debug', 'start');

                    // Get original source to load
                    var srcOriginal = $(self).attr("original");

                    // Set fragmentId
                    self.history = {};

                    // -- Call script and let's dance
                    _debug('------------------------------  Lazy Load Ad CALL ----');
                    _debug('Context : ', self);

                    // Bind document.write overload
                    self.trigger('docWrite_overload');

                    // Eval code
                    self.trigger('evalCode');

                    // Eval attached script
                    if (srcOriginal) {
                        self.trigger('loadJS', srcOriginal);
                    }

                }
            });



            /* When wanted event is triggered load ad */
            /* by triggering appear.                              */
            if ("scroll" !== settings.event) {
                self.bind(settings.event, function (event) {
                    if (!self.isLoaded()) {
                        self.trigger("load");
                    }
                });
            }
        });

        /* Force initial check if images should appear. */
        $(settings.viewport).trigger('checkLazyLoadAd');

        return this;

    };

    /* Convenience methods in $ namespace.           */
    /* Use as  $.belowthefold(element, {threshold : 100, container : window}) */

    $.belowthefold = function (element, settings) {
        var fold = 0;
        if (settings.viewport === undefined || settings.viewport === window) {
            fold = $(window).height() + $(window).scrollTop();
        } else {
            fold = $(settings.viewport).offset().top + $(settings.viewport).height();
        }
        return fold <= $(element).offset().top - settings.threshold;
    };

    $.abovethetop = function (element, settings) {
        var fold = 0;
        if (settings.viewport === undefined || settings.viewport === window) {
            fold = $(window).scrollTop();
        } else {
            fold = $(settings.viewport).offset().top;
        }
        return fold >= $(element).offset().top + settings.threshold + $(element).height();
    };

})(jQuery);
