//
// mgbox.js
//
// All mgBox related functions.
// There should not be any need to modify this file,
// customisation can be achieved through various
// "hook" functions.
//
// Feel free to use as you please, but please retain this
// copyright notice.
//
// 2005-02-10 MGrill Version 1.00
// 2005-02-17 MGrill added onLoad hook
//
// 2008-07-12 JTuschek added functions for handling mouse events
//                     and fixed displaying problems on ie & ff
//
// 2008-12-21 JTuschek don't select the the first line of results
//					   copy the selected line into input field when using page up or down
//                     copy the entered value back into input field if you reach positon 0
//					   don't start from the begining if the last field was reached.
//					   close the msgBox if the result is empty or 'no magic'
//
// see http://www.dcs.lancs.ac.uk/~grill/mgbox/
//
// This is the mgBox class declaration.
// The one and only global mgBox object will be called "theMgBox",
// this object will be instantiated further down.
function mgBox() {
  if(typeof MGBOX_ASSIST_CGI_TEMPLATE == 'undefined') {
    this.ASSIST_CGI_TEMPLATE = "cgi/assist_@.php?s=";
  } else {
    this.ASSIST_CGI_TEMPLATE = MGBOX_ASSIST_CGI_TEMPLATE;
  }
  this.IDLETIME = 300;  // after this duration we start processing the user input!
  this.timerid=null;    // will hold the timer that waits until the user does nothing for IDLETIME ms.
  this.oldcontents="";  // the previous contents of the text box
  this.textboxid="";    // our textbox - will get updated in call to onFocus() later on...
  this.textfieldhandle=null;  // handle to our textbox, see above
  this.httprequest = createXMLHttpRequest();  // the one and only active httprequest object...
  this.httprequestactive = false;
  this.highlightedline = null;
  this.html_text_array = Array(); // results from database 
  this.raw_text_array = Array();  // queries go into
  this.id_array = Array();        // these three arrays
  this.isOpenAndFilled   = false;
  this.selectedSomething = false;
  this.copiedValueBack = false;

}

// gets called whenever a keydown event happens
function MgOnKeyDown(event) {
  // we process the key. if it is a "normal" character, we only react after
  // a specified timeout (see below).
  // "event" only gets set if the handler was assigned from within
  // javascript, not in the html code. this is done in onFocus().

  if(!event) {
    event = window.event;
  }
//  console.log('eventKeyDown',event.keyCode);

  var hBox = getElem("id","mgBox");
  theMgBox.copiedValueBack = false;

  ///////////////// DOWN //////////////////
  if(event.keyCode==40) {  // "down"
    if(!theMgBox.isOpenAndFilled) {
      theMgBox.timeoutReached(theMgBox.textboxid);
    } else {
      if(theMgBox.highlightedline!=null) {
        // change currently hightlighted line back to normal
        hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="";
        // advance selection by one
		if (theMgBox.highlightedline < theMgBox.html_text_array.length-1)
		{ 
        	theMgBox.highlightedline = theMgBox.highlightedline+1;
      	}
      
      } else {
        if (theMgBox.html_text_array.length>0) {
          theMgBox.highlightedline = 0;
        }
      }
      // highlight the new line
      hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="mgHighlighted";
      copyTextIntoField();
      theMgBox.selectedSomething = true;
    }
  }
  
  ///////////////// UP //////////////////
  if(event.keyCode==38) {  // "up"
    if(!theMgBox.isOpenAndFilled) {
      theMgBox.timeoutReached(theMgBox.textboxid);
    } else {
      if(theMgBox.highlightedline!=null) {
        // change currently hightlighted line back to normal
        hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="";
        // advance selection by one (upwards)
        if (theMgBox.highlightedline==0)
        {
        	copyOriginalTextIntoField();
        	theMgBox.highlightedline = null;
        	theMgBox.selectedSomething = false;
        	theMgBox.copiedValueBack = true;

            var id=theMgBox.textboxid;
            var mytextbox = getElem("id",id);
            var textfieldcontents = mytextbox.value;
            theMgBox.oldcontents = textfieldcontents;
            
        }
        else
        {
        	theMgBox.highlightedline = theMgBox.highlightedline-1
        }
      } 
//      else {
//       theMgBox.highlightedline = theMgBox.html_text_array.length-1;
//      }
      // highlight the new line
      if (theMgBox.highlightedline != null)
      {
      	hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="mgHighlighted";
      	copyTextIntoField();
      	theMgBox.selectedSomething = true;
      }
    }
  }
  
  ///////////////// ENTER //////////////////
  if(event.keyCode==13) {  // "ENTER"
    if(!theMgBox.isOpenAndFilled) {
      theMgBox.timeoutReached(theMgBox.textboxid);
    } else {
      theMgBox.hideMgBox();
      if (theMgBox.selectedSomething)
      {
	      // copy text into edit field
	      copyTextIntoField();
	  }      
      // Remember author ID in hidden input field (if it exists)
      var hHiddenIdBox = getElem("id",theMgBox.textboxid+"_id");
      if( hHiddenIdBox != null ) {
        hHiddenIdBox.value = theMgBox.id_array[theMgBox.highlightedline];
      }

      // call hook if present      
      if(typeof mgBox_onEnter_hook == 'function') {
        mgBox_onEnter_hook(theMgBox.textboxid);
      }
    }
  }
}
mgBox.prototype.onKeyDown = MgOnKeyDown;


