﻿/* AJAX Stuff */
/* Copyright (c) 2010 FoXSE Ltd */

/*
 * Keep checking a page and replace
 * body if it's updated.
 *
 * REMEMBER TO SET CACHE SETTINGS
 * Call will be /url?check=true
 */

function createRequestObject()
{
    var ro;
    
    //get the request object based on the browser used

    if (browserIsIE()) {
        ro = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
        ro = new XMLHttpRequest;
    }
    
    return ro;
}

var defaultCheckInterval = 5000;

/*
 * Set-up checker instances.
 */
 
//Array of checker instances
var ca = new Array();

//Temp buffer
var temp_buffer;

//scheduler
var int_checkRequests = null;
var checkHTTPInterval = 5000;

//no url modification
function initAutoRefresh(check_int, check_url) { initAsyncCheck(check_int, check_url, null, null, true); }

//overloads
function initAsyncCheck() { initAsyncCheck(null, null, null, null, null); }
function initAsyncCheck(check_int) { initAsyncCheck(check_int, null, null, null, null); }
function initAsyncCheck(check_int, check_url) { initAsyncCheck(check_int, check_url, null, null, null); }
function initAsyncCheck(check_int, check_url, upd_func) { initAsyncCheck(check_int, check_url, upd_func, null, null); }
function initAsyncCheck(check_int, check_url, upd_func, pre_upd_func) { initAsyncCheck(check_int, check_url, upd_func, pre_upd_func, null); }

function initAsyncCheck(check_int, check_url, upd_func, pre_upd_func, whole_page_refresh)
{
    //instantiate & add to array
    var nc = new clsAsync();
    
    ca.push(nc);
    nc.class_index = (ca.length - 1); /*do after push*/
    
    //this could be null
    nc.pre_update_func = pre_upd_func;
    
    if (nc.pre_update_func != null)
    {
        //Strip out () brackets if specified in the callback function        
        var lbracket = nc.pre_update_func.indexOf("(");
        
        if (lbracket > 0)
        {
            nc.pre_update_func = nc.pre_update_func.substr(0, lbracket);
        }
    }
    
    //this could be null
    nc.update_func = upd_func;
    
    if (nc.update_func != null)
    {
        //Strip out () brackets if specified in the callback function        
        var lbracket = nc.update_func.indexOf("(");
        
        if (lbracket > 0)
        {
            nc.update_func = nc.update_func.substr(0, lbracket);
        }
    }
    
    /*
     * Build the URL to check asynchronously.
     * If not specified - use the same page.
     */
     
    if (check_url == null)
        nc.url_to_check = document.location.href;
    else
        nc.url_to_check = check_url;
    
    //check interval
    if (check_int == null)
        check_int = defaultCheckInterval;
    
    /*
     * Add check=true
     */
     
    if (whole_page_refresh != true)
    {
        // old style body checking
        
        if (nc.url_to_check.indexOf("?") > 0)
            nc.url_to_check += "&";
        else
            nc.url_to_check += "?";
            
        nc.url_to_check += "check=true";
    }
    else
    {
        // new md5 checking
        
        nc.whole_page_refresh = true;
        
        if (nc.url_to_check.indexOf("?") > 0)
            nc.url_to_check += "&";
        else
            nc.url_to_check += "?";
            
        nc.url_to_check += "autorefreshmode=check";
    }
    
    /*
     * Call immediately on load
     * because first load just calls content
     */
    nc.checkChanged();
    
    //Set interval to call update checker
    nc.recentInt = setInterval("bootstrapChecker(" + nc.class_index + ")", check_int);
    
    //set interval to call checkRequests
    if (int_checkRequests == null)
    {
        int_checkRequests = setInterval("checkRequests()", checkHTTPInterval);
    }
}

/*
 * Call the checker method for the
 * specified class instance.
 */

function bootstrapChecker(class_index)
{
    ca[class_index].checkChanged();
}

/*
 * AJAX scheduler checks clsAsync
 * http objects for ready state changes
 * on a pre-determined interval.
 */

function checkRequests()
{
    for (i = 0; i < ca.length; i++)
    {
        if (ca[i].request_pending)
        {
            if (ca[i].http_object != null)
            {
                var http_obj = ca[i].http_object;
                
                //request ready state change
                if (http_obj.readyState == 4)
                {
                    ca[i].request_pending = false;
                    ca[i].checkNewData(http_obj.responseText);
                }
            }
        }
    }
}

