/*
  This is a simple class that allows you to call a function after a given timeout.
  Multiple calls to the touch method within atimeout will prevent acallback to be called.
  After the last touch() call, the object will wait atimeout milliseconds and call acallback.
  (If you never call touch, then acallback will never be called, of course...)

  Calling its clear() after calling touch(), within the given timeout will abort the callback procedure.

  Example usage:

<script>
function my_callback() {
    alert('You have stopped typing recently.');
}
var dc = new DelayedCall(my_callback,2000);
</script>
<input type="text" onkeyup="javascript:dc.touch();"/>


*/
function DelayedCall(acallback,atimeout) {
    var that = this;
    that.callback = acallback;
    that.timeout = atimeout;
    that.timerId = null;

    that.clear = function() {
        if (that.timerId!=null) {
            clearTimeout(that.timerId);
        }
    }

    that.touch = function () {
        that.clear();
        that.timerId = setTimeout( that.callback ,that.timeout);
    }
}

/*
  This function extends an element so it can be asked to hide itself.
  The hide_timeout parameter specifies the interval.

  Here is how you should use it:

  1. Make sure that the object can hide itself:    new DelayedHider(aelement,atimeout)
        
        This will create the hider object only if it is not created yet.
        The hider will be accessible as aelement.delayedHider  

  2. You can enable/disable hiding feature by setting:    aelement.delayedHider.allowHide(false); // false|true
  3. You can ask the object to hide itself immediatelly:  aelement.delayedHider.doHide();
  4. You can ask the object to hide itself later:         aelement.delayedHider.scheduleHide();

        This will hide the element ahide_timeout later, but multiple calls to this method will reset the timer.


  This object is magical in some way. If you prevent hiding by calling allowHide(false) and then 
  scheduleHide() gets called, then the element will be hided automagically after allowHide(true) has been called.

*/
function DelayedHider(aelement,ahide_timeout) {    
    if (hasCustomAttr(aelement,'delayedHider')) {
        return getCustomAttr(aelement,'delayedHider');        
    } else {
        var dh = new clsDelayedHider(aelement,ahide_timeout);
        setCustomAttr(aelement,'delayedHider',dh);
        return dh;
    }
}


function clsDelayedHider(aelement,ahide_timeout) {    
    var that = this;
    this.el = aelement;
    setCustomAttr(aelement,'delayedHider',this);
    this._can_hide = true;
    this._should_hide_after = false;
    this._dc = new DelayedCall( function() { that.doHide(); } ,ahide_timeout);
    return this;
}

clsDelayedHider.prototype.doHide = function() {
    if (this._can_hide) {
        this.el.style.display = 'none';
    } else {
        this._should_hide_after = true;
    }
}

clsDelayedHider.prototype.allowHide = function(avalue) {
        this._can_hide = avalue;
        if (avalue && this._should_hide_after) {
            this._should_hide_after = false;
            this.doHide();
        }
        if (!avalue) {
            this.scheduleHide();
        }
}

clsDelayedHider.prototype.scheduleHide = function() {
    this._dc.touch();
}

clsDelayedHider.prototype.clear = function() {
    this._dc.clear();
}


/*
  This abstract class is a general LiveSearch/AJAX implementation.
  You need to add a method named "getURL" in order to calculate the URL for live searching.

  NOTE: if you return a false value in getURL then the object will NOT do live searching.
*/
function BasicLiveSearch() {
    this.search_timeout = 1000; // Time to wait before we start live searching.
    this.hide_timeout = 60000;   // Time to wait before we hide search results (if nothing happens).
    this.xmlHttp=GetXmlHttpObject();
    if (this.xmlHttp==null)
    {
        alert ("Browser does not support HTTP Request");
        return;
    } 


    // These will be called from events...
    var that = this;
    this.touch = function () {
        that.doTouch();
    }
    this.search = function () {
        that.doSearch();
    }
    this._stateChanged = function() { 
        if (that.xmlHttp.readyState==4 || that.xmlHttp.readyState=="complete") { 
          that.showResults(that.xmlHttp.responseText);
          that.scheduleHideResults();
        } 
    }

    this.delayedsearch = new DelayedCall(this.search,this.search_timeout);
    return this;
}