// gets called when the mouse moves over an mgbox list element
function enterListElement(listElement)
{
  var hBox = getElem("id","mgBox");

  var hTextbox = getElem("id",theMgBox.textboxid);
  if (theMgBox.selectedSomething == false)
  {
  	 theMgBox.originalInput = hTextbox.value;
  }
  
  var actPos = listElement.id.substr(2);
  // alert(theMgBox.highlightedline);
  
  if(theMgBox.highlightedline!=null) 
  {
    // change currently hightlighted line back to normal
    hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="";
    // advance selection by one
  }     
  theMgBox.highlightedline = actPos-1;
  // highlight the new line
//  hBox.childNodes[0].childNodes[theMgBox.highlightedline].className="mgHighlighted";
    listElement.className="mgHighlighted";
    theMgBox.selectedSomething = true;

}

function clickedListElement(listElement)
{
//      theMgBox.hideMgBox();
      // copy text into edit field
      var mgForm = getElem("id","mgform");

      copyTextIntoField();
      
      // Remember author ID in hidden input field (if it exists)
      var hHiddenIdBox = getElem("id",theMgBox.textboxid+"_id");
      if( hHiddenIdBox != null ) {
        hHiddenIdBox.value = theMgBox.id_array[theMgBox.highlightedline];
      }

      // call hook if present      
      if(typeof mgBox_onEnter_hook == 'function') {
        mgBox_onEnter_hook(theMgBox.textboxid);
      }
      
      mgForm.submit();
      
}

function copyTextIntoField()
{ 
  var hTextbox = getElem("id",theMgBox.textboxid);
  if (theMgBox.selectedSomething == false)
  {
  	 theMgBox.originalInput = hTextbox.value;
  }
  hTextbox.value = theMgBox.raw_text_array[theMgBox.highlightedline];
  theMgBox.oldcontents = theMgBox.raw_text_array[theMgBox.highlightedline];
}

function copyOriginalTextIntoField()
{ 
  var hTextbox = getElem("id",theMgBox.textboxid);
  hTextbox.value = theMgBox.originalInput;
}


function MgOnFocus(event) {
  // we store our parent id within the MgBox object, so that later on we
  // can check whether a delayed result does actually belong to the currently
  // active box
  // we also reset some other properties
  
  // first we need to find out the id of our textfield, though:
//  console.log('on focus event');

  if( event == null ) {
    event = window.event;
  }
  var hElement = ( event.srcElement ) ? event.srcElement : event.originalTarget;
//  console.log('on focus event:',event.type,hElement)
  var id=hElement.id;
  
  theMgBox.textboxid = id;
  if( !id ) {
    alert( 'Error: every mgBox needs a unique ID attribute!' );
  }
  theMgBox.httprequestactive = false;
  theMgBox.textfieldhandle = getElem("id",id);
  theMgBox.oldcontents= theMgBox.textfieldhandle.value;  // old contents = current contents

  // we attach the keydown handler
  theMgBox.textfieldhandle.onkeydown = theMgBox.onKeyDown;
  theMgBox.textfieldhandle.onkeyup = theMgBox.onKeyUp;
  theMgBox.textfieldhandle.onblur = theMgBox.onBlur;
}
mgBox.prototype.onFocus = MgOnFocus;

function MgOnBlur(event) {
/*
  theMgBox.textfieldhandle.onkeydown = null;
  theMgBox.textfieldhandle.onkeyup = null;
  theMgBox.textfieldhandle.onblur = null;
  
//  theMgBox.hideMgBox();
    
  // call hook function if it exists
  if(typeof mgBox_onBlur_hook == 'function') {
    mgBox_onBlur_hook(theMgBox.textboxid);
  }

  // reset some MgBox related properties to make sure it
  // doesn't suddenly pop up!
//  theMgBox.textboxid = "";
  theMgBox.oldcontents = "";
  theMgBox.httprequestactive = false;
  theMgBox.handle = null;
*/
}
mgBox.prototype.onBlur = MgOnBlur;

