// Utility function used for timeout capability
function fireTimer(obj) {
    var myObj = obj;
    function timerFired() {
        if (myObj.inprogress){myObj._abortRequest(myObj);}
    }
    myObj._timeoutID = window.setTimeout(timerFired, myObj.timeoutMS);
}

// Code originated from article on http://jibbering.com
// Constructor for generic R9HTTP client
function R9HTTPXml() {};

// Add methods and properties as array
R9HTTPXml.prototype = {
    uservars: null,
    url: null,
    postbuffer: null,
    // Instance of XMLHttpRequest
    xmlhttp: null,
    // Used to make sure multiple calls are not placed
    // with the same client object while another in progress
    inprogress: false,
    // The user defined handler - see MyHandler below
    thecallback: null,

    timeoutMS: -1,

    _timeoutID: null,

    // Tracks user initiated cancellation via timeout or explicit call
    cancelled: false,

    // Use this to setup what you will be calling
    //      -- method is GET/POST
    //      -- url is URL to invoke
    //      -- postbuffer is used to post url-form-encoded data with request
    init: function(url, postbuffer, args) {
        this.url = url;
        this.uservars = args;
        this.postbuffer = postbuffer;
        if (window.XMLHttpRequest) {
            this.xmlhttp = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } else {
            window.alert('Sorry your browser is not compatible with this functionality');
        }
    },

    // Use this before calling asynchGET to make the request timeout after a given period
    setTimeout: function(timeInMillisec) {
        this.timeoutMS = timeInMillisec;
    },


    // client argument is a user defined object to be called
    asyncGET: function (client) {

        // Prevent multiple calls
        if (this.inprogress) {
            throw "Call in progress";
        };

        // Have to assign "this" to a variable
        var self = this;

        if (client==null) {client=self;}
        this.thecallback = client;
        var args = null;

        // Open an async request - third argument makes it async
        if (this.postbuffer == null) {
            this.xmlhttp.open('GET',this.url,true);
        } else {
            this.xmlhttp.open("POST", this.url, true);
            this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=ISO-8859-1");
            args = this.postbuffer;
        }

        // Assign a closure to the onreadystatechange callback
        this.xmlhttp.onreadystatechange = function() {
            self.stateChangeCallback(self);
        }

        // Send the request
        this.inprogress = true;
        this.cancelled = false;

        this.xmlhttp.send(args);

        // Fire timeout if set
        if (this.timeoutMS > 0) {
            fireTimer(this);
        }
    },

    stateChangeCallback: function(client) {
        switch (client.xmlhttp.readyState)
        {
            // Request not yet made
            case 1:
                try {
                    if (! client.cancelled) {
                        client.thecallback.onInit();
                    }
                } catch (e) { /* client method not defined */ }
            break;

            // Contact established with server but nothing downloaded yet
            case 2:
                try {
                    // Check for HTTP status 200
                    if ( client.xmlhttp.status != 200 ) {
                        if (! client.cancelled) {
                        window.status="error";
                        client.thecallback.onError(
                            client.xmlhttp.status,
                            client.xmlhttp.statusText,
                            client
                            );
                        }

                        // Abort the request
                        client.xmlhttp.abort();

                        // Call no longer in progress
                        client.inprogress = false;
                    }
                } catch (e) {
                    /* client method not defined */
                }
            break;

            // Called multiple while downloading in progress
            case 3:
                // Notify user client of download progress
                var contentLength;
                try {
                    // Get the total content length
                    // -useful to work out how much has been downloaded
                    try {
                        contentLength =
                            client.xmlhttp.getResponseHeader("Content-Length");
                    } catch (e) {
                        contentLength = NaN;
                    }

                    // Call the progress client with what we've got
                    if (! client.cancelled) {
                        window.status="ping";
                        client.thecallback.onProgress(
                            client.xmlhttp.responseText,
                            contentLength);
                    }

                } catch (e) { /* client method not defined */ }
            break;

            // Download complete
            case 4:
                try {
                    if (client._timeoutID) {
                        window.clearTimeout(client._timeoutID);
                        client._timeoutID = null;
                    }
                    // Suppress multiple IE5 events
                    if (client.inprogress) {
                        client.inprogress = false;
                        if (! client.cancelled) {
                            window.status="done";
                            client.thecallback.onLoad(client);
                        }
                    }
                } catch (e) {
                    /* client method not defined */
                } finally {
                    // Call no longer in progress
                    client.inprogress = false;
                }
            break;
        }
    },

    cancelRequest: function() {
        // kill timer
        var gizmo = this;

        // cancel to suppress message
        this.cancelled = true;

        if (this._timeoutID) {
            window.clearTimeout(this._timeoutID);
            this._timeoutID = null;
        }

        gizmo._abortRequest(gizmo);
    },

    _abortRequest: function(gizmo) {
        if (gizmo.xmlhttp!=null) {
            // Abort the request

            try {
                gizmo.xmlhttp.abort();
                if (gizmo.inprogress){
                    window.status="abort";
                    gizmo.thecallback.onError('timeout', 'Your request has timed out.',gizmo);
                }
            } catch (e) {}

            // Call no longer in progress
            gizmo.cancelled = true;
            gizmo.inprogress = false;
        }
    },

    // utility to get tags on results
    getText: function () {
        return this.xmlhttp.responseText;
    },

    // utility to get tags on results
    getXML: function () {
        return this.xmlhttp.responseXML;
    },

    // utility to get tags on results
    getTags: function (tagname) {
        try {
            return this.xmlhttp.responseXML.getElementsByTagName(tagname);
        } catch (e) {return null;}
    },

    // utility to get subtags -- from opera.com folks
    getOperaText: function (parent,item,index)
    {
        //opera.postError("hello world, opera function");

        try {
            var result = parent.getElementsByTagName(item)[index];

            if (result) {
                var returnText='', i=0, node;
                while (node = result.childNodes[i]) {
                    returnText += node.nodeValue;
                    i++;
                }
                return returnText;
            } else {
                return "";
            }
        } catch (e) {
            opera.postError("exception: " + e);
        }
    },

    getOpera: function (parent,item,index)
    {
        return "foo";
    },

    // utility to get subtags
    getTagText: function (parent,item,index)
    {
        var result = parent.getElementsByTagName(item)[index];
        if (result)
        {
            if (result.childNodes.length > 1) {
                return result.childNodes[1].nodeValue; }
            else if (result.childNodes.length==1) {
                return result.firstChild.nodeValue; }
        }
        else {
            return "";
        }
    },


    onProgress:function(t,l){},
    onError:function(s,t,c){},
    onLoad:function(c) { },
    onInit:function(c) { }

}