BasicLiveSearch.prototype.getURL = function() {
    alert('You need to override BaseLiveSearch.getURL()!');
    return;        
}

BasicLiveSearch.prototype.hideResults = function() {    // You need to override this.
    alert('You need to override BaseLiveSearch.hideResults()!');
    return;        
}
BasicLiveSearch.prototype.scheduleHideResults = function () {
    alert('You need to override BaseLiveSearch.scheduleHideResults()!');
    return;        
}
BasicLiveSearch.prototype.showResults = function(data) { // You need to override this. Parameter is the raw data returned from the search.
    alert('You need to override BaseLiveSearch.showResults(data)!');
    return;        
}

BasicLiveSearch.prototype.doTouch = function () {
    this.hideResults();
    this.delayedsearch.touch(); // You need to call this whenever search params has changed.
}



// Call this manually to force live search. Also called automatically.
BasicLiveSearch.prototype.doSearch = function() {         
    this.hideResults();
    var url = this.getURL();
    if (url) {
        this.xmlHttp.onreadystatechange=this._stateChanged 
        this.xmlHttp.open("GET",url,true)
        this.xmlHttp.send(null)
    }
}




/*
  Similar to BasicLiveSeach but it uses a predefined popup window (id="livesearchwin") to display the results.
  Only thing you need to override is the getURL() method.

  The "target" parameter should be an element that is used as a reference position for the popup window.

  Example usage (not complete):

    function MyLiveSearch(input_id) {
        var edt = document.getElementById(input_id);
        var that = new PopupLiveSearch(edt);
        that.getURL = function() {                    
            if (edt.value) {
                return "?search="+escape(edt.value);
            } else {
                return false;
            }
        }
        edt.onkeyup = that.touch;
        edt.onblur = that.hideResults;
        return that;
    }
    mylivesearch = new MyLiveSearch("edt");


*/


function PopupLiveSearch(reference_element) {
    var that = new BasicLiveSearch();
    that._getspan = function() {
        var span = document.getElementById("livesearchwin");
        if (!span) {
            alert("You need to create a span (id='livesearchwin') before you can use PopupLiveSearch.");
            return;
        }
        // IE hides the span after moving the mouse...
        var dh = new DelayedHider(span,that.hide_timeout);
        span.onmouseover = function() { dh.allowHide(false); }
        span.onmouseout = function()  { dh.allowHide(true); }
        return span;
    }
    that.showResults = function(data) {
        var span = that._getspan();
        span.innerHTML = data;
        popupNear(span,reference_element);        
    }
    that.hideResults = function() {
        getCustomAttr(that._getspan(),'delayedHider').doHide();
    }
    that.scheduleHideResults = function () {
        var span = that._getspan();
        getCustomAttr(span,'delayedHider').scheduleHide();
    }
    return that;
}


function hideLiveSearchWin() {
    var dh = getCustomAttr(document.getElementById('livesearchwin'),'delayedHider'); 
    dh.allowHide(true); 
    dh.doHide();
}


/*
  This object will download a remote URL and callback a given function with the results.

  The callback should look like this:

  function myCallback(aparams,aresponse) {
    // ??? do something here
  }

  The aparams parameter can be anything, it will be passed to the callback.

  Then you can do:

    new AJAXCallback("delete.php?id=12",myCallback,null);

*/
function AJAXCallback(aurl,acallback,aparams) {
    this.xmlHttp=GetXmlHttpObject();
    if (this.xmlHttp==null)
    {
        alert ("Browser does not support HTTP Request");
        return;
    }
    this.params = aparams;
    this.callback = acallback;

    // These will be called from events...
    var that = this;
    this.doCallback = function(aresponse) {
        that.callback(that.params,aresponse);
    }
    this._stateChanged = function() { 
        if (that.xmlHttp.readyState==4 || that.xmlHttp.readyState=="complete") { 
            that.doCallback(that.xmlHttp.responseText);
        }  
    }
    this.xmlHttp.onreadystatechange=this._stateChanged 
    this.xmlHttp.open("GET",aurl,true)
    this.xmlHttp.send(null)
}