function MgOnKeyUp(event) {
  // we process the results of the previous keypress. 
  // "event" only gets set if the handler was assigned from within
  // javascript, not in the html code. this is done in onFocus().
  if(!event) {
    event = window.event;
  }
  var id=theMgBox.textboxid;
  var mytextbox = getElem("id",id);

  var textfieldcontents = mytextbox.value;
  
  if(textfieldcontents!=theMgBox.oldcontents && theMgBox.copiedValueBack != true) {
    // execute new query to fill listbox
    theMgBox.oldcontents = textfieldcontents;

    // call hook if present  
    if(typeof mgBox_onContentsChange_hook == 'function') {
      mgBox_onContentsChange_hook(id);
    }
  
    // We also try to parse the entry if a suitable
    // parsing function exists
    var parse_function = "mgBox_parse_"+id.removeTrailingDigitsIfPresent()+"_field";
    if (eval("typeof "+parse_function)=='function') {
      eval(parse_function+"('"+id+"')");
    }
    
    // we reset our timeout timer (search will only spring into action when the user does nothing
    // for IDLETIME ms so that we don't slow down the user experience.
    if (theMgBox.timerid!=null) {
      clearTimeout(theMgBox.timerid);
    }
    theMgBox.timerid = setTimeout("theMgBox.timeoutReached('"+theMgBox.textboxid+"')",theMgBox.IDLETIME);
  }
}
mgBox.prototype.onKeyUp = MgOnKeyUp;




function MgTimeoutReached(id) {
  // user was idle for IDLETIME ms, do some processing!
  this.timerid = null;

	// has the focus changed in the meantime?
  if (id!=theMgBox.textboxid) {
    return;
  }

  // we update the listbox if the contents of the text field have changed
  var mytextbox = getElem("id",id);
  var textfieldcontents = mytextbox.value;
  if(1) {
    // execute new query to fill listbox
    
    // first, show "updating..." indicator inside mgBox DIV
    var mgBoxDiv = getElem("id","mgBox");
    mgBoxDiv.innerHTML = "searching for '"+textfieldcontents+"'...";
    theMgBox.isOpenAndFilled = false;
    theMgBox.highlightedline = null;  // reset selection
    theMgBox.selectedSomething = false;  // reset selection
    
    // make sure the box is displayed (this may be the first time!!!)
    theMgBox.showMgBox();

    // cancel any previous http requests that are still active
    if( theMgBox.httprequestactive ) {
      // cancel currently ongoing request
      //this.httprequest.abort(); // see http://www.w3schools.com/dom/dom_http.asp
      // creates empty results, so we simply create a new object instead...
      delete theMgBox.httprequest; // not sure whether we need to delete objects, we do it just in case...
      theMgBox.httprequest = createXMLHttpRequest(); // create a new one...
      theMgBox.httprequestactive = false;
    }
    if( !theMgBox.httprequestactive ) {
      theMgBox.httprequestactive = true;
      // build name of cgi, based on textbox id (less 2-digit suffix if present)
      var cgi_name = theMgBox.ASSIST_CGI_TEMPLATE.replace(/@/,
                      theMgBox.textboxid.removeTrailingDigitsIfPresent());
      theMgBox.httprequest.open("GET", cgi_name+URLEncode(textfieldcontents),true);
      theMgBox.httprequest.onreadystatechange=function() {

// Ready States
//    * 0: UNINITIALIZED open() has not been called yet
//    * 1: LOADING Request not yet made (send() not called)
//    * 2: LOADED Contact established with server and HTTP status code recieved
//    * 3: INTERACTIVE In Mozilla, called multiple times while response is fetched - every 4096 bytes of response
//    * 4: COMPLETED Response complete

        if (theMgBox.httprequest.readyState==4) {
        	// has the focus changed in the meantime?
          if (theMgBox.httprequestactive == false) {
            return;
          }

          //alert(theMgBox.httprequest.readyState.toString() + " - " + theMgBox.httprequest.responseText)
          var mgBoxDiv = getElem("id","mgBox");
          // store result in internal arrays
          var result_array = theMgBox.httprequest.responseText.split("\n");
          if(result_array[0]!="magic") {
            // error
            theMgBox.hideMgBox();
            mgBoxDiv.innerHTML = "no magic";
          } else if(result_array.length<3) {
            // only magic, but no results
            theMgBox.hideMgBox();
            mgBoxDiv.innerHTML = "";
          } else {
            delete theMgBox.html_text_array;
            delete theMgBox.raw_text_array;
            delete theMgBox.id_array;
            theMgBox.html_text_array = new Array();
            theMgBox.raw_text_array = new Array();
            theMgBox.id_array = new Array();
            var total_html='<ul>';
            // file format is "html \n raw \n id \n [...]"
            for(var i=1; i<result_array.length-1; i+=3) { // ignore line after last \n
              theMgBox.html_text_array.push(result_array[i]);
              theMgBox.raw_text_array.push(result_array[i+1]);
              theMgBox.id_array.push(result_array[i+2]);
              total_html = total_html + '<li onMouseOver="enterListElement(this);" onclick="clickedListElement(this);" id="mb'+(((i-1)/3)+1)+'" ><div class="mgitem">' + result_array[i] + '</div></li>';
            }
            total_html = total_html + '</ul>';
//total_html = total_html + '<br><img border="0" src="/images/1pix.gif" width="200"/>';	
      
            mgBoxDiv.innerHTML = total_html;
            // highlight the first line
            //theMgBox.highlightedline = 0;
            //mgBoxDiv.childNodes[0].childNodes[theMgBox.highlightedline].className="mgHighlighted";
            theMgBox.isOpenAndFilled = true;
          }
          theMgBox.httprequestactive = false;
        }
      }
      theMgBox.httprequest.send(null);   // this will send the request
    }
  } 
  // otherwise we do nothing
}
mgBox.prototype.timeoutReached = MgTimeoutReached;