/*
The below code should be put in the front-end page
*/
var _toolong, _lastsent, _timeout,_ids,_values,_idletimer,_cursel,_target;
var _prevsel; var _inputcode=null,_searchtype; var _targetBG; var _input=null; var _numChoices;
var sbHttp = null; var _stop; var _chars=3; var _sboxminwidth; var _clientsbcb; var _sbcountrycode;

function cancelSmartBox() {
    keysInit(null); if (_inputcode!=null && _inputcode.value!=null && _inputcode.value.length < 1) {_setValue(0);}
    abortSmartBox(); closeSmartBox(); _input = null; _stop = false;
}

function abortSmartBox(){if(sbHttp!=null){if(sbHttp.inprogress){sbHttp.cancelRequest();}}}

function closeSmartBox()
{
    if (_target!=null){_target.style.display = 'none'; _target.innerHTML = "";}
    if (_targetBG!=null){_targetBG.style.display = 'none';}
    _ids=new Array();
    _values=new Array();
    _stop = true;
}

// searchtype 'hotel' | 'car' | 'air' | 'all'
function initSmartBox(where, wherecode, searchtype, timeout, minwidth, valuesetcb)
{
    _inputcode=_input=null;
    cancelSmartBox();
    _target = document.getElementById('smartbox');
    _targetBG = document.getElementById('smartboxBG');
    if (isNaN(parseInt(minwidth))) {
        _sboxminwidth = -1;
    } else {
        _sboxminwidth = parseInt(minwidth);
    }
    keysInit(keypressed);
    _input = where;           // visible input field
    _inputcode = wherecode; // hidden input field
    _searchtype = searchtype;
    _lastsent = "";
    _timeout=timeout;

    _clientsbcb = valuesetcb;

    var x = findPosX(where);
    var y = findPosY(where) + where.offsetHeight + 1;

    _target.style.top=y + 'px';
    _target.style.left=x + 'px';

    if (_idletimer){clearTimeout(_idletimer);_idletimer='';}
    _toolong=false;_lastsent="";
    _ids=new Array(); _values=new Array();
    _cursel=0;
    _prevsel=0;
    _stop = false;
}
function _runSearch(input)
{
    if (sbHttp==null) {sbHttp = new R9HTTPXml();}
    if (!sbHttp.inprogress)
    {
        if (_lastsent==_input.value || _input.value.length<_chars){return;}
        window.status = "searching...";
        _lastsent=_input.value;

        var url = "bwib_prodfiles/n_city_inquiry.asp?q=" + escape(_lastsent);
        var sbHttp = new R9HTTPXml();
//		alert(_searchtype);

        if (_searchtype == "air") {
            url += "&p=1";
        } else if (_searchtype == "car") {
            url += "&p=2";
        } else if (_searchtype == "hotel") {
            url += "&p=3";
        } else if (_searchtype == "all") {
            url += "&p=4";
        }
        if (_sbcountrycode != null && _sbcountrycode.length > 0) {
            url += "&cc=" + _sbcountrycode;
        }
        sbHttp.init(url);
        try {
            sbHttp.setTimeout(10000);
            sbHttp.asyncGET(new _Callback(input));
        } catch (e) {
            alert(e);
        }
    }
    else {alert("busy");}
}