/*
 * Make asynchronous HTTP GET call and pass to a func.
 */

function asyncGET(a_url, call_on_success) {
    
    //send http get
    var http = createRequestObject();
    http.open('GET', a_url, true);
    
    http.onreadystatechange = function(){
        if (http.readyState == 4) {
            
            if (call_on_success != null) {
                //strip brackets
                var lbracket = call_on_success.indexOf("(");
                    
                if (lbracket > 0) {
                    call_on_success = call_on_success.substr(0, lbracket);
                }
                
                //external callback
                eval(call_on_success + ".call(this, http.responseText);");
            }
        }
    };
    
    http.send(null);
}

/*
 * GET then call back to class.
 */

function asyncGETClassReturn(a_url, class_index)
{    
    //send http get
    var http = createRequestObject();
    
    //scheduler
    ca[class_index].http_object = http;
    ca[class_index].request_pending = true;
    
    http.open('GET', a_url, true);
    http.send(null);
}

/*
 * Asynchronous checker class.
 */

function clsAsync()
{
    //properties
    this.class_index = -1;
    this.update_func = null;
    this.pre_update_func = null;
    this.url_to_check = null;
    this.recentInt = -1;
    this.check_ms = -1;
    this.last_resp = null;
    this.whole_page_refresh = false;
    
    //scheduler
    this.request_pending = false;
    this.http_object = null;
    
    //methods
    this.checkChanged = itl_checkChanged;
    this.checkNewData = itl_checkNewData;
    
    /*
     * Begin check to see if page contents have changed.
     */
    
    function itl_checkChanged()
    {
        asyncGETClassReturn(this.url_to_check, this.class_index);
    }

    
    /*
     * Compare incoming data to the current document body.
     * If it's different, replace it with the new data.
     *
     * Since checkNewData() is called immediately as well
     * as at an interval, the first returned data is close
     * to (or identical to) the active page. We can cache
     * this and use it to compare to future responses.
     */
     
    function itl_checkNewData(new_resp)
    {
        var new_resp;
        
        /*
         * This method calls a ping back page to the
         * current url when a change is detected.
         * 
         * The server provides a response in the form
         * of an MD5 hash of the page.
         */
        if (this.whole_page_refresh)
        {
            //alert(this.last_resp + "\n" + new_resp);
            
            //don't compare on first check
            if (this.last_resp != null)
            {
                if (new_resp.length > 0)
                {
                    if (new_resp != this.last_resp)
                    {
                        //clear interval checker for this class index
                        clearInterval(this.recentInt);
                
                        //reload current page
                        document.location.href = getPingBackUrl();
                    }
                }
            }
            
            //save for next time
            this.last_resp = new_resp;
        }
        
        /*
         * This method (old-style) replaces the body
         * of the current place in-situ.
         */
        else
        {
            new_resp = getBodyContent(new_resp);
            
            if (new_resp.length > 0)
            {
                if (new_resp != this.last_resp)
                {
                    this.last_resp = new_resp;

                    //pre-update
                    if (this.pre_update_func != null)
                    {
                        try
                        {
                            eval(this.pre_update_func + ".call(this);");
                        }
                        catch(e)
                        {
                            //don't alert
                            //alert("Failed to call pre-update function");
                        }
                    }
                    
                    //update body html
                    document.body.innerHTML = new_resp;
                    
                    //post-update
                    if (this.update_func != null)
                    {
                        try
                        {
                            temp_buffer = new_resp;
                            eval(this.update_func + ".call(this, temp_buffer);");
                        }
                        catch(e)
                        {
                            //don't alert
                            //alert("Failed to call post-update function");
                        }
                    }                
                }
            }
        }
    }

    /*
     * Isolate <BODY> to </body> in the incoming data.
     */
     
    function getBodyContent(s)
    {
        var ret = "";
        
        var b1start = s.toLowerCase().indexOf("<body");
        
        if (b1start > 0)
        {
            var s = s.substr(b1start, s.length - b1start);
            
            var b1end = s.toLowerCase().indexOf(">");
        
            if (b1end > 0)
            {
                b1end++;
                
                var s = s.substr(b1end, s.length - b1end);
                
                var b2start = s.toLowerCase().indexOf("</body");
        
                if (b1start > 0)
                {
                    var s = s.substr(0, b2start);
                    ret = s;
                }
            }
        }
        
        return ret;
    }
    
}