function MgShowMgBox() {
  box    = getElem("id","mgBox","");
  search = getElem("id","searchbox","");
  box1    = getElem("id","mgbox1","");

/*  par = this.textfieldhandle;
  x = findPosX(par);
  y = findPosY(par);
  box.style.top=y+par.offsetHeight+'px';
  box.style.left=x+'px';
  box.style.visibility = "visible";
*/
//  alert(box1.offsetWidth+","+box1.clientWidth);
  var x = search.offsetLeft;
  var y = search.offsetTop + search.offsetHeight ;

  var width = box1.offsetWidth;
  

//alert("test");
/*
  // deal with elements inside tables and such
  var parent = search;
  while (parent.offsetParent) {
    parent = parent.offsetParent;
    x += parent.offsetLeft;
    y += parent.offsetTop ;
  }
*/
//  alert (x+","+y);

  box.style.top=y+62+'px';
  box.style.left=x+10+'px';
  box.style.width=width-2+'px';
  box.style.visibility = "visible";

}
mgBox.prototype.showMgBox = MgShowMgBox;

function MgHideMgBox() {
  getElem("id","mgBox","").style.visibility = "hidden";
  theMgBox.isOpenAndFilled = false;
}
mgBox.prototype.hideMgBox = MgHideMgBox;

// iterate through all text fields, install handlers for
// all text fields with attribute "mgBox"
function MgInitMgBoxes() {

// This function was inspired by Mircho Mirev's cAutocomplete.autoInit()
//  mo /mo@momche.net/
//	Copyright (c) 2004 Mircho Mirev
//
//  console.log('init msg box');

  var nI = 0;
  var sLangAtt;
  var hTextField=null;
  var nInputsLength = document.getElementsByTagName( 'INPUT' ).length
  for( nI = 0; nI < nInputsLength; nI++ ) {
    if( document.getElementsByTagName( 'INPUT' )[ nI ].type.toLowerCase() == 'text' ) {

      sLangAtt = document.getElementsByTagName( 'INPUT' )[ nI ].getAttribute( 'mgBox' )
      if( sLangAtt != null && sLangAtt.length > 0 ) {

        //alert("i think i found one");
        hTextField=document.getElementsByTagName( 'INPUT' )[ nI ];
        //alert(hTextField.onfocus);
        hTextField.onfocus=theMgBox.onFocus;
        //hTextField.onclick=theMgBox.onFocus;
       
        hTextField.onblur=theMgBox.onBlur;
        //alert(hTextField.onfocus);
      }
    }
  }

  theMgBox.httprequestactive = false;
  theMgBox.textboxid = hTextField.id;
  theMgBox.textfieldhandle = hTextField;
  theMgBox.oldcontents= theMgBox.textfieldhandle.value;  // old contents = current contents

  // we attach the keydown handler
  theMgBox.textfieldhandle.onkeydown = theMgBox.onKeyDown;
  theMgBox.textfieldhandle.onkeyup = theMgBox.onKeyUp;
  theMgBox.textfieldhandle.onblur = theMgBox.onBlur;


  // call hook if present      
  if(typeof mgBox_onLoad_hook == 'function') {
    mgBox_onLoad_hook();
  }
}
mgBox.prototype.initMgBoxes = MgInitMgBoxes;


/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
////
////  the "main()" function
////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
theMgBox = new mgBox();
//console.log('mgBox created');

//theMgBox.initMgBoxes();
//alert(1);
// Attach onLoad handler...
if( window.attachEvent ) {
  window.attachEvent( 'onload', theMgBox.initMgBoxes )
} else if( window.addEventListener ) {
  window.addEventListener( 'load', theMgBox.initMgBoxes, false )
}