var _divB = "<div class='smartboxItem' " +
            "onmousedown='_sbMouseDown(this)' onmouseover='_sbMouseOver(this)' " +
            "onmouseout='_sbMouseOut(this)'>";
var _divE = "<\/div>";
var _spanB = "<span class='smartboxItemLabel'>";
var _spanE = "<\/span>";

function getSbIndex(divObj)
{
    var retIndex = 0;
    for (var i=0; i < _target.childNodes.length; i++)
    {
        if (divObj == _target.childNodes[i])
        {
            retIndex = i;
            break;
        }
    }

    return retIndex;
}

function _sbMouseOut(obj)
{
    _cursel = -1;
    selChoice(-1);
}

function _sbMouseDown(obj)
{
    _cursel = getSbIndex(obj);
    _setValue(_cursel);
    closeSmartBox();
}

function _sbMouseOver(obj)
{
    _cursel = getSbIndex(obj);
    selChoice(_cursel);
}

function _Callback(input) {
    this.onError = function(status,statusText) { }
    this.onLoad=function done(client) {
        window.status = "";
        if (!client.cancelled && (_input == input))
        {
        var html = ""; var list = "";
        _ids=new Array(); _values=new Array();
        var cnt=0;
		var results=client.getTags("sb");
        var ii=client.getTags("d");
        var icon="";
        for (var j = 0; j < ii.length; j++) {
            var data = new Object();
            var id=client.getTagText(results[0], "a", j);
            data.id = id;
            var str=client.getTagText(results[0], "d", j);
            // Add data to object as needed

            data.str = str;
            _ids[j] = id; _values[j]=data;
            list += _divB + _spanB  + '\u2022' + ' '  + str + _spanE + _divE;cnt++;
        }
        if (cnt > 0) {
            html += list;
            _target.style.width = 'auto';
            _target.innerHTML = html;
            _target.style.display = 'inline';
            if (_sbShowBGIframe()) {
                _targetBG.style.display = 'inline';
            }
            if (_sboxminwidth > _target.offsetWidth) {
               _targetBG.style.width = _sboxminwidth;
               _target.style.width = _sboxminwidth;
            } else {
                _targetBG.style.width = _target.offsetWidth;
            }
            _targetBG.style.height = _target.offsetHeight;
            _targetBG.style.top = _target.style.top;
            _targetBG.style.left = _target.style.left;
            _targetBG.style.zIndex = _target.style.zIndex -1;
        } else {
            _target.style.display = 'none';
            _targetBG.style.display = 'none';
        }

        _numChoices = cnt;
        _cursel=0;
        _prevsel=0;
        selChoice(0);
    }
    }
}

function _setValue(newi)
{
    if (newi<0) {_lastsent="";try{_inputcode.value="";_clientsbcb(null);}catch(e){}}
    else if (_ids.length > 0) {
        _inputcode.value = _ids[newi];
        _input.value = _values[newi].str;
        try {
            if (typeof _clientsbcb == 'function') {
                _clientsbcb(_values[newi]);
            }
        } catch (ignored) {}
    }
}

function selChoice(newi)
{
    if (_target.childNodes.length > 0) {
        if (_prevsel >= 0)
        {
            var prev = _target.childNodes[_prevsel];
            prev.className = "smartboxItem";
        }

        if (newi >= 0) {
            if (_target.childNodes.length > 0)
            {
                var cur = _target.childNodes[newi];
                cur.className = "smartboxItemHi";
            }

            _prevsel = newi;
        }
    }
}

var _pressed=0;
function keypressed(keycode, keyvalue)
{
    clearTimeout(_idletimer);
    //window.status=keycode;

    _pressed = new Date().getTime();
    switch (keycode){
    case LEFT:
    case UP:
        _cursel--;if (_cursel<0){_cursel=0;}selChoice(_cursel);
        break;
    case RIGHT:
    case DOWN:
        _cursel++;if (_cursel>=_ids.length){_cursel=_ids.length-1;}selChoice(_cursel);
        break;
    case ENTER:
        if (_ids.length>0){
            _setValue(_cursel);
            closeSmartBox();
        }
        break;
    case ESC:
        closeSmartBox();
        break;
    case TAB:
        if (_cursel>=0&&_cursel<_ids.length){_setValue(_cursel);}
    case ALT:
    case SHIFT:
        break;
    default:
        _setValue(-1);
        _idletimer = self.setTimeout('idle()', _timeout);
    }

}
function sbNoEnter() {var v = (_stop==null || _stop!=true);
    _stop = false; return (v ? true: noEnter());
}

function idle(input)
{
    if (noEnter()) {
        var now = new Date().getTime();
        if (now - _pressed > _timeout)
        {
            clearTimeout(_idletimer);
            _typer(_input);
        }
        else
        {
            clearTimeout(_idletimer);
            _idletimer = self.setTimeout('idle()', _timeout);
        }
    }
}

function _typer(input)
{
    if ((_input != null )&& _input.value.length > 0) {abortSmartBox();_runSearch(input); }
    else {_setValue(-1);closeSmartBox();}
}

// Opera and potentially other older browsers do not support iframe behind other elements
function _sbShowBGIframe()
{
    return ( window.opera ) ? false : true;
}
function clearHelpText(field) {
	if (field.value.length > 0 && field.value.indexOf(' start') == 0) {
		field.className = 'searchbox';
		field.value = '';
	}
}

/* To get position from screen */
function findPosX(obj)
{
    var curleft = 0;
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            curleft += obj.offsetLeft
            obj = obj.offsetParent;
        }
    }
    else if (obj.x)
        curleft += obj.x;
    return curleft;
}

function findPosY(obj)
{
    var curtop = 0;
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            curtop += obj.offsetTop
            obj = obj.offsetParent;
        }
    }
    else if (obj.y)
        curtop += obj.y;
    return curtop;
}
/* To handle key press */
var who;var ESC=27;var TAB=9;var ALT=18;var UP=38;var DOWN=40;var LEFT=37;var RIGHT=39;var ENTER=13;var SHIFT=16;var lastKey=0;
function keyDown(e){var pK = document.all? window.event.keyCode:e.which;
var pK2 = String.fromCharCode(pK).toLowerCase(); lastKey=pK; if(who!=null){who(pK, pK2);}}
function keysInit(callback){ document.onkeydown = keyDown; who=callback;
if (document.layers) {document.captureEvents(Event.KEYPRESS);} }
function noEnter(){return lastKey!=ENTER;}


/*
** HERE IS SOME SAMPLE CODE TO USE THIS OBJECT

// A user defined handler to response to the XMLHTTPRequest
function MyHandler()
{
    this.onInit = function() {
        alert("About to send request<br>");
    }
    this.onError = function(status,statusText) {
        alert("Error: "+status+": "+statusText+"<br>");
    }

    this.onProgress = function(responseText,length) {
        alert("Downloaded "+responseText.length+" of "+length+"<br>");
    }

    this.onLoad = function(client) {
        alert("<pre>"+client.xmlhttp.responseText+"</pre>");
    }
}

// Invoke the client
function getPage() {
    // Modify this to some page
    var url = "http://bosco.ma.runwaynine.com/test.txt";
    var client = new R9HTTPXml();
    client.init(url);
    // optionally client.setTimeout(5000);

    try {
        client.asyncGET(new MyHandler());
    } catch (e) {
        alert(e);
    }
    alert("Async request so still able to do stuff here<br>");
}
*/
