/*******************************************************************************
 *
 *  client.js ver 1.0 (by krem - some parts use code snippets and suggestions
 *  from various public resources)
 *
 *  references:
 *
 *  - http://www.quirksmode.org/resources.html
 *  - http://www.howtocreate.co.uk/tutorials/javascript/
 *  - http://msdn.microsoft.com/workshop/author/om/measuring.asp
 *  - http://www.ditchnet.org/wp/2005/06/15/ajax-freakshow-drag-n-drop-events-2/
 *  - http://developer.apple.com/internet/webcontent/iframe.html
 *  - http://www.pxl8.com/iframes.html
 *  - http://www.w3schools.com/
 *  - http://blog.vishalon.net/Post/57.aspx
 *  - http://siderite.blogspot.com/2007/10/absolute-position-of-html-elements-in.html
 *  - ... and many more
 *
 *  features:
 *
 *  b1:
 *  - 15.12.2006 - bodyFloater, d&d, MouseEvent, TextUtil, WebForm anim,
 *                 WebGrid util, ...
 *
 *  b2:
 *  - 26.01.2007 - WebTree AJAX support
 *  - 01.02.2007 - beforeSubmit() and showLoading()
 *
 *  b3:
 *  - 06.02.2007 - support code for WebBubble
 *
 *  b4:
 *  - 27.02.2007 - added support for WebDesktop and browser-window size
 *
 *  b5:
 *  - 01.03.2007 - added general scroll methods
 *
 *  b6:
 *  - 30.03.2007 -  fixed bug: lost scroll state for bubble's inner components
 *
 *  b7:
 *  - 24.04.2007 - added WebDesktop and WebArea util methods
 *
 *  b8:
 *  - 07.06.2007 - fixed bug: invisible caret Gecko workaround caused invalid
 *                 WebBubble size for older Gecko engines.
 *  - 08.06.2007 - prevented WebBubble from exit the visible window area
 *
 *  b9:
 *  - 12.06.2007 - fixed bug: premature form submit = lost app state
 *               - minor changes in window initialization
 *
 *  b10:
 *  - 03.10.2007 - added bubble preview
 *
 *******************************************************************************
 *
 *  client.js ver 1.1 (by krem)
 *
 *  features:
 *
 *  b1:
 *  - 09.10.2007 - added support for WebDateChooser
 *  - 11.10.2007 - added support for WebDateChooser events
 *
 *  b2:
 *  - 13.11.2007 - fixed bug in WebDateChooser
 *
 *  b3:
 *  - 27.11.2007 - added control key attributes to mouse event
 *               - fixed bug in function TextUtil.endsWith
 *               - injecting rFrame only when needed (server doesn't generate
 *                 the rFrame anymore)
 *               - added an additional update tree margin by 2px to prevent
 *                 unnecessary tree updates in FireFox
 *               - added method cutString in TextUtil
 *               - added support for new WebListbox painter
 *
 *  b4:
 *  - 10.12.2007 - moved (add/remove)ElementOver and
 *                 (add/remove)ElementSelection methods to the util section and
 *                 upgraded both methods.
 *               - added support for WebGrid's advanced mode painter - WIP
 *               - added support for advanced HTML tooltips
 *
 *  b5:
 *  - 04.01.2008 - added Desktop3Painter support
 *               - added function submit()
 *
 *  b6:
 *  - 18.02.2008 - added focus handlers
 *
 *  b7:
 *  - 17.04.2008 - fixed bug in gridDoMouseDownClient when celection multiple
 *                 rows from bottom to up (using SHIFT)
 *
 *  b8:
 *  - 22.04.2008 - focus methods don't try to focus on disabled and readonly
 *                 components anymore
 *
 *******************************************************************************
 *
 *  client.js ver 1.2 (by krem)
 *
 *  features:
 *
 *  b1:
 *  - 30.04.2008 - full-ajax support (method form2axFrame)
 *
 *  b2:
 *  - added method hideLoading()
 *  - started adding reinitialization methods for full-ajax mode
 *  - added method injectEvent(element,eventName,code)
 *  - method deskStopDrag() now cleans the drag-border element
 *  - change inside mouse event constructor
 *  - fixed showLoading() - doesn't generate a new loading panel if one already
 *                          present
 *  - added method resetFullAjax()
 *  - added method showBubbleAjax()
 *  - added method gridFindGridTableElement(...) which is used by the
 *    gridDoMouseDownClient(...). Last selected index is now stored inside a
 *    table element rather than inside a div
 *
 *  b3:
 *  - moved window size tracking to application rather than WebDesktop
 *
 *  b4:
 *  - 30.07.2008 - added WebPanel advanced support
 *               - added WebMenu support
 *               - added WebApplication - WebEcho support
 *
 *  b5:
 *  - 12.12.2008 - added methods prepareSubmitParameter and forceSubmitSelection
 *
 *  b6:
 *  - 05.01.2009 - fixed method blockBackspace. Now should work well for IE and
 *                 FF.
 *
 *  b7:
 *  - 06.01.2009 - added KeyEvent and updated key-handlers
 *               - added the handleKeyEvents dispatcher
 *               - added the treeKeyHandler method
 *
 *  b8:
 *  - 08.01.2009 - bug-fix in convertCombos() for empty combos
 *               - upgraded method blockBackspace() with block_keys support
 *
 *  b9:
 *  - 27.02.2009 - added support for Grid4Painter (row height multipliers,
 *                 clicked cell index submission,  etc.)
 *
 *  b10:
 *  - 06.03.2009 - added support for linkButtons inside advanced WebGrid
 *
 *  b11:
 *  - 10.03.2009 - added method resetComponentFocus();
 *               - changes in WebGrid code (after-header-resize-submit support)
 *               - added method gridRowEditKeyHandler
 *  - 12.03.2009 - changes in WebGrid code (add-row support)
 *
 *  b12:
 *  - 07.04.2009 - added suggestion support
 *
 *  b13:
 *  - 14.04.2009 - added method forceCancelUpdate()
 *
 *  b15:
 *  - 16.04.2009 - added method menuFindAbsolutePosition(obj)
 *
 *  b16:
 *  - 06.05.2009 - suggestion text-field and combobox support
 *  - 07.05.2009 - WebGrid favorite support
 *
 *  b17:
 *  - 22.05.2009 - WebClientDocumentEdit support
 *
 *  b18:
 *  - 08.06.2009 - WebArea.WebAreaActionContainer support
 *
 *  b19:
 *  - 05.07.2009 - notifyComponentBlur calls resetComponentFocus again
 *
 *  b20:
 *  - 07.07.2009 - blocking all keys while events disabled
 *
 *  b21:
 *  - 13.07.2009 - fixed some issues on suggestion key handling
 *               - fixed some issues on component focus handling
 *               - fixed bug: collapsed advanced panel allowed focus gaining on
 *                 its children
 *  b22:
 *  - 14.07.2009 - rewritten suggestions and removed automatic suggestion list
 *                 loading
 *               - blockAllkeys always blocks the escape key
 *
 *  b23:
 *  - 15.07.2009 - fixed bug: notifyComponentBlur didn't reset the block_keys
 *                 array. Some keys remained blocked after switching focus
 *                 to another component.
 *               - added default mouse blocker
 *
 *  b24:
 *  - 20.07.2009 - fixed bug: "var" keyword not used for local variable
 *                 declaration in method checkBubbleHide.
 *
 *  b25:
 *  - 27.07.2009 - added support for suggestion-combos without the search field
 *  - 31.07.2009 - fixed some suggestion issues (fast typing forced the
 *                 StateManager to fork the current application's state, ...)
 *
 *  b26:
 *  - 03.08.2009 - fixed paint artifacts in showBubble when using IE (bubbles
 *                 inside WebGrid)
 *
 *  b27:
 *  - 05.08.2009 - new class Delay
 *
 *  b28:
 *  - 10.08.2009 - added tooltip support to favorites and fake favorites
 *                 (WebGrid, Suggestions)
 *
 *  b29:
 *  - 22.09.2009 - added mouse-click support on suggestion fields
 *
 *  b30:
 *  - 18.11.2009 - changed functions menuFindAbsolutePosition() and
 *                 gridFindCellElement(cellCandidate)
 *               - bugfix in WebFavoriteButton client code: favorite tooltip
 *                 may cause fork-application-state on the server.
 *
 *  b31:
 *  - 07.12.2009 - added support for WebPanelTabView
 *
 *  b32:
 *  - 09.12.2009 - fixed bug: WebSuggestionCombobox hangs the application if
 *                 interrupted while loading (e.g. by pressing the ESC key while
 *                 it's loading current list data).
 *
 *  b33:
 *  - 15.12.2009 - WebSuggestionTextField: every mouse click triggers a
 *                 suggestion request.
 *
 *  b34:
 *  - 15.12.2009 - added support for WebPanelTabView's tab overflow (tab widths
 *                 larger than available width)
 *
 *  b35:
 *  - 17.12.2009 - added parameter forcePrepare to
 *                 prepareSubmitParameter(sel_container_id,value,forcePrepare)
 *
 *  b36:
 *  - 15.02.2010 - added webGrid statusbar support in gridResizeColumn(e)
 *
 *  b37:
 *  - 15.03.2010 - text-event support on text fields (including suggestion) and
 *                 text-area. Changed notifyTextComponentFocus and
 *                 notifyTextComponentBlur methods. Added method
 *                 fireTextComponentEvent. WebSuggestionTextField fires text
 *                 events on suggestion-select.
 *               - upgraded DebugClient
 *
 *  b38:
 *  - 18.03.2010 - marker support for components (see focus methods)
 *  - 22.03.2010 - double click common and WebGrid support
 *
 *  b39:
 *  - 06.04.2010 - changed suggestDoMouseClick definition due to IE problems.
 *                 Now accepts a guid rather than a mouse event.
 *
 *  b40:
 *  - 07.04.2010 - WebCheckbox and radio action events.
 *
 *  b41:
 *  - 16.04.2010 - WebDesktopContainer left tab delayed hiding support
 *               - cleaning errors as suggested by jslint.
 *
 *  b42:
 *  - 22.04.2010 - minor changes in suggestion code (outer div doesn't expand to
 *                 entire window area)
 *
 *  b43:
 *  - 18.05.2010 - tab-view: tabViewDoPrepareOverflow adopted to "table" rather
 *                 than "ul".
 *  - 20.05.2010 - radio buttons: new method radioDoChanged that supports radio
 *                 triggers
 *
 *  b44:
 *  - 31.05.2010 - added support for clear button in suggestion combo
 *
 *  b45:
 *  - 14.07.2010 - fixed bug in date chooser: inadequate event handling caused
 *                 event race conditions between WebDateChoosed and
 *                 WebDateBubble on server. Fixed methods
 *                 DateChooser.prototype.setDateSafe and
 *                 DateChooser.prototype.mouseClick.
 *
 *  b46:
 *  - 23.07.2010 - added method selectText
 *
 *  b47:
 *  - 26.07.2010 - added getAllModifiers in KeyEvent
 *               - changed WebGrid's edit/add submit methods. Now sending key
 *                 modifiers info if available.
 *
 *  b48:
 *  - 24.08.2010 - replaced all blank.html with px.gif
 *
 *  b49:
 *  - 13.09.2010 - added WebClientPDFSign support
 *
 *******************************************************************************
 *
 *  client.js ver 1.3 (by krem)
 *
 *  features:
 *
 *  b1:
 *  - 09.12.2010 - new AJAX class
 *               - DebugClient upgrade: supports IDE (eclipse) connections
 *                 (opens source files and scripts directly in IDE in runtime).
 *                 
 *  - 24.12.2010 - added constant SUPPORT_IE6. function convertCombos runs only 
 *                 if the said constant equals true.
 *  
 *  b2:
 *  - 28.12.2010 - added _disableFocusEventsOnce in fireTextComponentEvent.
 *               - changes in suggestCommitSelection() (text field handling 
 *                 events)
 *               - suggestDisableBlurEventsOnce and suggestEnableBlurEvents
 *               
 *  b3:
 *  - 28.03.2011 - docClientEditStart adopted to app_services (VINP)
 *  
 *  b4:
 *  - 06.04.2011 - new method preloadLoadingImage
 *
 ******************************************************************************/

/******************************************************************************/
/* initialization                                                             */
/******************************************************************************/

var SUPPORT_IE6 = false;
var loadingTimeout = null;

/**
 *
 **************************************/
function hideLoading() {
 if (loadingTimeout) {
   clearTimeout(loadingTimeout);

   //
   loadingTimeout = null;
 }

 var loading = document.getElementById("_sys_load_icon_");
 if (loading) {
   loading.style.visibility = 'hidden';
   loading.style.display = 'none';
 }
}

/**
 *
 **************************************/
window.onload = function() {
  try {
    saveWindowSize();
  }
  catch (ex) {
    doDebug('INIT GET WINDOW SIZE ERROR:');
    doDebug(ex.name+': '+ex.message);
    doDebug(' ');
  }

  //
  setEnabledEvents(true);

  //
  try {
    initPage();
  }
  catch (ex) {
    doDebug('INIT SCRIPT ERROR:');
    doDebug(ex.name+': '+ex.message);
    doDebug(' ');
  }

  //
  //self.focus(); // may interfere with other component focus requests
};


/******************************************************************************/
/* ajax essentials                                                            */
/******************************************************************************/

var AJAX = {

  //
  XMLHttpFactories: [
    function () {return new XMLHttpRequest()},
    function () {return new ActiveXObject("Msxml2.XMLHTTP")},
    function () {return new ActiveXObject("Msxml3.XMLHTTP")},
    function () {return new ActiveXObject("Microsoft.XMLHTTP")}
  ],

  //
  sendRequest: function (url,responseHandler,errorHandler,postData) {
    var req = AJAX.createXMLHTTPObject();
    if (!req) return;

    //
    var method = (postData) ? "POST" : "GET";
    req.open(method,url,true);
    req.setRequestHeader('User-Agent','XMLHTTP/1.0');
    if (postData) req.setRequestHeader('Content-type','application/x-www-form-urlencoded');

    //
    req.onreadystatechange = function () {
      if (req.readyState!=4) return;
      if (req.status!=200 && req.status!=304) {
        errorHandler(req);
        return;
      }
      responseHandler(req);
    }

    //
    if (req.readyState==4) return;
    req.send(postData);
  },

  //
  createXMLHTTPObject: function() {
    var xmlhttp = false;
    for (var i=0;i<AJAX.XMLHttpFactories.length;i++) {
      try {
        xmlhttp = AJAX.XMLHttpFactories[i]();
      }
      catch (e) {
        continue;
      }
      break;
    }
    return xmlhttp;
  }

}//AJAX

/******************************************************************************/
/* full-ajax                                                                  */
/******************************************************************************/

var block_backspace = true;
var block_keys = null;
var block_all_keys = true;

/**
 *
 **************************************/
function keysReinitialize() {
  block_keys = null;
  block_all_keys = false;
  block_backspace = true;

  //
  var element = document.getElementById("_app_comp_key_event_");
  if (element) {
    element.parentNode.removeChild(element);
  }
}

/**
 *
 **************************************/
function reinitializePage() {
  keysReinitialize();

  //
  deskReinitialize();
  bubbleReinitialize();
  gridReinitialize();

  //
  hideLoading();
  setEnabledEvents(true);
  if (defaultTooltip) {
    defaultTooltip.hide();
  }

  //
  deskReinitialize();

  //
  mouseDoDoubleClickReinitialize();
}

var fullAjax = false;

/**
 *
 **************************************/
function prepareFullAjax() {
  doDebug("FULL AJAX");

  var form = document.appform;
  form.target = "axFrame";

  //
  var directUpdate = document.getElementById("_direct_app_update");
  if (directUpdate) {
    directUpdate.value = "1";
  }

  //
  fullAjax = true;
}

/**
 *
 **************************************/
function resetFullAjax() {
  doDebug("RESET FULL AJAX");

  var form = document.appform;
  form.target = "_self";

  //
  var directUpdate = document.getElementById("_direct_app_update");
  if (directUpdate) {
    directUpdate.value = "0";
  }

  //
  fullAjax = false;

  //
  form.submit();
}

/******************************************************************************/
/* Load Info                                                                  */
/******************************************************************************/

/**
 * LoadInfo :: constructor
 **************************************/
function LoadInfo() {
  this.loading = false;
}

/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function getLoadInfo() {
  if (!document.body.tLoadInfo) {
    document.body.tLoadInfo = new LoadInfo();
  }

  return document.body.tLoadInfo;
}


/******************************************************************************/
/* HTTP form                                                                  */
/******************************************************************************/

window.hasBeforeSubmit = true;

/**
 * 
 **************************************/
function preloadLoadingImage() {
  if (document.images) {
    var img = new Image();
    img.src = "/public/_common_look/loading.gif";
  }
}

/**
 *
 **************************************/
function showLoading() {
  var dx = 0;
  var dy = 0;

  //
  if (document.body.scrollLeft) {
    dx = document.body.scrollLeft;
  }
  else if (window.pageXOffset) {
    dx = window.pageXOffset;
  }

  //
  if (document.body.scrollTop) {
    dy = document.body.scrollTop;
  }
  else if (window.pageYOffset) {
    dy = window.pageYOffset;
  }

  //
  var loading = document.getElementById("_sys_load_icon_");
  if (!loading) {
    var element = document.createElement("div");
    element.id = "_sys_load_icon_";
    document.body.appendChild(element);
    //
    element.style.position = "absolute";
    //
    //element.style.right = (dx+8)+"px";
    //element.style.top = (dy+8)+"px";
    element.style.left = "49%";
    element.style.top = "45%";
    //
    element.style.width = "46px";
    element.style.height = "46px";
    element.style.zIndex = "1000000000";
    element.innerHTML = '<div style="display:block; border:3px solid #007DB5;background:white;padding:4px;"><center><img src="/public/_common_look/loading.gif"></center></div>';
  }
  else {
   loading.style.display = '';
   loading.style.visibility = '';
  }

  //
  loadingTimeout = null;
}

/**
 *
 **************************************/
function beforeSubmit(event) {
  if (!isEnabledEvents()) {
    var ev = new GenericEvent(event);
    ev.consume();
    return false;
  }

  //
  setEnabledEvents(false);
  saveScroll();

  //
  loadingTimeout = setTimeout(showLoading,10); // FIXME

  //
  //doDebug('before submit: target='+document.forms[0].target);
}

/**
 * Generates a hidden input if not found
 **************************************/
function prepareSubmitParameter(sel_container_id,value,forcePrepare) {
  if (isEnabledEvents() || forcePrepare) {
    var element = document.getElementById(sel_container_id);

    //
    if (!element) {
      element = document.createElement("input");
      element.type = "hidden";
      element.id = sel_container_id;
      element.name = sel_container_id;
      element.value = value;
      document.appform.appendChild(element);
    }

    //
    else {
      element.value = value;
    }
  }
}

/**
 *
 **************************************/
function removeSubmitParameter(sel_container_id) {
  var el = document.getElementById(sel_container_id);
  if (el && el.parentNode) {
    el.parentNode.removeChild(el);
  }
}

/**
 *
 **************************************/
function submit() {
 if (isEnabledEvents()) {
   try{
     beforeSubmit();
   }
   catch(ex){}

   //doDebug('submit all: target='+document.forms[0].target);

   //
   var dblc = mouseWasDoubleClick();
   if (dblc) {
     prepareSubmitParameter("_sys_mdblc_","1",true);
   }
   // submit
   document.forms[0].submit();
   //
   if (dblc) {
     removeSubmitParameter("_sys_mdblc_");
   }
 }
}

/**
 *
 **************************************/
function submitSelection(sel_container_id,value) {
  if (isEnabledEvents()) {

    //
    document.getElementById(sel_container_id).value=value;

    //
    submit();

    //
    /* REMOVEME
    try{
      beforeSubmit();
    }
    catch(ex){}

    //
    //doDebug('submitSelection: ' + sel_container_id + ' = ' + value + ', target='+document.forms[0].target);
    prepareSubmitParameter("_sys_mdblc_","1");
    document.forms[0].submit();
    removeSubmitParameter("_sys_mdblc_");
    */
  }
}

/**
 * Generates a hidden input if not found and submits the form
 **************************************/
function forceSubmitSelection(sel_container_id,value) {
  if (isEnabledEvents()) {
    prepareSubmitParameter(sel_container_id,value);

    //
    submitSelection(sel_container_id,value);
  }
}

/**
 * Generates a hidden input if not found and submits the form.
 * Deletes the genereated input element after submission.
 **************************************/
function forceSubmitSelectionOnce(sel_container_id,value) {
  if (isEnabledEvents()) {
    forceSubmitSelection(sel_container_id,value);
    removeSubmitParameter(sel_container_id);
  }
}

/**
 *
 *************************************/
function forceCancelUpdate() {
  var rFrame = document.getElementById("rFrame");
  if (rFrame) {
    rFrame.src = "/public/_common_look/px.gif";
  }
}

/**
 *
 *************************************/
function tryInjectRFrame() {
  if (!document.getElementById("rFrame")) {
    var element = document.createElement("div");
    //
    var style = element.style;
    style.position = "absolute";
    style.left = "0px";
    style.top = "0px";
    style.width = "1px";
    style.height = "1px";
    style.zIndex = "-100";
    //
    element.innerHTML = '<iframe name="rFrame" id="rFrame" style="width:1px;height:1px;border:0px;" src="/public/_common_look/px.gif"></iframe>';
    //
    document.body.appendChild(element);
  }
}

var formTarget = null;

/**
 *
 **************************************/
function form2frame() {
  tryInjectRFrame();

  //
  var form = document.appform;
  //form.method = "POST";
  //form.action = "tree.srv";
  formTarget = form.target;
  form.target = "rFrame";
}

/**
 *
 **************************************/
function form2self() {
  var form = document.appform;
  //form.method = "GET";
  //form.action = "";
  //form.target = "_self";
  form.target = formTarget;

  //
  if (fullAjax) {
    prepareFullAjax(); // reinitialize full ajax after single component update
  }
}

/**
 *
 **************************************/
function setFormWillUpdate(update) {
  var doUpdate = document.getElementById("doUpdate");
  var directUpdate = document.getElementById("_direct_app_update");
  //
  if (doUpdate) {
    if (update) {
      doUpdate.value = "1";
      directUpdate.value = "1";
    }
    else {
      doUpdate.value = "0";
      directUpdate.value = "0";
    }
  }
}

/******************************************************************************/
/* Scroll info in general                                                     */
/******************************************************************************/

/**
 *
 **************************************/
function saveScrollInfo(destId,sLeft,sTop) {
  var dest = document.getElementById(destId);
  //
  dest.value = sLeft+","+sTop;
}

/**
 *
 **************************************/
function initializeScroll(destId,sLeft,sTop) {
  var dest = document.getElementById(destId);
  //
  dest.scrollLeft = sLeft;
  dest.scrollTop = sTop;
}

/**
 *
 **************************************/
function getElementScroll(element) {
  return [element.scrollLeft,element.scrollTop];
}

/******************************************************************************/
/* HTTP table                                                                 */
/******************************************************************************/


/**
 * Deprecated - new WebGrid painters (should) use TR styles to achieve this!
 **************************************/
function setRowStyle(row,style,startIndex) {
  var cells = row.cells;
  var len = cells.length;
  for (var i=startIndex; i<len; i++) {
    cells[i].className = style;
  }
}

var BLINK_TIME = 100;

/**
 *
 **************************************/
function blinkRow(rowId,count) {
  var row = document.getElementById(rowId);

  //
  if (row) {
    var cells = row.cells;
    var len = cells.length;

    //
    if (count>0) {
      var fw = (count%2==0 ? "green":"white");

      for (var i=0; i<len; i++) {
        cells[i].style.color = fw;
      }
      //
      var newCall = "blinkRow('"+rowId+"',"+(count-1)+");";
      setTimeout(newCall,BLINK_TIME);
      //window.alert(newCall);
    }
    else {
      for (var i=0; i<len; i++) {
        cells[i].style.color = "";
      }
    }
  }
}

/******************************************************************************/
/* Common components                                                          */
/******************************************************************************/

/**
 *
 **************************************/
function doGetCaretPosition(element) {
 var CaretPos = 0;

 // IE Support
 if (document.selection) {
   element.focus();
   var sel = document.selection.createRange();
   sel.moveStart('character',-element.value.length);
   pos = sel.text.length;
 }

 // Firefox support
 else if (element.selectionStart || element.selectionStart == '0') {
   pos = element.selectionStart;
 }

 //
 return pos;
}

/**
 *
 **************************************/
function setCaretPosition(element,pos) {
 //
 if(element.setSelectionRange) {
   element.focus();
   element.setSelectionRange(pos,pos);
 }

 //
 else if (element.createTextRange) {
   var range = element.createTextRange();
   range.collapse(true);
   range.moveEnd('character',pos);
   range.moveStart('character',pos);
   range.select();
 }
}

/**
 *
 **************************************/
function selectText(element,pos1,pos2) {
  //
  if(element.setSelectionRange) {
    element.focus();
    element.setSelectionRange(pos1,pos2);
  }

  //
  else if (element.createTextRange) {
    var range = element.createTextRange();
    range.collapse(true);
    range.moveEnd('character',pos2);
    range.moveStart('character',pos1);
    range.select();
  }
}

/**
 *
 **************************************/
function submitKeyEvent(keyInfo) {
  //doDebug(keyInfo.id+","+keyInfo.event.keyNum);
  forceSubmitSelectionOnce(
    "_app_comp_key_event_",
    keyInfo.id
    +"," +keyInfo.event.keyNum
    +","+(keyInfo.event.isCtrl?"1":"0")
    +","+(keyInfo.event.isAlt?"1":"0")
    +","+(keyInfo.event.isShift?"1":"0")
  );
}

/**
 *
 **************************************/
function handleGlobalKeys(event) {
  var ev = new KeyEvent(event);

  //
  //doDebug("Global key: "+ev.src.tagName+" "+ev.keyChar+" "+ev.keyNum);
}

/**
 *
 **************************************/
function blockBackspace(event) {
  var ev = new KeyEvent(event);
  var keyNum = ev.keyNum;

  //
  if (block_backspace && keyNum==KeyEvent.KEY_BACKSPACE) {
    ev.consume();
    return false;
  }

  //
  if (block_keys!=null) {
    var len = block_keys.length;
    for (var i=0; i<len; i++) {
      if (keyNum==block_keys[i]) {
        ev.consume();
        return false;
      }
    }
  }

  //
  return true;
}

/**
 *
 **************************************/
function blockAllKeys(event) {
  var ev = new KeyEvent(event);

  //
  if (block_all_keys) {
    ev.consume();
    return false;
  }

  // always block escape on the top
  else {
    if (ev.keyNum==KeyEvent.KEY_ESC) {
      ev.consume();
    }
  }

  //
  return true;
}

/**
 *
 **************************************/
function blockMouse(event) {
  if (!isEnabledEvents()) {
    var ev = new GenericEvent(event);
    ev.consume();
    return false;
  }

  //
  return true;
}

/**
 *
 **************************************/
function storeComponentFocus(guid) {
  var focusElement = document.getElementById("_app_comp_focus_");

  //
  if (!focusElement) {
    focusElement = document.createElement("input");
    focusElement.type = "hidden";
    focusElement.id = "_app_comp_focus_";
    focusElement.name = "_app_comp_focus_";
    //
    document.appform.appendChild(focusElement);
  }

  //
  focusElement.value = guid;
}

/**
 *
 **************************************/
function resetComponentFocus() {
  var focusElement = document.getElementById("_app_comp_focus_");
  if (focusElement) {
    focusElement.parentNode.removeChild(focusElement);
  }
}

/**
 *
 **************************************/
function doComponentFocus(element) {
  if (!element.disabled) {
    element.focus();
  }
  //if (!element.disabled && !element.readonly) element.focus();
}

/**
 *
 **************************************/
function doTextComponentFocus(element) {
  doComponentFocus(element);
  //
  if (!element.disabled /*&& !element.readonly*/) {
    if (element.value.length>0) {
      setCaretPosition(element,element.value.length);
    }
  }
}

/**
 *
 **************************************/
function moveMarkerOut(element) {
  if (element.id) {
    var marker = document.getElementById(element.id+"_$mk$_");
    if (marker) {
      //
      if (marker._anim) {
        marker._anim.stop();
      }

      //
      var anim = new Animator(100,-90*2,200);
      anim.setAnimHandler(
        function(value) {
          setOpacity(marker,value/100);
        }
      );
      anim.setStopHandler(
        function(value) {
          setOpacity(marker,0.1);
          marker._anim = null;
        }
      );

      //
      marker._anim = anim;
      anim.start();

      // setOpacity(marker,0.2);


      //
      /*
      var width = marker.offsetWidth;
      var pos = (marker._originalPos) ? marker._originalPos : findAbsolutePosition(marker);
      if (!marker._originalPos) marker._originalPos = pos;

      //
      var anim = new Animator(pos.left,-width,250);
      anim.setAnimHandler(
        function(value) {
          marker.style.left = value + "px";
        }
      );
      anim.setStopHandler(
        function(value) {
          marker.style.left = (marker._originalPos.left - width) + "px";
          marker._anim = null;
        }
      );

      //
      marker._anim = anim;
      anim.start();

      //
      //marker.style.left = (pos.left - width) + "px";
      */
    }
  }
}

/**
 *
 **************************************/
function moveMarkerIn(element) {
 if (element.id) {
   var marker = document.getElementById(element.id+"_$mk$_");
   if (marker) {
     //
     if (marker._anim) {
       marker._anim.stop();
     }

     //
     var anim = new Animator(10,90*2,200);
     anim.setAnimHandler(
       function(value) {
         setOpacity(marker,value/100);
       }
     );
     anim.setStopHandler(
       function(value) {
         setOpacity(marker,1);
         marker._anim = null;
       }
     );

     //
     marker._anim = anim;
     anim.start();

     //
     /*
     var width = marker.offsetWidth;
     var pos = findAbsolutePosition(marker);

     //
     var anim = new Animator(pos.left,width,250);
     anim.setAnimHandler(
       function(value) {
         marker.style.left = value + "px";
       }
     );
     anim.setStopHandler(
       function(value) {
         marker.style.left = marker._originalPos.left + "px";
         marker._anim = null;
       }
     );

     //
     marker._anim = anim;
     anim.start();

     //marker.style.left = (pos.left + width) + "px";
     */

     setOpacity(marker,1);
   }
 }
}

/**
 *
 **************************************/
function notifyComponentFocus(event,element) {
  storeComponentFocus(element.id);
  //doDebug("focus gained: "+element.id);
  moveMarkerOut(element);
}

/**
 * @param doEvent (boolean) if true then an event should be fired if component's
 * text changed while it had focus.
 **************************************/
function notifyTextComponentFocus(event,element,doEvent) {
  //doDebug("TXT FOCUS "+element.id);
  //doDebug("block_backspace = "+block_backspace);

  element.style.borderStyle = "dotted";
  element.style.borderColor = "#404040";
  notifyComponentFocus(event,element);
  //
  block_backspace = false;

  // only elements with a valid id may fire change events
  if (doEvent && element.id) {
    // notify other events that this component handles text events
    element._isTxtEv = true;
    // save current value
    element._oldValue = element.value ? element.value : "";
  }
}

/**
 *
 **************************************/
function notifyComponentBlur(event,element) {
  //doDebug("focus lost: "+element.id);
  resetComponentFocus();
  block_keys = null; // reset blocked keys
  moveMarkerIn(element);
}

/**
 *
 **************************************/
function fireTextComponentEvent(element) {
 var handler = function() { 
   // if we still don't have focus
   //if (element._oldValue==null) {
   if (!element._disableFocusEventsOnce) forceSubmitSelectionOnce(element.id+'_$tc$_','1');
   else element._disableFocusEventsOnce = false;
   element._oldValue = null;
   //}
   // else do nothing (some components e.g. suggestion text-fields may gain 
   // focus right after loosing it). This lost focus must therefore be ignored
 }
 
 //
 setTimeout(handler,20);
 //element._oldValue = null;
}


/**
 * @param doEvent (boolean) if true then an event should be fired if component's
 * text changed while it had focus.
 **************************************/
function notifyTextComponentBlur(event,element,doEvent) {
  //doDebug("TXT BLUR "+element.id);
  //doDebug("block_backspace = "+block_backspace);
  //if (element._disableFocusEvents) return;
  
  element.style.borderStyle = "solid";
  element.style.borderColor = "";
  notifyComponentBlur(event,element);
  //
  block_backspace = true;

  // fire an event after a short period of time so the next component in tab
  // list will have enough time to accept focus.
  if (doEvent && element.id) {
    var newVal = element.value ? element.value : "";
    if (element._oldValue != newVal) {
      fireTextComponentEvent(element);
    }
  }
}

/**
 *
 **************************************/
function handleComponentEnterKeyToButton(event,element,guid) {
  var e = new KeyEvent(event);

  //
  if (e.keyNum==13) {
    document.getElementById(guid).click();
    e.consume();
    return false;
  }
  else {
    return true;
  }

  //
  return true;
}

/**
 *
 **************************************/
function handleComponentEnterKeyToImageButton(event,element,guid,value) {
  var e = new KeyEvent(event);

  //
  if (e.keyNum==13) {
    submitSelection(guid,value);
    e.consume();
    return false;
  }
  else {
    return true;
  }

  //
  return true;
}

/**
 *
 **************************************/
function blockComponentEnterKey(event,element) {
  var e = new KeyEvent(event);

  //
  if (e.keyNum==13) {
    e.consume();
    return false;
  }

  //
  return true;
}

/**
 * Handles key events from a specified component. If a keyNumArray is given,
 * dispatches only key events using the keys inside the array.
 * If a null keyNumArray is given, dispatches all events. Events are always
 * dispatched to the given handler.
 **************************************/
function handleKeyEvents(event,compId,keyNumArray,handler) {
  var keyEvent = new KeyEvent(event);

  // IE block key handler on BODY
  block_keys = keyNumArray;

  //
  if (!isEnabledEvents()) {
    keyEvent.consume();
    return false;
  }

  //
  if (keyNumArray!=null) {
    var keyNum = keyEvent.keyNum;
    var len = keyNumArray.length;
    var ok = false;
    for (var i=0; i<len; i++) {
      if (keyNumArray[i]==keyNum) {
        ok = true;
        break;
      }
    }

    //
    if (!ok) {
      return true;
    }
  }

  //
  return handler({id:compId, event:keyEvent});
}

/******************************************************************************/
/* WebForm                                                                    */
/******************************************************************************/

var SCROLL_DELTA_TIME = 25;
var SCROLL_STEP       = 1;
var SCROLL_DELTA_STEP = 7;

/**
 *
 **************************************/
function animOpenY(scrollerId, max_scroll) {
  setTimeout("incHeight('"+scrollerId+"',1,"+SCROLL_DELTA_TIME+","+SCROLL_STEP+","+SCROLL_DELTA_STEP+","+max_scroll+")",SCROLL_DELTA_TIME);
}

/**
 *
 **************************************/
function animCloseY(scrollerId, wrapperId, max_scroll) {
  document.getElementById(scrollerId).style.height=max_scroll;
  document.getElementById(wrapperId).style.display="none";
  setTimeout("decHeight('"+scrollerId+"',"+max_scroll+","+SCROLL_DELTA_TIME+","+SCROLL_STEP+","+SCROLL_DELTA_STEP+")",SCROLL_DELTA_TIME);
}

/**
 *
 **************************************/
function incHeight(divElement, size, delta_time, step, delta_step, max_scroll) {
  var div = document.getElementById(divElement);
  step += delta_step;
  size += step;
  if (size<max_scroll) {
    div.style.height = size;
    setTimeout("incHeight('"+divElement+"',"+size+","+delta_time+","+step+","+delta_step+","+max_scroll+")",delta_time);
  }
  else {
    div.style.height = "";
    div.style.overflow = "";
  }
}

/**
 *
 **************************************/
function decHeight(divElement, size, delta_time, step, delta_step) {
  var div = document.getElementById(divElement);
  step += delta_step;
  size -= step;
  if (size>0) {
    div.style.height = size;
    setTimeout("decHeight('"+divElement+"',"+size+","+delta_time+","+step+","+delta_step+")",delta_time);
  }
  else {
    div.style.height = 1;
    div.style.display = "none";
  }
}


/******************************************************************************/
/* BodyFloater                                                                */
/******************************************************************************/


/**
 *
 **************************************/
function BodyFloater() {
  this.OFFSET_X = 4;
  this.OFFSET_Y = 16;
  //
  this.x = 0;
  this.y = 0;
  this.visible = false;
  this.drop = false;
  //
  this.init();
}

/**
 *
 **************************************/
BodyFloater.prototype.init = function() {
  var el = document.createElement("div");
  el.innerHTML = '<div id="body_floater" class="floater" style="filter:alpha(opacity=100);opacity:1;display:none;width:24px;height:22px;z-index:1000;position: absolute;left:0px;top:0px;"></div>';
  document.body.appendChild(el);
  //
  this.element = document.getElementById("body_floater");
};

/**
 *
 **************************************/
BodyFloater.prototype.getElement = function() {
  return this.element;
};

/**
 *
 **************************************/
BodyFloater.prototype.setVisible = function(visible) {
  this.visible = visible;
  //
  if (visible) {
    var element = this.getElement();
    element.style.display = "inline";
    setOpacity(element,0.6);
  }
  else {
    this.getElement().style.display = "none";
  }
};

/**
 *
 **************************************/
BodyFloater.prototype.moveTo = function(x,y) {
  this.x = x;
  this.y = y;
  //
  var style = this.getElement().style;
  style.left = x+"px";
  style.top = y+"px";
};

/**
 *
 **************************************/
BodyFloater.prototype.mouseMoved = function(mouseEvent) {
  this.moveTo(mouseEvent.x+this.OFFSET_X,mouseEvent.y+this.OFFSET_Y);
};

/**
 *
 **************************************/
BodyFloater.prototype.setContent = function(srcElement) {
  var floaterElement = this.getElement();
  floaterElement.innerHTML = srcElement.innerHTML;
  //
  var className = srcElement.className;
  if (className) {
    floaterElement.className = TextUtil.cropStringEnd(className,"_over")+"_cant";
  }
};

/**
 *
 **************************************/
BodyFloater.prototype.setCanDrop = function(drop) {
  this.drop = drop;
  var floaterElement = this.getElement();
  var className = floaterElement.className;
  //
  var classNameEndsWithCant = TextUtil.endsWith(className,"_cant");
  if (drop && classNameEndsWithCant) {
    floaterElement.className = TextUtil.cropStringEnd(className,"_cant");
  }
  else if (!drop && !classNameEndsWithCant) {
    floaterElement.className += "_cant";
  }
};

/**
 *
 **************************************/
BodyFloater.prototype.canDrop = function() {
  return this.drop;
};


/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function getBodyFloater() {
  if (!document.body.tFloater) {
    document.body.tFloater = new BodyFloater();
  }

  //
  return document.body.tFloater;
}


/******************************************************************************/
/* WebDragAndDrop                                                             */
/******************************************************************************/


/**
 *
 **************************************/
function activateDrops(idList,dragId) {
  var len = idList.length;
  for (var i=0; i<len; i++) {
    var element = document.getElementById(idList[i]);
    if (element) {
      if (TextUtil.endsWith(element.className,"_disabled")) {
        element.className = TextUtil.cropStringEnd(element.className,"_disabled")+"_anim";
        element.tWasDisabled = true;
      }
      else {
        element.className += "_anim";
        element.tWasDisabled = false;
      }
      //
      element.tDragId = dragId;
    }
  }
}

/**
 *
 **************************************/
function deactivateDrops(idList) {
  var len = idList.length;
  for (var i=0; i<len; i++) {
    var element = document.getElementById(idList[i]);
    if (element) {
      var className = element.className;
      if (TextUtil.endsWith(className,"_over")) {
        element.className = className.substring(0,className.length-10);
      }
      else {
        element.className = className.substring(0,className.length-5);
      }

      //
      if (element.tWasDisabled) {
        element.className += "_disabled";
      }

      //
      element.tDragId = null;
      element.tWasDisabled = false;
    }
  }
}

/**
 *
 **************************************/
function getDDState(element) {
  return document.getElementById(element.id+"_state");
}

/**
 *
 **************************************/
function processDragMove(e) {
 if (!isEnabledEvents()) {
   return true;
 }

 //
 var event = new MouseEvent(e);
 var src = event.source;
 var floater = getBodyFloater();

 //
 var dragId = src.tDragId ? src.tDragId : null;
 if (dragId!=null) {
   if (!floater.canDrop()) {
     floater.setCanDrop(true);
   }
 }
 else {
   if (floater.canDrop()) {
     floater.setCanDrop(false);
   }
 }

 //
 floater.mouseMoved(event); // TODO IE
 //defaultStatus = src.disabled;

 //
 event.consume();
 return false;
}

/**
 *
 **************************************/
function processDragStop(e) {
 if (!isEnabledEvents()) {
   return true;
 }
 var event = new MouseEvent(e);
 var src = event.source;
 //
 MouseEvent.removeEventListener(document,"mousemove",processDragMove,false);
 MouseEvent.removeEventListener(document,"mouseup",processDragStop,false);
 //
 var floater = getBodyFloater();
 var dragId = src.tDragId ? src.tDragId : null;
 floater.setVisible(false);
 //
 if (floater.idList) {
   deactivateDrops(floater.idList);
   floater.idList = null;
 }
 //
 document.body.ondrag = returnTrue;
 document.body.onselectstart = returnTrue;
 document.onmousedown = returnTrue;
 //
 if (dragId!=null) {
   submitSelection(src.id+"_state",dragId);
 }
 // return true;
}

/**
 *
 **************************************/
function processDragStart(e,idList) {
  if (!isEnabledEvents()) {
    return true;
  }
  var event = new MouseEvent(e);

  //
  if (idList) {
    activateDrops(idList,event.source.id);
  }
  //
  var floater = getBodyFloater();
  floater.setContent(event.source);
  floater.mouseMoved(event);
  floater.setVisible(true);
  floater.idList = idList;
  //
  MouseEvent.addEventListener(document,"mousemove",processDragMove,false);
  MouseEvent.addEventListener(document,"mouseup",processDragStop,false);
  //
  document.body.ondrag = returnFalse;
  document.body.onselectstart = returnFalse;
  document.onmousedown = returnFalse;
  //
  return false;
}

/**
 *
 **************************************/
function processDDMouseOver(e) {
  if (!isEnabledEvents()) {
    return true;
  }
  var event = new MouseEvent(e);
  var src = event.source;
  //
  if (!TextUtil.endsWith(src.className,"_disabled")) {
    src.className += "_over";
  }
}

/**
 *
 **************************************/
function processDDMouseOut(e) {
  if (!isEnabledEvents()) {
    return true;
  }
  var event = new MouseEvent(e);
  var src = event.source;
  //
  if (!TextUtil.endsWith(src.className,"_disabled")) {
    src.className = TextUtil.cropStringEnd(src.className,"_over");
  }
}

/******************************************************************************/
/* WebTree                                                                       */
/******************************************************************************/

/**
 *
 **************************************/
function Tree(owner) {
  this.owner = owner;
  this.cachedPages = 1;
  //
  this.LINE_HEIGHT = 20;
  this.MAX_CACHED_PAGES = 5;
}

/**
 *
 **************************************/
Tree.prototype.setLoading = function(loading) {
  // nothing here for now
};

/**
 * @param scrollTag - "TOP", "BOTTOM", "NONE"
 **************************************/
Tree.prototype.setScrollInfo = function(scrollTag,scrollElement,topLoadedId) {
  if (topLoadedId) {
    this.getStateElement().value = scrollTag+","+scrollElement.scrollLeft+","+scrollElement.scrollTop+","+scrollElement.scrollWidth+","+topLoadedId;
  }
  else {
    this.getStateElement().value = scrollTag+","+scrollElement.scrollLeft+","+scrollElement.scrollTop+","+scrollElement.scrollWidth;
  }
};

/**
 *
 **************************************/
Tree.prototype.checkNeedsFill = function() {
  var ownerId = this.owner.id;
  var scrollLeft = this.owner.scrollLeft;
  var scrollTop = this.owner.scrollTop;
  var clientH = this.owner.clientHeight;
  //
  var topEmpty = this.getTopEmptyElement();
  var topH = topEmpty.offsetHeight;
  //
  if (topH>=this.LINE_HEIGHT && scrollTop<=topH-2) {
    this.setScrollInfo("TOP",this.owner,this.getTopLoadedLineId());
    return true;
  }

  var bottomEmpty = this.getBottomEmptyElement();
  var bottomH = bottomEmpty.offsetHeight;
  var bottomTop = bottomEmpty.offsetTop;
  //
  if (bottomH>=this.LINE_HEIGHT && scrollTop+clientH>=bottomTop) {
    this.setScrollInfo("BOTTOM",this.owner,this.getBottomLoadedLineId());
    return true;
  }

  this.setScrollInfo("NONE",this.owner,"");
  //
  return false;
};

/**
 *
 **************************************/
Tree.prototype.getStateElement = function() {
  return document.getElementById(this.owner.id+"_st");
};

/**
 *
 **************************************/
Tree.prototype.getTopLoadedLineId = function() {
  var topElement = document.getElementById(this.owner.id+"_e_top");
  var topLoaded = topElement.nextSibling.firstChild;
  //
  if (topLoaded) {
    return topLoaded.rows[0].cells[1].firstChild.id;
  }
  else {
    return "NULL";
  }
};

/**
 *
 **************************************/
Tree.prototype.getBottomLoadedLineId = function() {
  var bottomElement = document.getElementById(this.owner.id+"_e_bottom");
  var bottomLoaded = bottomElement.previousSibling.lastChild;
  //
  if (bottomLoaded) {
    return bottomLoaded.rows[bottomLoaded.rows.length-1].cells[1].firstChild.id;
  }
  else {
    return "NULL";
  }
};

/**
 *
 **************************************/
Tree.prototype.getTopEmptyElement = function() {
  return document.getElementById(this.owner.id+"_e_top");
};

/**
 *
 **************************************/
Tree.prototype.getBottomEmptyElement = function() {
  return document.getElementById(this.owner.id+"_e_bottom");
};

/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function getTree(owner) {
  if (!owner.treeObj) {
    owner.treeObj = new Tree(owner);
  }
  //
  return owner.treeObj;
}

/**
 *
 **************************************/
function initializeTree(id,scrollLeft,scrollTop) {
  var treeElement = document.getElementById(id);
  //
  treeElement.scrollTop = scrollTop;
  treeElement.scrollLeft = scrollLeft;
}

/**
 *
 **************************************/
function updateTree(srcDoc,srcId,destId,fillPosition,moreDataToCome) {
  var src = srcDoc.getElementById(srcId);
  var target = document.getElementById(destId);
  var tree = getTree(target);
  var div = document.createElement("div");
  //
  if (src.childNodes.length>0) {
    div.innerHTML = src.innerHTML;
    //
    var srcH = src.childNodes.length*tree.LINE_HEIGHT;
    tree.cachedPages++;
    //
    if (fillPosition=="TOP") {
      // remove cached pages if necessary
      if (tree.cachedPages>tree.MAX_CACHED_PAGES) {
        var bottomElement = tree.getBottomEmptyElement();
        var elementToRemove = bottomElement.previousSibling;
        var addH = elementToRemove.offsetHeight;
        //
        target.removeChild(elementToRemove);
        bottomElement.style.height = bottomElement.offsetHeight+addH+"px";
        tree.cachedPages--;
      }
      //
      var topElement = tree.getTopEmptyElement();
      var topH = topElement.offsetHeight;
      topH -= srcH;
      //
      if (moreDataToCome) {
        if (topH<tree.LINE_HEIGHT) {
          topElement.style.height = (tree.LINE_HEIGHT+1)+"px";
          if (target.scrollTop<(tree.LINE_HEIGHT+1)) {
            target.scrollTop = tree.LINE_HEIGHT+1;
          }
        }
        else {
          topElement.style.height = topH+"px";
        }
        //
      }
      else {
        topElement.style.height = "1px";
      }
      target.insertBefore(div,topElement.nextSibling);
    }
    else if (fillPosition=="BOTTOM") {
      // remove cached pages if necessary
      if (tree.cachedPages>tree.MAX_CACHED_PAGES) {
        var topElement = tree.getTopEmptyElement();
        var elementToRemove = topElement.nextSibling;
        var addH = elementToRemove.offsetHeight;
        //
        target.removeChild(elementToRemove);
        topElement.style.height = topElement.offsetHeight+addH+"px";
        tree.cachedPages--;
      }
      //
      var bottomElement = tree.getBottomEmptyElement();
      target.insertBefore(div,bottomElement);
      var bottomH = bottomElement.offsetHeight;
      bottomH -= srcH;

      if (moreDataToCome) {
        if (bottomH<tree.LINE_HEIGHT) {
          bottomElement.style.height = (tree.LINE_HEIGHT+1)+"px";
        }
        else {
          bottomElement.style.height = bottomH+"px";
        }
      }
      else {
        bottomElement.style.height = (tree.LINE_HEIGHT-1)+"px";
      }
    }
  }
  else {
    if (fillPosition=="TOP") {
      tree.getTopEmptyElement().style.height="1px";
    }
    else if (fillPosition=="BOTTOM") {
      tree.getBottomEmptyElement().style.height= (tree.LINE_HEIGHT-1)+"px";
    }
  }
  //
  getLoadInfo().loading = false;
  tree.setLoading(false);
  //
  if (tree.checkNeedsFill()) {
    setTimeout("processTreeScroll(document.getElementById('"+destId+"'))",20); // TODO
  }
}

/**
 *
 **************************************/
function absoluteUpdateTree(srcDoc,srcId,destId) {
  var src = srcDoc.getElementById(srcId);
  var target = document.getElementById(destId);
  var tree = getTree(target);
  //
  if (src.childNodes.length>0) {
    target.innerHTML = src.innerHTML;
  }
  //
  getLoadInfo().loading = false;
  tree.setLoading(false);
  tree.cachedPages = 1;
  //
  if (tree.checkNeedsFill()) {
    setTimeout("processTreeScroll(document.getElementById('"+destId+"'))",20); // TODO
  }
}

/**
 *
 **************************************/
function makeRequestForTreeScroll(owner) {
  var tree = getTree(owner);
  if (tree.checkNeedsFill()) {
    getLoadInfo().loading = true;
    tree.setLoading(true);
    //
    form2frame();
    setFormWillUpdate(true);
    document.appform.submit();
    setFormWillUpdate(false);
    form2self();
  }
}

/**
 *
 **************************************/
function processTreeScroll(owner) {
  if (!isEnabledEvents()) {
    return true;
  }

  //
  if (!getLoadInfo().loading) {
    makeRequestForTreeScroll(owner);
  }

  //
  return true;
}

/******************************************************************************/
/******************************************************************************/

/**
 * @param eventInfo: {id,event}
 **************************************/
function treeKeyHandler(eventInfo) {
  submitKeyEvent(eventInfo);
  setEnabledEvents(false);

  //
  eventInfo.event.consume();
  return false;
}

/******************************************************************************/
/* WebBubble                                                                  */
/******************************************************************************/
var BUBBLE_WINDOW_GAP = 14;
var ID_PREFIX_BUBBLE_ELEMENT = 'bub_x_elem_x_';

var mX = 0;  // start mouse x
var mY = 0;  // start mouse y
var bdX = 0; // start bubble dx
var bdY = 0; // start mouse dy

var bubbleId = null;
var bubbleW = 0;
var bubbleH = 0;

var bPInfo = null;

/**
 *
 **************************************/
function bubbleReinitialize() {
  mX = 0;
  mY = 0;
  bdX = 0;
  bdY = 0;

  bubbleId = null;
  bubbleW = 0;
  bubbleH = 0;

  bPInfo = null;
}


/**
 * BubblePreviewInfo
 **************************************/
function BubblePreviewInfo(id,x,y,dx,dy) {
  this.COUNT = 14;
  this.W1 = 2;
  this.H1 = 2;
  this.W2 = 16;
  this.H2 = 16;
  this.W3 = 100;
  this.H3 = 60;

  //
  this.x = x;
  this.y = y;
  this.id = id;

  //
  this.xx = x - 2;
  this.yy = y - 2;
  this.dxx = (1.0*dx)/this.COUNT;
  this.dyy = (1.0*dy)/this.COUNT;
  this.dww = (1.0*(this.W2-this.W1))/this.COUNT;
  this.dhh = (1.0*(this.H2-this.H1))/this.COUNT;
}

/**
 *
 **************************************/
function saveBubbleState(id,type,x,y,dx,dy) {
  var hidden = document.getElementById(id+"_sbm_");
  //
  hidden.value = type+","+x+","+y+","+dx+","+dy;
}

/**
 *
 **************************************/
function submitBubbleShow(event,id,dx,dy) {
  e = new MouseEvent(event);

  //
  //bPInfo = new BubblePreviewInfo(id,e.x,e.y,dx,dy);
  //prepareBubblePreview();
  //doBubblePreview(0);
  //

  submitSelection(id,"1,"+e.x+","+e.y);
  //submitSelection(id,"1,"+100+","+100);
}

/**
 *
 **************************************/
function doBubblePreview(i) {
  // i
  var element = document.getElementById(ID_PREFIX_BUBBLE_ELEMENT+i);
  if (element) {
    element.style.visibility="visible";
  }
  else {
    submitSelection(bPInfo.id,"1,"+bPInfo.x+","+bPInfo.y);
    return;
  }

  // i+1
  element = document.getElementById(ID_PREFIX_BUBBLE_ELEMENT+(i+1));
  if (element) {
    element.style.visibility="visible";
  }
  else {
    submitSelection(bPInfo.id,"1,"+bPInfo.x+","+bPInfo.y);
    return;
  }

  // i+2
  element = document.getElementById(ID_PREFIX_BUBBLE_ELEMENT+(i+2));
  if (element) {
    element.style.visibility="visible";
  }
  else {
    submitSelection(bPInfo.id,"1,"+bPInfo.x+","+bPInfo.y);
    return;
  }

  // i+3
  element = document.getElementById(ID_PREFIX_BUBBLE_ELEMENT+(i+3));
  if (element) {
    element.style.visibility="visible";
  }
  else {
    submitSelection(bPInfo.id,"1,"+bPInfo.x+","+bPInfo.y);
    return;
  }

  //
  setTimeout('doBubblePreview('+(i+4)+')',20);
}

/**
 *
 **************************************/
function prepareBubblePreview() {
  for (var i =0; i<bPInfo.COUNT; i++) {
    prepareSingleBubblePreview(i);
  }
}

/**
 *
 **************************************/
function prepareSingleBubblePreview(i) {
  var currW = bPInfo.W1+parseInt(i*bPInfo.dww);
  var currH = bPInfo.H1+parseInt(i*bPInfo.dhh);

  var element = document.createElement("div");
  element.id = ID_PREFIX_BUBBLE_ELEMENT+i;
  element.name = ID_PREFIX_BUBBLE_ELEMENT+i;
  document.body.appendChild(element);
  //
  if (i<bPInfo.COUNT-1) {
    element.style.left = parseInt(bPInfo.xx+i*bPInfo.dxx-currW/2)+"px";
    element.style.top = parseInt(bPInfo.yy+i*bPInfo.dyy-currH/2)+"px";
    element.style.width = currW+"px";
    element.style.height = currH+"px";
    element.innerHTML = '';
  }
  else {
    element.style.left = parseInt(bPInfo.xx+i*bPInfo.dxx-bPInfo.W3/2)+"px";
    element.style.top = parseInt(bPInfo.yy+i*bPInfo.dyy-bPInfo.H3/2)+"px";
    element.style.width = bPInfo.W3+"px";
    element.style.height = bPInfo.H3+"px";
    element.innerHTML = '';
  }
  element.style.position = "absolute";
  element.style.overflow = "hidden";
  element.style.zIndex = 11000000+i;
  element.style.border = "3px solid #4751B3";
  element.style.background = "#F1F1DB";
  //element.style.visibility = "hidden";
}

/**
 *
 **************************************/
function submitBubbleHide(event,id) {
  var hidden = document.getElementById(id+"_sbm_");
  var value = "2"+hidden.value.substring(1);
  //
  submitSelection(id+"_sbm_",value);
}

/**
 *
 **************************************/
function checkBubbleHide(event,id) {
  var e = new MouseEvent(event);

  //
  var el = e.source;
  if (el.id && el.id==id) {
    submitBubbleHide(event,id);
  }

  //
  return true;
}

/**
 * Returns recalculated dx and dy (may be the same as the given ones).
 **************************************/
function checkBubbleInsideWindow(x,y,dx,dy,bW,bH) {
  var wSize = getWindowSize();
  var bW2 = parseInt(bW/2)+1;
  var bH2 = parseInt(bH/2)+1;
  var xx = x+dx;
  var yy = y+dy;
  //
  if (xx-bW2<BUBBLE_WINDOW_GAP) {
    dx += bW2-xx+BUBBLE_WINDOW_GAP;
  }
  else if (xx+bW2>wSize[0]-BUBBLE_WINDOW_GAP) {
    dx -= bW2+xx-wSize[0]+BUBBLE_WINDOW_GAP;
  }
  //
  if (yy-bH2<BUBBLE_WINDOW_GAP) {
    dy += bH2-yy+BUBBLE_WINDOW_GAP;
  }
  else if (yy+bH2>wSize[1]-BUBBLE_WINDOW_GAP) {
    dy -= bH2+yy-wSize[1]+BUBBLE_WINDOW_GAP;
  }
  //
  return [dx,dy];
}

/**
 *
 **************************************/
function isComboConvertable(requestingComponent,combo) {
  var parent = combo.parentNode;

  //
  while (parent!=document) {
    if (parent==requestingComponent) {
      return false;
    }
    parent = parent.parentNode;
  }

  //
  return true;
}

/**
 * IE combo z-index workaround
 **************************************/
function convertCombos(requestingComponent) {
  if (!SUPPORT_IE6) return;
  
  //
  try {
    if (document.getElementsByTagName) {
      var combos = document.getElementsByTagName("select");
      var len = combos.length;
      var holders = []; // must save placeholders first - removing combos in first loop affects the combo array
      var labels = [];
      var widths = [];

      //
      for (var i=0; i<len; i++) {
        var combo = combos[i];

        //
        if (isComboConvertable(requestingComponent,combo)) {
          holders[i] = combo.parentNode;
          if (combo.length>0 && combo.selectedIndex>=0) {
            labels[i] = combo[combo.selectedIndex].text;
          }
          else {
            labels[i] = "";
          }
          widths[i] = combo.offsetWidth;
        }
        else {
          holders[i] = null;
          labels[i] = null;
          widths[i] = null;
        }

      }

      //
      for (var i=0; i<len; i++) {
        var holder = holders[i];
        if (holder!=null && combos[i]) { // combos[i] may not exist (don't know why)
          holder.innerHTML = '<input type="text" name="_dmycombo'+i+'_" id="_dmycombo'+i+'_" class="'+
                              combos[i].className+'" style="padding-left:4px;width:'+
                              widths[i]+'px" value="'+labels[i]+'">';
        }
      }

    }
  }
  catch (ex) {
    doDebug(ex);
  }
}

/**
 *
 **************************************/
function showBubble(id,x,y,dx,dy) {
  //
  var outer = document.getElementById(id);

  //
  outer.parentNode.removeChild(outer);
  document.appform.appendChild(outer);
  outer.className =  ""; // this fixes some paint artifacts in IE (bubble painted inside WebGrid)

  //
  var size = getWindowSize();
  var scroll = getWindowScroll();

  //
  outer.style.width = (size[0]+scroll[0]-5)+"px";
  outer.style.height = (size[1]+scroll[1]-5)+"px";
  //outer.style.display = "block";
  outer.style.visibility = "visible";
  //outer.style.background = "gray";
  //
  var bubble = outer.lastChild;

  // bubble content
  var content = bubble.rows[bubble.rows.length-2].cells[1];
  var contentDiv = content.firstChild; // Gecko BUG WORKAROUND
  //

  var cW = contentDiv.scrollWidth;
  var cH = contentDiv.scrollHeight;
  //
  //content.style.width = (cW+2)+"px";
  //content.style.height = (cH+2)+"px";

  // content div (Gecko BUG WORKAROUND)
  contentDiv.style.width = (cW+17)+"px";
  contentDiv.style.height = (cH+2)+"px";

  // bubble coordinates
  bubble.bx = x;
  bubble.by = y;
  //
  var bW = bubble.offsetWidth;
  var bH = bubble.offsetHeight;

  // check if inside visible window area
  var recalced = checkBubbleInsideWindow(x,y,dx,dy,bW,bH);
  dx = recalced[0];
  dy = recalced[1];

  // show bubble
  doShowBubbleMoved(outer,x,y,dx,dy,bW,bH);

  //
  convertCombos(outer);
}

/**
 *
 **************************************/
function showBubbleAjax(id,x,y,dx,dy) {
  var outer = document.getElementById(id);

  //
  var size = getWindowSize();
  var scroll = getWindowScroll();

  //
  outer.style.width = (size[0]+scroll[0]-5)+"px";
  outer.style.height = (size[1]+scroll[1]-5)+"px";
  //outer.style.display = "block";
  outer.style.visibility = "visible";
  //outer.style.background = "gray";
  //
  var bubble = outer.lastChild;

  // bubble coordinates
  bubble.bx = x;
  bubble.by = y;
  //
  var bW = bubble.offsetWidth;
  var bH = bubble.offsetHeight;

  // check if inside visible window area
  var recalced = checkBubbleInsideWindow(x,y,dx,dy,bW,bH);
  dx = recalced[0];
  dy = recalced[1];

  // show bubble
  doShowBubbleMoved(outer,x,y,dx,dy,bW,bH);
}


/**
 *
 **************************************/
function doShowBubbleMoved(el,x,y,dx,dy,bW,bH) {

  // check if inside visible window area
  var recalced = checkBubbleInsideWindow(x,y,dx,dy,bW,bH);
  dx = recalced[0];
  dy = recalced[1];

  //
  saveBubbleState(el.id,"0",x,y,dx,dy);

  //
  var children = el.childNodes;
  var bubble = el.lastChild;

  //
  var bLeft = parseInt(x+dx-bW/2);
  var bTop = parseInt(y+dy-bH/2);
  var bWidth = bW;
  var bHeight = bH;

  //
  bubble.style.left = bLeft+"px";
  bubble.style.top = bTop+"px";

  //
  bubble.style.width = bWidth;
  bubble.style.height = bHeight;

  //
  var len1 = children.length-1;

  //
  var errBubble = children[len1-1];
  var hasErrorBubble = (errBubble && errBubble.tagName && errBubble.tagName.toUpperCase()=="TABLE");
  if (hasErrorBubble) {
    len1--;
  }

  //
  for (var i=0; i<len1; i++) {
    var element = children[i];
    bW = element.offsetWidth;
    bH = element.offsetHeight;
    //
    if (element.style) {
      element.style.left = parseInt(x+dx*i/len1-bW/2)+"px";
      element.style.top = parseInt(y+dy*i/len1-bH/2)+"px";
    }
  }

  //
  if (hasErrorBubble) {
    var ebW = errBubble.offsetWidth;
    //
    errBubble.style.left = bLeft;
    errBubble.style.top = (bTop+bHeight-7)+"px";
  }
}

/**
 *
 **************************************/
function startBubbleDrag(event) {
  e = new MouseEvent(event);

  var bubble = e.source;
  //setOpacity(bubble,0.5);
  //
  //if (el.tagName && el.tagName.toUpperCase()!="TD") return true;
  while (bubble.tagName && bubble.tagName.toUpperCase()!="TABLE") {
    bubble = bubble.parentNode;
  }

  //
  mX = e.x;
  mY = e.y;

  //
  bubbleId = bubble.parentNode.id;
  bubbleW = bubble.offsetWidth;
  bubbleH = bubble.offsetHeight;

  //
  bdX = (bubble.offsetLeft+parseInt(bubbleW/2)-bubble.bx);
  bdY = (bubble.offsetTop+parseInt(bubbleH/2)-bubble.by);

  //
  MouseEvent.addEventListener(document,"mousemove",doBubbleDrag,false);
  MouseEvent.addEventListener(document,"mouseup",stopBubbleDrag,false);

  //
  document.body.ondrag = returnFalse;
  document.body.onselectstart = returnFalse;
  document.onmousedown = returnFalse;

  //
  e.consume();
  return false;
}

/**
 *
 **************************************/
function doBubbleDrag(event) {
  e = new MouseEvent(event);

  //
  var outer = document.getElementById(bubbleId);
  var bubble = outer.lastChild;

  //
  var dX = e.x - mX;
  var dY = e.y - mY;
  var newX = bdX + dX;
  var newY = bdY + dY;

  //
  doShowBubbleMoved(outer,bubble.bx,bubble.by,newX,newY,bubbleW,bubbleH);

  //
  e.consume();
  return false;
}

/**
 *
 **************************************/
function stopBubbleDrag(event) {
  e = new MouseEvent(event);
  //
  //var bubble = e.source;
  //setOpacity(bubble,1);

  //
  MouseEvent.removeEventListener(document,"mousemove",doBubbleDrag,false);
  MouseEvent.removeEventListener(document,"mouseup",stopBubbleDrag,false);

  //
  document.body.ondrag = returnTrue;
  document.body.onselectstart = returnTrue;
  document.onmousedown = returnTrue;

  //
  e.consume();
  return false;
}


/******************************************************************************/
/* WebDesktop                                                                 */
/******************************************************************************/
var RESIZE_DELTA_TIME = 200;
var initSize = getWindowSize();
var willResize = false;

/**
 *
 **************************************/
function deskReinitialize() {
  initSize = getWindowSize();
  willResize = false;

  //
  if (lastDragBorder) {
    lastDragBorder.parentNode.removeChild(lastDragBorder);
    lastDragBorder = null;
  }
}

/**
 *
 **************************************/
function processDTabEnter(tab) {
  if (!TextUtil.endsWith(tab.className,"_over")) {
    tab.className += "_over";
  }
}

/**
 *
 **************************************/
function processDTabExit(tab) {
  if (TextUtil.endsWith(tab.className,"_over")) {
    tab.className = TextUtil.cropStringEnd(tab.className,"_over");
  }
}

/**
 *
 **************************************/
function checkDResize() {
  if (!willResize) {
    var size = getWindowSize();
    //
    if (initSize[0]!=size[0] || initSize[1]!=size[1]) {
      willResize = true;
      initSize = size;
      setTimeout("submitDResize('')",RESIZE_DELTA_TIME);
    }
  }
}

/**
 *
 **************************************/
function submitDResize() {
  var size = getWindowSize();
  //
  if (initSize[0]!=size[0] || initSize[1]!=size[1]) {
    // still resizing --> submit later
    initSize = size;
    setTimeout("submitDResize('')",RESIZE_DELTA_TIME);
  }
  else {
    saveWindowSize();
    submit();
  }
}


/******************************************************************************/
/* WebDesktop - Desktop3Painter                                               */
/******************************************************************************/

var DRAG_BORDER_ID = "drg_brd_id_";
var DESK_MIN_W = 300;
var DESK_MIN_H = 200;
var DESK_X_GAP = 18;
var DESK_Y_GAP = 18;

var lastDragBorder = null;

var lastZIndex = 0;
var dragStartX = 0;
var dragStartY = 0;
var dragSourceStartX = 0;
var dragSourceStartY = 0;
var dragSourceStartW = 0;
var dragSourceStartH = 0;
var dragSource = null;
var dragType = "none";

/**
 *
 **************************************/
function getCurrentDragBorder() {
  if (!lastDragBorder) {
    lastDragBorder = document.createElement("div");
    document.body.appendChild(lastDragBorder);

    var style = lastDragBorder.style;
    //
    style.position = "absolute";
    style.left = dragSourceStartX + "px";
    style.top = dragSourceStartY + "px";
    style.width = dragSourceStartW + "px";
    style.height = dragSourceStartH + "px";
    style.zIndex = lastZIndex+1;
    style.border = "2px dotted #400000";
    style.margin = 0;
    style.padding = 0;
  }

  //
  return lastDragBorder;
}

/**
 *
 **************************************/
function deskDoDrag(e) {
  var event = new MouseEvent(e);

  // move
  if (dragType=="move") {
    var dX = event.x - dragStartX - DESK_X_GAP;
    var dY = event.y - dragStartY - DESK_Y_GAP;
    //
    var style = dragSource.style;
    style.left = (dragSourceStartX + dX) + "px";
    style.top = (dragSourceStartY + dY) + "px";
  }

  // resize - right
  else if (dragType=="right") {
    var dragBorderElement = getCurrentDragBorder();
    //
    var dW = event.x - dragStartX;
    var w = dragSourceStartW + dW;
    if (w<DESK_MIN_W) {
      w = DESK_MIN_W;
    }
    //
    dragBorderElement.style.width = w + "px";
  }

  // resize - bottom_right
  else if (dragType=="bottom_right") {
    var dragBorderElement = getCurrentDragBorder();
    //
    var dW = event.x - dragStartX;
    var w = dragSourceStartW + dW;
    if (w<DESK_MIN_W) {
      w = DESK_MIN_W;
    }
    //
    var dH = event.y - dragStartY;
    var h = dragSourceStartH + dH;
    if (h<DESK_MIN_H) {
      h = DESK_MIN_H;
    }
    //
    dragBorderElement.style.width = w + "px";
    dragBorderElement.style.height = h + "px";
  }

  // resize - bottom
  else if (dragType=="bottom") {
    var dragBorderElement = getCurrentDragBorder();
    //
    var dH = event.y - dragStartY;
    var h = dragSourceStartH + dH;
    if (h<DESK_MIN_H) {
      h = DESK_MIN_H;
    }
    //
    dragBorderElement.style.height = h + "px";
  }

  // resize - bottom_left
  else if (dragType=="bottom_left") {
    var dragBorderElement = getCurrentDragBorder();
    //
    var dW = event.x - dragStartX;
    var w = dragSourceStartW - dW;
    if (w<DESK_MIN_W) {
      w = DESK_MIN_W;
    }
    //
    var dH = event.y - dragStartY;
    var h = dragSourceStartH + dH;
    if (h<DESK_MIN_H) {
      h = DESK_MIN_H;
    }
    //
    var x = dragSourceStartX + dragSourceStartW - w;
    //
    dragBorderElement.style.width = w + "px";
    dragBorderElement.style.left = x + "px";
    dragBorderElement.style.height = h + "px";
  }

  // resize - left
  else if (dragType=="left") {
    var dragBorderElement = getCurrentDragBorder();
    //
    var dW = event.x - dragStartX;
    //
    var w = dragSourceStartW - dW;
    if (w<DESK_MIN_W) {
      w = DESK_MIN_W;
    }
    //
    var x = dragSourceStartX + dragSourceStartW - w;
    //
    dragBorderElement.style.width = w + "px";
    dragBorderElement.style.left = x + "px";
  }

}

/**
 *
 **************************************/
function deskStartDrag(e,type) {
  var event = new MouseEvent(e);
  dragStartX = event.x;
  dragStartY = event.y;

  //
  dragSource = event.source.parentNode;
  dragSourceStartX = dragSource.offsetLeft + DESK_X_GAP;
  dragSourceStartY = dragSource.offsetTop + DESK_Y_GAP;
  dragSourceStartW = dragSource.offsetWidth - 2*DESK_X_GAP;
  dragSourceStartH = dragSource.offsetHeight - 2*DESK_Y_GAP;
  dragType = type;

  //
  var zIndex = dragSource.style.zIndex;
  if (lastZIndex<zIndex) {
   lastZIndex = parseInt(zIndex) + 8;
  }
  else {
    lastZIndex += 8;
  }
  //
  dragSource.style.zIndex = lastZIndex;

  //
  MouseEvent.addEventListener(document,"mousemove",deskDoDrag,false);
  MouseEvent.addEventListener(document,"mouseup",deskStopDrag,false);
  //
  document.body.ondrag = returnFalse;
  document.body.onselectstart = returnFalse;
  document.onmousedown = returnFalse;

  //
  return false;
}

/**
 *
 **************************************/
function deskSaveFloatingParams(floatElement, dragBorder) {
  // find the id of the input element
  var id = TextUtil.cropStringEnd(floatElement.id,"x");
  var val = null;

  // resized
  if (dragBorder) {
    val = (dragBorder.offsetLeft - DESK_X_GAP) + ","
          + (dragBorder.offsetTop - DESK_Y_GAP ) + ","
          + (dragBorder.offsetWidth + 2*DESK_X_GAP) + ","
          + (dragBorder.offsetHeight + 2*DESK_Y_GAP) + ","
          + floatElement.style.zIndex;
  }

  // moved
  else {
    val = floatElement.offsetLeft+","
          + floatElement.offsetTop+","
          + floatElement.offsetWidth+","
          + floatElement.offsetHeight+","
          + floatElement.style.zIndex;
  }

  //
  document.getElementById(id).value = val;
}

/**
 *
 **************************************/
function deskStopDrag(e) {
  var event = new MouseEvent(e);

  //
  MouseEvent.removeEventListener(document,"mousemove",deskDoDrag,false);
  MouseEvent.removeEventListener(document,"mouseup",deskStopDrag,false);
  //
  document.body.ondrag = returnTrue;
  document.body.onselectstart = returnTrue;
  document.onmousedown = returnTrue;
  //
  event.consume();

  //
  deskSaveFloatingParams(dragSource,lastDragBorder);
  if (lastDragBorder) {
    submit();
  }

  //
  dragSource = null;

  //
  return false;
}

/******************************************************************************/
/* WebDesktopContainer                                                        */
/******************************************************************************/

var deskContTabHidingDelay = null;

/**
 *
 **************************************/
function deskContTabMouseOver(el) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  if (deskContTabHidingDelay!=null) {
    if (deskContTabHidingDelay.el==el) {
      deskContTabHidingDelay.stop(); // stop without running the delay job
    }
    else {
      deskContTabHidingDelay.doDelay(); // force the delay job and stop (immediately hides the other tab)
    }

    //
    deskContTabHidingDelay = null;
  }

  //
  addElementOver(el);
}

/**
 *
 **************************************/
function deskContTabMouseOut(el) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  deskContTabHidingDelay = new Delay(500, function() {
    removeElementOver(el);
    deskContTabHidingDelay = null;
  });
  deskContTabHidingDelay.el = el;
  deskContTabHidingDelay.start();
}

/******************************************************************************/
/* WebDateChooser                                                             */
/******************************************************************************/

/**
 * DateChooser: constructor
 **************************************/
function DateChooser(owner) {
  this.owner = owner;
  this.date = new Date();

  //
  this.TEMPLATE_DAY = "[%DAY%]";
  this.TEMPLATE_MONTH = "[%MONTH%]";
  this.TEMPLATE_YEAR = "[%YEAR%]";
  this.VALUE_ELEMENT_POSTFIX = "_val_";
  this.EVENT_PREFIX = "EVENT_";
}

/**
 * day: [1,31]
 * month: [0,11]
 * year:  [....,.....]
 **************************************/
DateChooser.prototype.init = function(day,month,year,monthNames,labelTemplate,doEvents) {
  this.date = new Date();
  this.monthNames = monthNames;
  this.labelTemplate = labelTemplate;
  this.setDateSafe(day,month,year);
  this.doEvents = doEvents;
  //
  this.repaint();
};

/**
 *
 **************************************/
DateChooser.prototype.getCurrentLabel = function() {
  if (this.labelTemplate) {
    var label = this.labelTemplate.replace(this.TEMPLATE_DAY,this.date.getDate());
    label = label.replace(this.TEMPLATE_MONTH,this.monthNames[this.date.getMonth()]);
    label = label.replace(this.TEMPLATE_YEAR,this.date.getFullYear());
    //
    return label;
  }
  //
  return "";
};

/**
 *
 **************************************/
DateChooser.prototype.setDateSafe = function(day,month,year) {
  var safeDay = this.clampDay(day,month,year);
  this.date.setFullYear(year,month,safeDay);
  //
  var valueElement = document.getElementById(this.owner.id+this.VALUE_ELEMENT_POSTFIX);
  valueElement.value = safeDay+'.'+(month+1)+'.'+year;
};

/**
 *
 **************************************/
DateChooser.prototype.yearBack = function(num) {
  this.setDateSafe(this.date.getDate(),this.date.getMonth(),this.date.getFullYear()-num);
  this.repaint();
};

/**
 *
 **************************************/
DateChooser.prototype.yearFwd = function(num) {
  this.setDateSafe(this.date.getDate(),this.date.getMonth(),this.date.getFullYear()+num);
  this.repaint();
};

/**
 *
 **************************************/
DateChooser.prototype.monthBack = function(num) {
  var month = this.date.getMonth();
  if (month-num<0) {
    this.setDateSafe(this.date.getDate(),12-(num-month),this.date.getFullYear());
    this.yearBack(1);
  }
  else {
    this.setDateSafe(this.date.getDate(),month-num,this.date.getFullYear());
    this.repaint();
  }
};

/**
 *
 **************************************/
DateChooser.prototype.monthFwd = function(num) {
  var month = this.date.getMonth();
  if (month+num>11) {
    this.setDateSafe(this.date.getDate(),(month+num)%12,this.date.getFullYear());
    this.yearFwd(1);
  }
  else {
    this.setDateSafe(this.date.getDate(),month+num,this.date.getFullYear());
    this.repaint();
  }
};

/**
 *
 **************************************/
DateChooser.prototype.getDaysInMonth = function(month,year) {
  return 32 - new Date(year,month,32).getDate();
};

/**
 *
 **************************************/
DateChooser.prototype.clampDay = function(day,month,year) {
  var maxDay = this.getDaysInMonth(month,year);
  //
  return day>maxDay?maxDay:day;
};

/**
 * monday = 1, ..., sunday = 7
 **************************************/
DateChooser.prototype.getStartCell = function(year, month) {
  var startCell = (new Date(year,month,1)).getDay();
  return (startCell>0) ? startCell : 7;
};

/**
 *
 **************************************/
DateChooser.prototype.isCellInRange = function(idx) {
  var startCell = this.getStartCell(this.date.getFullYear(),this.date.getMonth());
  var dayCount = this.getDaysInMonth(this.date.getMonth(),this.date.getFullYear());
  var endCell = startCell+dayCount;

  //
  return idx>=startCell && idx<endCell;
};

/**
 *
 **************************************/
DateChooser.prototype.repaint = function() {
  // paint year
  var year = this.date.getFullYear();
  this.owner.rows[0].cells[2].innerHTML = year;

  // paint month
  var month = this.date.getMonth();
  var monthCells = this.owner.rows[1].cells[0].childNodes[0].rows[0].cells;
  var len = monthCells.length;
  for (var i=0; i<len; i++) {
    if (i==month) {
      monthCells[i].className = "selected";
    }
    else {
      monthCells[i].className = "";
    }
  }

  // paint days
  var day = this.date.getDate();
  var dayCells = 42;
  var startCell = this.getStartCell(year,month);

  var dayCount = this.getDaysInMonth(month,year);
  var endCell = startCell+dayCount;
  var column = 0;
  var row = 3;

  for (var i=0; i<dayCells; i++) {
    var cell = this.owner.rows[row].cells[column];
    var currDay = i-startCell+2;
    //
    if (currDay>0 && currDay<=dayCount) {
      cell.innerHTML = currDay;
    }
    else {
      cell.innerHTML = "&nbsp;";
    }

    if (currDay==day) {
      cell.className = "selected";
    }
    else {
      if (column<5) {
        cell.className = "";
      }
      else {
        cell.className = "weekend";
      }
    }

    //
    column++;
    if (column>6) {
      column = 0;
      row++;
    }

    // paint date label
    this.owner.rows[9].cells[0].innerHTML = this.getCurrentLabel();
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseOverMonth = function(event,idx) {
  if (this.date.getMonth()!=idx) {
    var e = new MouseEvent(event);
    e.source.className = "over";
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseOutMonth = function(event,idx) {
  if (this.date.getMonth()!=idx) {
    var e = new MouseEvent(event);
    e.source.className = "";
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseClickMonth = function(event,idx) {
  if (this.date.getMonth()!=idx) {
    var e = new MouseEvent(event);
    this.setDateSafe(this.date.getDate(),idx,this.date.getFullYear());
    this.repaint();
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseOver = function(event,idx) {
  if (this.isCellInRange(idx)) {
    var startCell = this.getStartCell(this.date.getFullYear(),this.date.getMonth());
    var cellDay = idx-startCell+1;
    //
    if (this.date.getDate()!=cellDay) {
      var e = new MouseEvent(event);
      e.source.className = "over";
    }
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseOut = function(event,idx) {
  if (this.isCellInRange(idx)) {
    var startCell = this.getStartCell(this.date.getFullYear(),this.date.getMonth());
    var cellDay = idx-startCell+1;
    //
    if (this.date.getDate()!=cellDay) {
      var e = new MouseEvent(event);
      e.source.className = "";
    }
  }
};

/**
 *
 **************************************/
DateChooser.prototype.mouseClick = function(event,idx) {
  if (this.isCellInRange(idx)) {
    var e = new MouseEvent(event);
    //
    var startCell = this.getStartCell(this.date.getFullYear(),this.date.getMonth());
    var cellDay = idx-startCell+1;
    //
    this.setDateSafe(cellDay,this.date.getMonth(),this.date.getFullYear());

    // submit to server
    if (this.doEvents) {
      e.source.className = "clicked";
      var valId = this.owner.id+this.VALUE_ELEMENT_POSTFIX;
      var val = document.getElementById(valId).value;
      submitSelection(valId,(this.doEvents && !TextUtil.startsWith(val,this.EVENT_PREFIX) ? this.EVENT_PREFIX : "") + val);
    }
    else {
      this.repaint();
    }
  }
};


/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function getDateChooser(ownerId) {
  var owner = document.getElementById(ownerId);
  if (owner && !owner.dateObj) {
    owner.dateObj = new DateChooser(owner);
  }
  //
  return owner.dateObj;
}


/******************************************************************************/
/* WebListbox                                                                 */
/******************************************************************************/

var ID_VALUE_POSTFIX = "_val_";

/**
 *
 **************************************/
function getIdValueElement(elId) {
  return document.getElementById(elId+ID_VALUE_POSTFIX);
}

/**
 *
 **************************************/
function getIdValueCount(elId) {
  var value = getIdValueElement(elId).value;
  if (value.length==0) {
    return 0;
  }
  //
  return value.split(',').length;
}

/**
 *
 **************************************/
function processLBMouseOver(e,index) {
  var event = new MouseEvent(e);
  var className = event.source.className;
  if (!TextUtil.endsWith(className,"_over")) {
    addElementOver(event.source);

    // disable text selection
    document.body.onselectstart = returnFalse;
    event.source.style.MozUserSelect="none";
  }
}

/**
 *
 **************************************/
function processLBMouseOut(e,index) {
  var event = new MouseEvent(e);
  var className = event.source.className;
  //
  removeElementOver(event.source);

  // disable text selection
  document.body.onselectstart = returnTrue;
  event.source.style.MozUserSelect="";
}

/**
 *
 **************************************/
function isSelectedLBIndex(lbId,index) {
  var values = ','+getIdValueElement(lbId).value+',';
  //
  return values.indexOf(','+index+',')>=0;
}

/**
 *
 **************************************/
function clearLBSelections(element) {
  var child = element.firstChild;
  removeElementOver(child);
  removeElementSelection(child);
  //
  while (child.nextSibling) {
    child = child.nextSibling;
    removeElementOver(child);
    removeElementSelection(child);
  }
}

/**
 *
 **************************************/
function clearLBSelection(lbId) {
  getIdValueElement(lbId).value = "";
}

/**
 *
 **************************************/
function setLBSelection(lbId,index) {
  getIdValueElement(lbId).value = index.toString();
}

/**
 *
 **************************************/
function addLBSelection(lbId,index) {
  var value = getIdValueElement(lbId).value;
  //
  if (value=="") {
    getIdValueElement(lbId).value = index.toString();
  }
  else {
    getIdValueElement(lbId).value += ","+index;
  }
}

/**
 *
 **************************************/
function removeLBSelection(lbId,index) {
  if (isSelectedLBIndex(lbId,index)) {
    var valElement = getIdValueElement(lbId);
    var values = ','+valElement.value+',';
    var value = ','+index+',';
    if (values.indexOf(value)>=0) {
      values = values.replace(value,',');
      values = TextUtil.cropStringStart(values,',');
      values = TextUtil.cropStringEnd(values,',');
      //
      valElement.value = values;
    }
  }
}

/**
 *
 **************************************/
function processLBMouseClick(e,index,multiple) {
  var event = new MouseEvent(e);
  var element = event.source;
  //
  var lbElement = element.parentNode.parentNode;
  if (!lbElement.lastClickedIdx) {
    lbElement.lastClickedIdx = 0;
  }
  var lbId = lbElement.id;

  // ctrl
  if (multiple && event.keyCTRL) {
    lbElement.lastClickedIdx = index;

    //
    if (isSelectedLBIndex(lbId,index)) {
      removeLBSelection(lbId,index);
      //
      removeElementOver(element);
      removeElementSelection(element);
    }
    else {
      addLBSelection(lbId,index);
      //
      removeElementOver(element);
      addElementSelection(element);
      addElementOver(element);
    }
  }

  // shift
  else if (multiple && event.keySHIFT) {
    clearLBSelections(element.parentNode);
    getIdValueElement(lbId).value = "";

    //
    var firstIndex = 0;
    if (lbElement.lastClickedIdx) {
      firstIndex = lbElement.lastClickedIdx;
    }
    var lastIndex = index;

    // swap indices
    if (lastIndex<firstIndex) {
      var tmp = firstIndex;
      firstIndex = lastIndex;
      lastIndex = tmp;
    }

    // select all in range
    var itemList = element.parentNode.childNodes;
    //
    for (var i=firstIndex; i<=lastIndex; i++) {
      addLBSelection(lbId,i);
      //
      var currElement = itemList[i];
      removeElementOver(currElement);
      addElementSelection(currElement);
      if (i==index) {
        addElementOver(currElement);
      }
    }

  }

  // plain click
  else {
    lbElement.lastClickedIdx = index;

    //
    clearLBSelections(element.parentNode);
    setLBSelection(lbId,index);
    //
    addElementSelection(element);
    addElementOver(element);
  }
}

/******************************************************************************/
/* WebGrid - advanced                                                         */
/******************************************************************************/

var lastTimeDrag = 0;
var DRAG_RES_MILLIS = 20; // this prevents resize choking i.e. Internet Explorer will render slow when resizing table columns too often

var GRID_MIN_WIDTH = 40;
var gridSX = 0;
var gridSW = 0;
var gridDragSource = null;
var gridDragIndex = 0;
var gridLastOver = null;

var gridEnableOverHeader = true;

var gridKeysEnabled = true;

/**
 *
 **************************************/
function gridReinitialize() {
  lastTimeDrag = 0;

  GRID_MIN_WIDTH = 30;
  gridSX = 0;
  gridSW = 0;
  gridDragSource = null;
  gridDragIndex = 0;
  gridLastOver = null;

  gridEnableOverHeader = true;
  gridKeysEnabled = true;
}

// --- SCROLL ----------------------------------

/**
 *
 **************************************/
function gridDoScroll(e) {
  var event = new MouseEvent(e);
  var divElement = event.source;
  var gridId = TextUtil.cropStringEnd(divElement.id,"_innr_");
  //var scrollElement = document.getElementById(gridId+"_scrl_");

  //
  saveScrollInfo(gridId+"_scrl_",divElement.scrollLeft,divElement.scrollTop);
}

// --- HEADERS ----------------------------------

/**
 *
 **************************************/
function gridDoClickHeader(e, index, colId) {
  var event = new MouseEvent(e);

  //
  submitSelection(gridFindGridId(event.source)+"_sort",colId);
}

/**
 *
 **************************************/
function gridDoOverHeader(e, processHeader) {
  var event = new MouseEvent(e);
  var headerCell = event.source;

  if (gridEnableOverHeader) {
    if (processHeader) {
      addElementOver(headerCell);
      addElementOver(headerCell.nextSibling);
    }

    //
    tooltipDoOver(e,headerCell.innerHTML);
  }

  // disable text selection
  document.body.onselectstart = returnFalse;
  headerCell.style.MozUserSelect="none";
}

/**
 *
 **************************************/
function gridDoOutHeader(e, processHeader) {
  var event = new MouseEvent(e);
  var headerCell = event.source;

  if (gridEnableOverHeader) {
    if (processHeader) {
      removeElementOver(headerCell);
      removeElementOver(headerCell.nextSibling);
    }
  }

  //
  tooltipDoOut(e);

  // enable text selection
  document.body.onselectstart = returnTrue;
  headerCell.style.MozUserSelect="";
}

/**
 *
 **************************************/
function gridDoMoveHeader(e, processHeader) {
  if (gridEnableOverHeader) {
    var event = new MouseEvent(e);
    //
    tooltipDoMove(e);
  }
}

/**
 *
 **************************************/
function gridStartDrag(e, index, submitAfterDrag) {
  gridEnableOverHeader = false;

  //
  var event = new MouseEvent(e);
  gridSX = event.x;
  gridDragSource = event.source;

  //
  resetComponentFocus();

  //
  if (submitAfterDrag) {
    gridDragSource.submitAfterDrag = submitAfterDrag;
  }
  gridDragIndex = index;
  gridSW = gridDragSource.previousSibling.previousSibling.offsetWidth;

  //
  MouseEvent.addEventListener(document,"mousemove",gridDoDrag,false);
  MouseEvent.addEventListener(document,"mouseup",gridStopDrag,false);
  //
  document.body.ondrag = returnFalse;
  document.body.onselectstart = returnFalse;
  document.onmousedown = returnFalse;

  //
  return false;
}

/**
 *
 **************************************/
function gridStopDrag(e) {
  gridEnableOverHeader = true;

  //
  var event = new MouseEvent(e);

  //
  MouseEvent.removeEventListener(document,"mousemove",gridDoDrag,false);
  MouseEvent.removeEventListener(document,"mouseup",gridStopDrag,false);
  //
  document.body.ondrag = returnTrue;
  document.body.onselectstart = returnTrue;
  document.onmousedown = returnTrue;
  //
  event.consume();

  //
  if (gridDragSource.submitAfterDrag) {
    submit();
  }

  //
  gridDragSource = null;

  //
  return false;
}

/**
 * gridId - the id of the outer div
 * index - column index
 * width - new column width
 **************************************/
function gridSaveColumnWidth(gridId, index, width) {
  var wElement = document.getElementById(gridId+"_col_w");
  var wVal = wElement.value;

  //
  var len = wVal.length;
  var cutStart = 0;
  var cutEnd = 0;
  var i = index;
  //
  while (i>0) {
    var cutStart = wVal.indexOf(",",cutStart+1);
    i--;
  }
  //
  cutEnd = wVal.indexOf(",",cutStart+1);

  // last number
  if (cutEnd==-1) {
    // and first number
    if (cutStart==0) {
      wElement.value = width.toString();
    }
    // just last number
    else {
      wElement.value = wVal.substring(0,cutStart+1) + width;
    }
  }
  // first number
  else if (cutStart==0) {
    wElement.value = width + wVal.substring(cutEnd,len+1);
  }
  // middle number
  else {
    wElement.value = wVal.substring(0,cutStart+1) + width + wVal.substring(cutEnd,len+1);
  }
}

/**
 *
 **************************************/
function gridGetStatusbarHeaders(guid) {
  return document.getElementById(guid+"_sb_hdrs_");
}

/**
 *
 **************************************/
function gridResizeColumn(e) {
  var event = new MouseEvent(e);
  var newW = gridSW + event.x - gridSX;
  if (newW<GRID_MIN_WIDTH) {
    newW = GRID_MIN_WIDTH;
  }
  var newStyleW = newW+"px";

  //
  var guid = gridFindGridId(gridDragSource);

  //
  var cell2resize = gridDragSource.previousSibling.previousSibling;

  //
  var style = cell2resize.style;
  var currentWidth = style.width;
  if (currentWidth!=newStyleW) {
    style.width = newStyleW;

    // TODO - adjust the statusbar as well
    var sbHeaders = gridGetStatusbarHeaders(guid);
    if (sbHeaders) {
      var cellIndex = cell2resize.cellIndex;
      var sbCell = sbHeaders.childNodes[cellIndex];
      if (sbCell) {
        sbCell.style.width = newStyleW;
      }
    }
  }

  // save new size
  gridSaveColumnWidth(guid, gridDragIndex, newW);
}

/**
 *
 **************************************/
function gridDoDrag(e) {
  if (lastTimeDrag==0) {
    gridResizeColumn(e);
    lastTimeDrag = (new Date()).getTime();
  }
  else {
    var elapsed = (new Date()).getTime() - lastTimeDrag;
    if (elapsed>DRAG_RES_MILLIS) { // skip some events rather than calling resize to often
      gridResizeColumn(e);
      lastTimeDrag = (new Date()).getTime();
    }
  }
}

/**
 *
 **************************************/
function gridFindCellElement(cellCandidate) {
  var cell = cellCandidate;
  var isWebGridAncestor = false;

  //
  /* REMOVEME - old stuff
  while (cell.tagName.toUpperCase()!='TD' && cell.tagName.toUpperCase()!='TH') {
    cell = cell.parentNode;
  }
  */

  //
  while (!isWebGridAncestor) {
    while (cell.tagName.toUpperCase()!='TD' && cell.tagName.toUpperCase()!='TH') {
      cell = cell.parentNode;
    }
    if (cell) {
      var tbodyId = cell.parentNode.parentNode.id;
      isWebGridAncestor = (tbodyId && TextUtil.endsWith(tbodyId,"_WGMarker_"));
      if (!isWebGridAncestor) {
        cell = cell.parentNode;
      }
    }
    else {
      break;
    }
  }

  //
  return cell;
}

/**
 *
 **************************************/
function gridFindGridElement(element) {
  return gridFindCellElement(element).parentNode.parentNode.parentNode.parentNode.parentNode;
}

/**
 *
 **************************************/
function gridFindGridTableElement(cellElement) {
  return cellElement.parentNode.parentNode.parentNode;
}

/**
 *
 **************************************/
function gridFindGridId(cellElement) {
  return gridFindGridElement(cellElement).id;
}


// --- SELECTIONS ----------------------------------


/**
 *
 **************************************/
function gridIsSelectedId(gridId, selId) {
  var values = ','+getIdValueElement(gridId).value+',';
  //
  return values.indexOf(','+selId+',')>=0;
}

/**
 *
 **************************************/
function gridClearSelections(table) {
  var rows = table.rows;
  var len = rows.length;

  //
  for (var i=0; i<len; i++) {
    removeElementOver(rows[i]);
    removeElementSelection(rows[i]);
  }
}

/**
 *
 **************************************/
function gridClearSelection(gridId) {
  getIdValueElement(gridId).value = "";
}

/**
 *
 **************************************/
function gridSetSelection(gridId, selId) {
  getIdValueElement(gridId).value = selId;
}

/**
 *
 **************************************/
function gridAddSelection(gridId, selId) {
  var value = getIdValueElement(gridId).value;
  //
  if (value=="") {
    getIdValueElement(gridId).value = selId;
  }
  else {
    getIdValueElement(gridId).value += ","+selId;
  }
}

/**
 *
 **************************************/
function gridRemoveSelection(gridId, selId) {
  if (gridIsSelectedId(gridId, selId)) {
    var valElement = getIdValueElement(gridId);
    var values = ','+valElement.value+',';
    var value = ','+selId+',';
    if (values.indexOf(value)>=0) {
      values = values.replace(value,',');
      values = TextUtil.cropStringStart(values,',');
      values = TextUtil.cropStringEnd(values,',');
      //
      valElement.value = values;
    }
  }
}

/**
 * Returns an appropriate value or -1 if no label element found
 **************************************/
function getGridSelCountLabel(gridId) {
  var label = document.getElementById(gridId+"_l_sel_c_");
  if (label) {
    return parseInt(label.innerHTML);
  }

  //
  return -1;
}

/**
 * Sets the given selection count if an appropriate value container found.
 **************************************/
function setGridSelCountLabel(gridId, count) {
  var label = document.getElementById(gridId+"_l_sel_c_");
  if (label) {
    label.innerHTML = count;
  }
}

/**
 *
 **************************************/
function gridDoMouseDownClient(e, multiple, startIndex, endIndex, idList, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  var cellElement = gridFindCellElement(event.source);

  //
  var gridElement = gridFindGridElement(cellElement);
  var gridTableElement = gridFindGridTableElement(cellElement);

  //
  var gridId = gridElement.id;
  var selCount = getGridSelCountLabel(gridId); // user-visible selected row count
  var rowElement = cellElement.parentNode;
  var tableElement = rowElement.parentNode.parentNode;

  //
  var index = rowElement.rowIndex;
  if (index>=endIndex || index==ignoreIndex) {
    return;
  }

  //
  var selId = idList[index];

  //
  if (!gridTableElement.lastClickedIdx) {
    gridTableElement.lastClickedIdx = startIndex;
  }

  // ctrl
  if (multiple && event.keyCTRL) {
    gridTableElement.lastClickedIdx = index;

    //
    if (gridIsSelectedId(gridId,selId)) {
      gridRemoveSelection(gridId,selId);
      setGridSelCountLabel(gridId,getGridSelCountLabel(gridId)-1);
      //
      removeElementOver(rowElement);
      removeElementSelection(rowElement);
    }
    else {
      gridAddSelection(gridId,selId);
      setGridSelCountLabel(gridId,getGridSelCountLabel(gridId)+1);
      //
      removeElementOver(rowElement);
      addElementSelection(rowElement);
      addElementOver(rowElement);
    }
  }

  // shift
  else if (multiple && event.keySHIFT) {
    var valElement = getIdValueElement(gridId);
    var selCount = getIdValueCount(gridId);

    //
    gridClearSelections(tableElement);
    valElement.value = "";

    //
    var firstIndex = startIndex;
    if (gridTableElement.lastClickedIdx) {
      firstIndex = gridTableElement.lastClickedIdx;
    }
    var lastIndex = index;

    // check index order

    // asc
    if (firstIndex<=lastIndex) {
      // select all in range
      var rowList = tableElement.rows;
      //
      for (var i=firstIndex; i<=lastIndex; i++) {
        gridAddSelection(gridId,idList[i]);
        //
        var currElement = rowList[i];
        removeElementOver(currElement);
        addElementSelection(currElement);
        if (i==index) {
          addElementOver(currElement);
        }
      }
    }

    // desc
    else {
      // select all in range
      var rowList = tableElement.rows;
      //
      for (var i=firstIndex; i>=lastIndex; i--) {
        gridAddSelection(gridId,idList[i]);
        //
        var currElement = rowList[i];
        removeElementOver(currElement);
        addElementSelection(currElement);
        if (i==index) {
          addElementOver(currElement);
        }
      }
    }

    //
    var oldCount = getGridSelCountLabel(gridId);
    if (firstIndex<lastIndex) {
      setGridSelCountLabel(gridId,oldCount - selCount + lastIndex - firstIndex + 1);
    }
    else {
      setGridSelCountLabel(gridId,oldCount - selCount + firstIndex - lastIndex + 1);
    }
  }

  // plain click
  else {
    gridTableElement.lastClickedIdx = index;

    //
    gridClearSelections(tableElement);
    gridSetSelection(gridId,selId);
    //
    addElementSelection(rowElement);
    addElementOver(rowElement);

    // save reset info (possible other selections on server must be reset)
    document.getElementById(gridId+"_rst_").value="1";
    //
    setGridSelCountLabel(gridId,1);
  }

}

/**
 *
 **************************************/
function gridDoStartEdit(e, gridId, rowId) {
  var value = "0," + rowId + ",0";

  //
  submitSelection(gridId+"_val_",value);
}

/**
 *
 **************************************/
function gridDoMouseDownServer(e, multiple, startIndex, endIndex, idList, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  var cellElement = gridFindCellElement(event.source);
  var gridElement = gridFindGridElement(cellElement);
  var gridId = gridElement.id;
  var rowElement = cellElement.parentNode;
  var tableElement = rowElement.parentNode.parentNode;

  //
  var index = rowElement.rowIndex;
  if (index>=endIndex || index==ignoreIndex) {
    return;
  }

  // prepare the value to submit
  var value = null;

  // ctrl: 0 = no CTRL, 1 = CTRL
  if (event.keyCTRL) {
    value = "1," + idList[index];
  }
  else {
    value = "0," + idList[index] + "," + cellElement.cellIndex;
  }

  // painted selections
  removeElementOver(rowElement);
  removeElementSelection(rowElement);
  addElementClicked(rowElement);

  //
  var tbodyElement = rowElement.parentNode;

  // wait for possible double click
  if (tbodyElement.ondblclick) {
    mouseDoDoubleClickDelay(
        function() {
          submitSelection(gridId+"_val_",value);
        }
    );
  }

  // no double click
  else {
    submitSelection(gridId+"_val_",value);
  }
}

/**
 *
 **************************************/
function gridDoMouseDown(e, multiple, startIndex, endIndex, doSubmit, idList, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  var src = event.source;
  if (src.tagName.toUpperCase()=="A") {
    return true;
  }

  //
  if (doSubmit) {
    gridDoMouseDownServer(e, multiple, startIndex, endIndex, idList, ignoreIndex);
  }
  else {
    gridDoMouseDownClient(e, multiple, startIndex, endIndex, idList, ignoreIndex);
  }
}

// --- LINK BUTTON ---------------------------

/**
 *
 **************************************/
function gridDoLinkButtonDown(e,rowId, colId) {
  var event = new MouseEvent(e);

  //
  var cellElement = gridFindCellElement(event.source);
  var gridElement = gridFindGridElement(cellElement);
  var gridId = gridElement.id;

  //
  var value = rowId + "," + colId;
  forceSubmitSelectionOnce(gridId+"_act_",value);

  //
  event.consume();
  return false;
}

// --- ROWS ----------------------------------

/**
 *
 **************************************/
function gridDoOver(e, endIndex, doSelect, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  if (event.source.tagName.toUpperCase()=="A") {
    return true;
  }

  //
  var thisCell = gridFindCellElement(event.source);
  gridLastOver = thisCell;
  var parentRow = thisCell.parentNode;
  var gridId = gridFindGridId(thisCell);

  //
  var index = parentRow.rowIndex;
  if (index>=endIndex || index==ignoreIndex) {
    return;
  }

  //
  if (doSelect) {
    addElementOver(parentRow);
    var numBar = document.getElementById(gridId+"_num_tbl_");
    if (numBar) {
      addElementOver(numBar.rows[parentRow.rowIndex]);
    }

    //
    var favBar = document.getElementById(gridId+"_fav_tbl_");
    if (favBar) {
      addElementOver(favBar.rows[parentRow.rowIndex]);
    }
  }

  //
  tooltipDoOver(e,thisCell.innerHTML);

  // disable text selection
  document.body.onselectstart = returnFalse;
  event.source.style.MozUserSelect="none";
}

/**
 *
 **************************************/
function gridDoMove(e, endIndex, doSelect, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  if (event.source.tagName.toUpperCase()=="A") {
    return true;
  }

  //
  var thisCell = gridFindCellElement(event.source);
  var parentRow = thisCell.parentNode;

  //
  var index = parentRow.rowIndex;
  if (index>=endIndex || index==ignoreIndex) {
    return;
  }

  //
  if (!gridLastOver || gridLastOver!=thisCell) { // IE workaround
    gridLastOver = thisCell;
    gridDoOver(e,endIndex,doSelect);
  }
  else {
    tooltipDoMove(e);
  }
}

/**
 *
 **************************************/
function gridDoOut(e, endIndex, doSelect, ignoreIndex) {
  var event = new MouseEvent(e);

  //
  var thisCell = gridFindCellElement(event.source);
  gridLastOver = null;
  var parentRow = thisCell.parentNode;
  var gridId = gridFindGridId(thisCell);

  //
  var index = parentRow.rowIndex;
  if (index>=endIndex || index==ignoreIndex) {
    return;
  }

  //
  if (doSelect) {
    removeElementOver(parentRow);
    var numBar = document.getElementById(gridId+"_num_tbl_");
    if (numBar) {
      removeElementOver(numBar.rows[parentRow.rowIndex]);
    }

    //
    var favBar = document.getElementById(gridId+"_fav_tbl_");
    if (favBar) {
      removeElementOver(favBar.rows[parentRow.rowIndex]);
    }
  }

  //
  tooltipDoOut(e);

  // enable text selection
  document.body.onselectstart = returnTrue;
  event.source.style.MozUserSelect="";
}

// --- KEYS ----------------------------------

/**
 * @param keyModifiers see method KeyEvent.prototype.getAllModifiers()
 **************************************/
function gridRowEditSubmitConfirm(gridId,keyModifiers) {
  gridKeysEnabled = false;

  //
  if (keyModifiers) setTimeout("forceSubmitSelectionOnce('"+gridId+"'+'_edt_','1,"+keyModifiers+"');",60);
  else setTimeout("forceSubmitSelectionOnce('"+gridId+"'+'_edt_','1');",60);
}

/**
 * @param keyModifiers see method KeyEvent.prototype.getAllModifiers()
 **************************************/
function gridRowEditSubmitCancel(gridId,keyModifiers) {
  gridKeysEnabled = false;

  //
  if (keyModifiers) setTimeout("forceSubmitSelectionOnce('"+gridId+"'+'_edt_','0,"+keyModifiers+"');",60);
  else setTimeout("forceSubmitSelectionOnce('"+gridId+"'+'_edt_','0');",60);
}

/**
 * @param eventInfo: {id,event}
 **************************************/
function gridRowEditCommitKeyHandler(eventInfo) {
  // global
  if (!gridKeysEnabled) {
    return;
  }

  //
  var keyEvent = eventInfo.event;

  //
  if (keyEvent.src.tagName.toUpperCase()=="TEXTAREA" && keyEvent.keyNum!=27) {
    return true;
  }

  // confirm edited data
  if (keyEvent.keyNum==13) {
    keyEvent.consume();
    block_all_keys = true;
    gridRowEditSubmitConfirm(eventInfo.id,keyEvent.getAllModifiers());
    return false;
  }

  //
  return true;
}

/**
 * @param eventInfo: {id,event}
 **************************************/
function gridRowEditCancelKeyHandler(eventInfo) {
  //doDebug(eventInfo.id);
  // global
  if (!gridKeysEnabled) {
    return;
  }

  //
  var keyEvent = eventInfo.event;

  //
  if (keyEvent.src.tagName.toUpperCase()=="TEXTAREA" && keyEvent.keyNum!=27) {
    return true;
  }

  // cancel edited data
  if (keyEvent.keyNum==27) {
    keyEvent.consume();
    block_all_keys = true;
    gridRowEditSubmitCancel(eventInfo.id,keyEvent.getAllModifiers());
    return false;
  }

  //
  return true;
}

/**
 * @param keyModifiers see method KeyEvent.prototype.getAllModifiers()
 **************************************/
function gridRowAddSubmitConfirm(gridId,keyModifiers) {
  if (keyModifiers) forceSubmitSelectionOnce(gridId+"_a_dd_","1,"+keyModifiers);
  else forceSubmitSelectionOnce(gridId+"_a_dd_","1");
}

/**
 * @param keyModifiers see method KeyEvent.prototype.getAllModifiers()
 **************************************/
function gridRowAddSubmitCancel(gridId,keyModifiers) {
  if (keyModifiers) forceSubmitSelectionOnce(gridId+"_a_dd_","0,"+keyModifiers);
  else forceSubmitSelectionOnce(gridId+"_a_dd_","0");
}

/**
 * @param eventInfo: {id,event}
 **************************************/
function gridRowAddKeyHandler(eventInfo) {
  //doDebug(eventInfo.id);
  var keyEvent = eventInfo.event;

  //
  if (keyEvent.src.tagName.toUpperCase()=="TEXTAREA" && keyEvent.keyNum!=27) {
    return true;
  }

  // confirm added data
  if (keyEvent.keyNum==13) {
    gridRowAddSubmitConfirm(eventInfo.id,keyEvent.getAllModifiers());
    keyEvent.consume();
    return false;
  }

  // cancel edited data
  else if (keyEvent.keyNum==27) {
    gridRowAddSubmitCancel(eventInfo.id,keyEvent.getAllModifiers());
    keyEvent.consume();
    return false;
  }

  //
  return true;
}

/**
 * @param eventInfo: {id,event}
 **************************************/
function gridRowAddKeyFilter(eventInfo) {
  //doDebug(eventInfo.id);

  //
  if (eventInfo.event.src.tagName.toUpperCase()=="TEXTAREA" && eventInfo.event.keyNum!=27) {
    return true;
  }

  // confirm added data
  if (eventInfo.event.keyNum==13) {
    eventInfo.event.consume();
    return false;
  }

  // cancel edited data
  else if (eventInfo.event.keyNum==27) {
    eventInfo.event.consume();
    return false;
  }

  //
  return true;
}

// --- FAVORITES ----------------------------------

/**
 * @param eventInfo: {id,event}
 **************************************/
function gridDoSubmitFavorite(guidWithPostfix,id) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  favoriteButtonDoCancelTooltip();

  //
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(guidWithPostfix,id);
  setFormWillUpdate(false);
  form2self();
}

/**
 *
 **************************************/
function gridFinishFavoriteUpdate() {
  hideLoading();
  setEnabledEvents(true);
}

/******************************************************************************/
/* favorite                                                                   */
/******************************************************************************/

var FAVORITE_BUTTON_TOOLTIP_DELAY = 600;
var favoriteButtonGUID = null;
var favoriteButtonTooltipSubmitGUID = null;
var favoriteButtonTooltipSubmitValue = null;
var favoriteButtonLocation = {x:0, y:0};
var favoriteButtonDelay = null;
var favoriteButtonLoadingTooltip = false;
var favoriteButtonTooltip = null;

/**
 *
 **************************************/
function favoriteButtonDoMouseIn(event, guid, submitGuid, submitValue) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var ev = new MouseEvent(event);

  //
  if (favoriteButtonDelay) {
    favoriteButtonDelay.stop();
  }

  //
  favoriteButtonGUID = guid;
  //
  favoriteButtonTooltipSubmitGUID = submitGuid ? submitGuid : null;
  favoriteButtonTooltipSubmitValue = submitValue ? submitValue : "1";
  //
  favoriteButtonLocation.x = ev.x;
  favoriteButtonLocation.y = ev.y;
  //
  favoriteButtonDelay = new Delay(
    FAVORITE_BUTTON_TOOLTIP_DELAY,
    function() {favoriteButtonDoLoadTooltip();}
  );
  favoriteButtonDelay.start();
}

/**
 *
 **************************************/
function favoriteButtonDoMouseOut(event, guid) {
  favoriteButtonDoCancelTooltip();
}

/**
 *
 **************************************/
function favoriteButtonDoLoadTooltip() {
  if (!isEnabledEvents()) {
    return;
  }

  //
  favoriteButtonLoadingTooltip = true;

  //
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(favoriteButtonTooltipSubmitGUID,favoriteButtonTooltipSubmitValue);
  setFormWillUpdate(false);
  form2self();
}

/**
 *
 **************************************/
function favoriteButtonShowTooltip(html) {
  if (!favoriteButtonGUID) {
    return; // this means that we've leaved this button
  }

 //
 if (!favoriteButtonTooltip) {
   favoriteButtonTooltip = new Tooltip("_fav__button__tooltip_");
 }

//
 favoriteButtonTooltip.MAX_WIDTH = getWindowSize[0]-10;
 favoriteButtonTooltip.setContentHTML(html);
 favoriteButtonTooltip.move(favoriteButtonLocation.x,favoriteButtonLocation.y);
 favoriteButtonTooltip.show();
}

/**
 *
 **************************************/
function favoriteButtonDoCancelTooltip() {
  // stop a possible pending request
  if (favoriteButtonGUID) {
    if (favoriteButtonDelay) {
      favoriteButtonDelay.stop();
    }
    favoriteButtonGUID = null;
    favoriteButtonTooltipSubmitGUID = null;
  }

  // hide tooltip
  if (favoriteButtonTooltip) {
    favoriteButtonTooltip.hide();
  }

  //
  favoriteButtonLoadingTooltip = false;
}

/**
 *
 **************************************/
function favoriteButtonDoClick(guid) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  favoriteButtonDoCancelTooltip();

  //
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(guid,"1");
  setFormWillUpdate(false);
  form2self();
}

/**
 *
 **************************************/
function favoriteButtonDoFinishUpdate() {
  hideLoading();
  setEnabledEvents(true);
}

/******************************************************************************/
/* Menu                                                                       */
/******************************************************************************/

var MENU_DELAY = 300;

var menuZIndex = 1;
var menuTimerShow = null;
var menuTimerHide = null;
var menuLastRowToShow = null;

/**
 *
 **************************************/
function menuStartUpdate(guid) {
  if (!isEnabledEvents()) {
    return true;
  }

  //
  setEnabledEvents(false);

  //
  form2frame();
  setFormWillUpdate(true);
  document.getElementById(guid+"_opn").value="1";
  document.appform.submit();
  setFormWillUpdate(false);
  form2self();
}

/**
 *
 **************************************/
function menuFinishUpdate(guid) {
  var floating = document.getElementById(guid+"_fltmenuroot");
  var dropDown = floating.firstChild;
  var button = document.getElementById(guid);

  //
  var pos = menuFindAbsolutePosition(button);
  var wSize = getWindowSize();
  var bh = button.offsetHeight;
  var w = dropDown.offsetWidth;
  var h = dropDown.offsetHeight;

  //
  var left = pos.left;
  if (left<0) {
    left = 0;
  }
  if (left+w+2>wSize[0]) {
    left -= left+w+2-wSize[0];
  }

  //
  var top = pos.top+bh-1;
  if (top+h+2>wSize[1]) {
    top = pos.top-h;
  }

  //
  dropDown.style.left = left+"px";
  dropDown.style.top = top+"px";

  //
  document.getElementById(guid+"_opn").value="0";

  // change button style
  var buttonRow = document.getElementById(guid).rows[0];
  removeElementOver(buttonRow);
  addElementSelection(buttonRow);

  //
  setEnabledEvents(true);
}

/**
 *
 **************************************/
function doMenuClose(buttonGUID) {
  // remove button's selection
  removeElementSelection(document.getElementById(buttonGUID).rows[0]);

  // remove floating root
  var element = document.getElementById(buttonGUID+"_fltmenuroot");
  element.parentNode.parentNode.removeChild(element.parentNode);
}

/**
 *
 **************************************/
function processMenuClose(ev,buttonGUID) {
  var e = new MouseEvent(ev);
  var element = e.source;

  //
  if (element.tagName.toUpperCase()=="DIV") {
    doMenuClose(buttonGUID);
  }
}

/**
 *
 **************************************/
function menuFindParentOfRow(row) {
  return row.parentNode.parentNode.parentNode;
}

/**
 *
 **************************************/
function processMenuNodeEnter(row,submenuId) {
  addElementOver(row);

  //
  var image = row.cells[2].firstChild;
  if (image && image.src) {
    image.src = TextUtil.cropStringEnd(image.src,".gif") + "_o.gif";
  }

  //
  showSubmenu(row,submenuId);
}

/**
 *
 **************************************/
function processMenuItemEnter(row,submenuId) {
  addElementOver(row);

  //
  var image = row.cells[2].firstChild;
  if (image && image.src) {
    image.src = TextUtil.cropStringEnd(image.src,".gif") + "_o.gif";
  }

  //
  hideSubmenuForRow(row);
}

/**
 *
 **************************************/
function processMenuExit(row) {
  removeElementOver(row);

  //
  var image = row.cells[2].firstChild;
  if (image && image.src) {
    var imgUrl = TextUtil.cropStringEnd(image.src,".gif");
    imgUrl = TextUtil.cropStringEnd(imgUrl,"_o");
    image.src = imgUrl+".gif";
  }
}

/**
 *
 **************************************/
function menuSetLocation(parentItem,submenu) {
  var pPos = findAbsolutePosition(parentItem);
  var pW = parentItem.offsetWidth;
  var pH = parentItem.offsetHeight;

  //
  var wSize = getWindowSize();
  var w = submenu.offsetWidth;
  var h = submenu.offsetHeight;

  //
  var left = pPos.left + pW - 2;
  var top = pPos.top;
  if (left+w>wSize[0]) {
    left = pPos.left - w + 3;
  }
  if (left<0) {
    left = 0;
  }
  if (top+h>wSize[1]) {
    top = pPos.top - h + pH;
  }
  if (top<0) {
    top = 0;
  }

  //
  if (h>wSize[1]) {
    submenu.style.height = wSize[1]+"px";
    submenu.style.width = (w+20)+"px";
    submenu.style.overflow = "auto";
  }

  //
  submenu.style.left = left+"px";
  submenu.style.top = top+"px";
}

/**
 *
 **************************************/
function doHideSubmenu(submenuId) {
  var menu = document.getElementById(submenuId);

  //
  if (menu.openSubmenuId) {
    var submenu = document.getElementById(menu.openSubmenuId);

    //
    if (submenu.openSubmenuId) {
      doHideSubmenu(submenu.id);
    }

    //
    submenu.style.display = "none";
    submenu.style.visibility = "hidden";
    submenu.style.width = null;

    //
    menu.openSubmenuId = null;
  }

  //
  if (menuTimerHide) {
    clearTimeout(menuTimerHide);
    menuTimerHide = null;
  }
}

/**
 * after timer
 **************************************/
function hideSubmenu(submenuId) {
  //
  if (menuTimerShow) {
    clearTimeout(menuTimerShow);
    menuTimerShow = null;
  }

  //
  if (menuTimerHide) {
    clearTimeout(menuTimerHide);
    menuTimerHide = null;
  }

  //
  menuTimerHide = setTimeout("doHideSubmenu('"+submenuId+"');",MENU_DELAY);
}

/**
 *
 **************************************/
function hideSubmenuForRow(row) {
  hideSubmenu(menuFindParentOfRow(row).id);
}

/**
 *
 **************************************/
function doHideSubmenuForRow(row) {
  doHideSubmenu(menuFindParentOfRow(row).id);
}

/**
 *
 **************************************/
function doShowSubmenu(submenuId) {
  if (menuLastRowToShow) {
    doHideSubmenuForRow(menuLastRowToShow);

    //
    var submenu = document.getElementById(submenuId);

    //
    submenu.style.display = "";
    menuSetLocation(menuLastRowToShow,submenu);
    submenu.style.zIndex = (++menuZIndex);
    submenu.style.visibility = "visible";

    //
    menuFindParentOfRow(menuLastRowToShow).openSubmenuId = submenuId;

    //
    menuLastRowToShow = null;
    menuTimerShow = null;
  }
}

/**
 * after timer
 **************************************/
function showSubmenu(row,submenuId) {
  menuLastRowToShow = row;

  //
  if (menuTimerShow) {
    clearTimeout(menuTimerShow);
    menuTimerShow = null;
  }

  //
  if (menuTimerHide) {
    clearTimeout(menuTimerHide);
    menuTimerHide = null;
  }

  //
  menuTimerShow = setTimeout("doShowSubmenu('"+submenuId+"');",MENU_DELAY);
}

/**
 *
 **************************************/
function processMenuNodeClick(row,submenuId) {
  menuLastRowToShow = row;

  //
  doShowSubmenu(submenuId);
}

/**
 *
 **************************************/
function processMenuItemClick(row,buttonGUID,actionCommand) {
  //
  var guid = buttonGUID+"_evt";

  //
  var eventInput = document.getElementById(guid);
  if (!eventInput) {
    eventInput = document.createElement("input");
    eventInput.type = "hidden";
    eventInput.id = guid;
    eventInput.name = guid;
    document.appform.appendChild(eventInput);
  }

  //
  submitSelection(guid,actionCommand);

  //
  if (fullAjax) {
    //
    if (menuTimerShow) {
      clearTimeout(menuTimerShow);
      menuTimerShow = null;
    }

    //
    if (menuTimerHide) {
      clearTimeout(menuTimerHide);
      menuTimerHide = null;
    }

    //
    menuLastRowToShow = null;

    //
    document.appform.removeChild(eventInput);
    doMenuClose(buttonGUID);
  }
}

/**
 *
 **************************************/
function menuFindAbsolutePosition(obj) {
 /*
 var curleft = 0;
 var curtop = 0;

 //
 var theObj = obj;

 //
 if (theObj.offsetParent) {
   while (theObj.offsetParent) {
     if (theObj==obj) {
       curleft += theObj.offsetLeft;
       curtop += theObj.offsetTop;
     }
     else {
       curleft += theObj.offsetLeft - theObj.scrollLeft;
       curtop += theObj.offsetTop - theObj.scrollTop;
     }

     //
     theObj = theObj.offsetParent;
   }
 }

 //
 else {
   if (obj.x) curleft += obj.x;
   if (obj.y) curtop += obj.y;
 }

 //
 return {left:curleft,top:curtop};
 */
  return suggestFindAbsolutePosition(obj);
}

/******************************************************************************/
/* WebPanel - advanced                                                        */
/******************************************************************************/

var PANEL_ANIM_TICK = 15;
var panelAdvAlpha = null;

/**
 *
 **************************************/
function processPanelAdvClick(guid) {
  if (panelAdvAlpha!=null) {
    return;
  }

  //
  var el = document.getElementById(guid);
  var icon = el.firstChild.firstChild;

  // heights
  var visH = el.offsetHeight;
  var h = el.scrollHeight;
  var minH = el.firstChild.offsetHeight;

  // show content
  var content = document.getElementById(guid+"_cOnTeNt_");
  if (content) {
    content.style.visibility = "visible";
  }

  // collapse
  if (TextUtil.endsWith(icon.src,"_up_o.gif") || TextUtil.endsWith(icon.src,"_up.gif")) {
    //el.style.height = el.firstChild.offsetHeight + "px";
    doPanelAdvCollapse(guid,visH,minH);

    //
    removeImageOver(icon);
    addImageDown(icon);
    addImageOver(icon);

    //
    document.getElementById(guid+"_exp_").value="0";
  }

  // expand
  else {
    //el.style.height = h + "px";
    doPanelAdvCollapse(guid,minH,h);

    //
    removeImageOver(icon);
    addImageUp(icon);
    addImageOver(icon);

    //
    document.getElementById(guid+"_exp_").value="1";
  }
}

/**
 *
 **************************************/
function doPanelAdvCollapse(guid,startH,endH) {
  if (panelAdvAlpha==null) {
    panelAdvAlpha = new Alpha((new Date()).getTime(),200);
    setTimeout("doPanelAdvCollapse('"+guid+"',"+startH+","+endH+");",PANEL_ANIM_TICK);
  }

  else {
    var el = document.getElementById(guid);
    var val = panelAdvAlpha.getValue((new Date()).getTime());

    //
    if (val<1) {
      var amount = (startH - endH) * Math.sin(val*Math.PI/2);
      el.style.height = (startH - amount) + "px";

      //
      setTimeout("doPanelAdvCollapse('"+guid+"',"+startH+","+endH+");",PANEL_ANIM_TICK);
    }
    else {
      el.style.height = endH + "px";
      el.scrollTop = 0;
      panelAdvAlpha = null;

      // this was collapse so hide the content (disables component focus on hidden components)
      if (startH>endH) {
        var content = document.getElementById(guid+"_cOnTeNt_");
        if (content) {
          content.style.visibility = "hidden";
        }
      }

    }

  }
}

/******************************************************************************/
/* WebSuggestionTextField                                                     */
/******************************************************************************/

/**
 *
 **************************************/
function SuggestionItem(id,label,isAlert) {
  this.id = id;
  this.label = label;
  this.isAlert = isAlert;
}

/**
 *
 **************************************/
function SuggestionItemList() {
  this.list = [];
  this.selIndex = -1;
}

/**
*
**************************************/
SuggestionItemList.prototype.getItemCount = function() {
  return this.list.length;
};

/**
 *
 **************************************/
SuggestionItemList.prototype.addItem = function(itm) {
  this.list[this.list.length] = itm;
};

/**
 *
 **************************************/
SuggestionItemList.prototype.getItemAt = function(idx) {
  return this.list[idx];
};

/**
 * @return the currently selected item or null in case no such item found
 **************************************/
SuggestionItemList.prototype.getSelectedItem = function(idx) {
  return (this.selIndex>-1) ? this.list[this.selIndex] : null;
};

/**
 * Scrolls the parent div in case the item is not entirely visible.
 **************************************/
SuggestionItemList.prototype.focusSelectedItem = function() {
  if (this.selIndex>-1) {
    var el = document.getElementById("_suggest_list_");

    //
    if (el) {
      var table = el.firstChild;

      //
      if (table) {
        var GAP = 40;
        var row = table.rows[this.selIndex];

        //
        var rY = row.offsetTop;
        var rH = row.offsetHeight;
        var pH = el.offsetHeight;
        var pScroll = el.scrollTop;

        // TODO
        if (rY-GAP<pScroll) {
          el.scrollTop = rY - GAP;
        }
        else if (rY+rH+GAP>pScroll+pH) {
          el.scrollTop = rY + rH + GAP - pH;
        }
      }

    }
  }
};

/**
 *
 **************************************/
SuggestionItemList.prototype.selectItem = function(idx) {
  var el = document.getElementById("_suggest_list_");

  //doDebug("select index: "+idx);

  //
  if (el) {
    var table = el.firstChild;

    //
    if (table) {
      // deselect old selection if present
      if (this.selIndex>-1) {
        var row = table.rows[this.selIndex];
        removeElementSelection(row);
      }

      // select the new row and scroll to it if necessary
      var row = table.rows[idx];
      addElementSelection(row);
    }

  }

  //
  this.selIndex = idx;
};

/**
 *
 **************************************/
SuggestionItemList.prototype.deselectAllItems = function() {
  var el = document.getElementById("_suggest_list_");

  //
  if (el) {
    var table = el.firstChild;

    //
    if (table) {
      // deselect old selection if present
      if (this.selIndex>-1) {
        var row = table.rows[this.selIndex];
        removeElementSelection(row);
      }
    }

  }

  //
  this.selIndex = -1;
};

/**
 *
 **************************************/
SuggestionItemList.prototype.selectNextItem = function() {
  // something already selected
  if (this.selIndex>-1) {
    var i = this.selIndex + 1;
    var len = this.list.length;
    //var itm = null;

    // REMOVEME
    if (i<len) {
      this.selectItem(i);
    }
    else {
      this.selectItem(0);
    }

    /*
    // find next appropriate sibling
    while (i<len) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i++;
    }

    // find appropriate sibling from start
    i = 0;
    while (i<this.selIndex) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i++;
    }
    */
  }

  // nothing selected
  else {
    //var i = 0;
    //var len = this.list.length;
    //var itm = null;

    // REMOVEME
    this.selectItem(0);

    /*
    // find appropriate sibling from start
    while (i<len) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i++;
    }
    */
  }
};

/**
 *
 **************************************/
SuggestionItemList.prototype.selectPrevItem = function() {
  // something already selected
  if (this.selIndex>-1) {
    var i = this.selIndex - 1;
    var len = this.list.length;
    //var itm = null;

    // REMOVEME
    if (i>=0) {
      this.selectItem(i);
    }
    else {
      this.selectItem(len-1);
    }

    /*
    // find previous appropriate sibling
    while (i>=0) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i--;
    }

    // find appropriate sibling from end
    i = len - 1;
    while (i>this.selIndex) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i--;
    }
    */
  }

  // nothing selected
  else {
    var len = this.list.length;
    var i = len - 1;
    //var itm = null;

    // RMEOVEME
    this.selectItem(i);

    /*
    // find appropriate sibling from start
    while (i>=0) {
      itm = this.list[i];
      if (!itm.isAlert) {
        this.selectItem(i);
        return;
      }
      i--;
    }
    */
  }
};

/******************************************************************************/
/******************************************************************************/

var SUGGEST_SUBMIT_DELAY = 30;
var SUGGEST_CHECK_DELAY = 30;
var SUGGEST_POLL_DELAY = 10;
var SUGGEST_FILTER_GAP = 22;
var SUGGEST_FILTER_MIN_WIDTH = 200;

var suggestTextChanged = false;
var suggestComboPreparingList = false; // if true, then text-change event handler should wait until list is prepared
var suggestIsWaiting = false;
var suggestLastGUID = null; // this will be set to null
var suggestGUID = null; // this is never set to null;
var suggestItemList = null;
var suggestMouseEnabled = true;
var suggestEnableMouseTimer = null;
var suggestIsCombo = false;
var suggestComboSearch = false; // if false, then make only default requests - don't allow token requests
var suggestSrcBox = null; // suggestion list rectangle

var suggestPollerTimer = null; // combo filter poller
var suggestComboLastValue = null;
var suggestComboFireEvent = false;
var suggestComboSubmitOnCommit = false; // always submit a combo change

/**
 *
 **************************************/
function suggestDoLinkButton(ev) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  var event = new MouseEvent(ev);
  var src = event.source;

  //
  var guid = src.parentNode.firstChild.id;

  //
  forceSubmitSelectionOnce(guid+"_sElLink_","1");
}

/**
 *
 **************************************/
function suggestDoClearButton(ev,doSubmit) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  var event = new MouseEvent(ev);
  var src = event.source;

  //
  var guid = src.parentNode.firstChild.id;

  //
  if (doSubmit) {
    forceSubmitSelectionOnce(guid+"_sElId_","[$cLr$]");
  }
  else {
    prepareSubmitParameter(guid+"_sElId_","[$cLr$]"); // store for later post
    src.previousSibling.value = "";
    addBackImageDisabled(src);
  }
}

/**
 *
 **************************************/
function suggestDisableMouse() {
  if (suggestEnableMouseTimer) {
    clearTimeout(suggestEnableMouseTimer);
  }
  suggestEnableMouseTimer = setTimeout("suggestEnableMouse()",100);

  //
  suggestMouseEnabled = false;
}

/**
 *
 **************************************/
function suggestEnableMouse() {
  if (suggestEnableMouseTimer) {
    clearTimeout(suggestEnableMouseTimer);
    suggestEnableMouseTimer = null;
  }

  //
  suggestMouseEnabled = true;
}


/**
 *
 **************************************/
function suggestDoMouseOverItem(ev) {
  if (!suggestMouseEnabled) {
    return true;
  }

  //
  var event = new MouseEvent(ev);
  var src = event.source;
  var row = src;

  //
  while (row.tagName.toUpperCase()!="TR") {
    row = row.parentNode;
  }

  //
  suggestItemList.deselectAllItems();
  var index = row.rowIndex;
  if (!suggestItemList.getItemAt(index).isAlert) {
    suggestItemList.selectItem(index);
  }
}

/**
 *
 **************************************/
function suggestDoMouseOutItem(ev) {
  if (!suggestMouseEnabled) {
    return true;
  }
  if (suggestIsShowing()) {
    suggestItemList.deselectAllItems();
  }
}

/**
 * Commits the currently selected item or does nothing if there is no such
 * item or the item represents an alert.
 **************************************/
function suggestCommitSelection() {
  var guid = suggestGUID;

  //
  if (suggestIsShowing()) {
    // combo
    if (suggestIsCombo) {
      var sItem = suggestItemList.getSelectedItem();
      if (sItem && !sItem.isAlert) {
        var el = document.getElementById(guid);
        if (el) {
          el.value = sItem.label;

          //
          suggestHideAll();
          el.focus();
          setCaretPosition(el,0);
          storeComponentFocus(suggestGUID);
          // doTextComponentFocus(el); - doesn't work in IE

          // store the id and fire an event to the server if necessary!
          if (suggestComboFireEvent) {
            forceSubmitSelectionOnce(guid+"_sElId_",sItem.id);
          }
          else {
            prepareSubmitParameter(guid+"_sElId_",sItem.id); // store for later post
            if (suggestComboSubmitOnCommit) {
              submit();
            }
            else {
              // visually enable clear button if available (the clear button
              // checks the suggestion's value so there are no other means to enable it but visually)
              if (el.nextSibling) {
                removeBackImageDisabled(el.nextSibling);
              }
            }

          }
        }
      }
    }

    // text field
    else {
      var sItem = suggestItemList.getSelectedItem();
      if (sItem && !sItem.isAlert) {
        var el = document.getElementById(guid);
        if (el) {
          //
          el.value = sItem.label;
          suggestHideAll();
          doTextComponentFocus(el);

          // Store for later post, which may occur with the text event below.
          // The user may change the text before this value gets submitted to 
          // the server. It is upon the server to determine if the sent text 
          // corresponds to the sent item id. If not, the server may ignore 
          // (drop) the given id.
          prepareSubmitParameter(guid+"_sElId_",sItem.id); 
          
          //
          if (el._isTxtEv) {
            suggestEnableBlurEvents(guid);
            fireTextComponentEvent(el);
          }
        }
      }
    }

  }// if (suggestIsShowing())
}

/**
 *
 **************************************/
function suggestDoMouseClick(guid,doCombo,fireEvent,submitCommit,doComboSearch) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  suggestIsCombo = doCombo;
  suggestComboFireEvent = fireEvent;
  suggestComboSubmitOnCommit = submitCommit;
  suggestComboSearch = doComboSearch;

  //
  var src = document.getElementById(guid);
  var val = src.value;

  //
  if (suggestLastGUID!=src.id) {
    if (suggestLastGUID) {
      forceCancelUpdate();
      suggestForceFinishUpdate(suggestLastGUID);
    }
    suggestLastGUID = src.id;
    suggestGUID = suggestLastGUID;
  }

  //
  //if ((TextUtil.trimString(val)=="" || doCombo) && !suggestIsWaiting) {
  //if ((TextUtil.trimString(val)=="" || suggestTextChanged || doCombo) && !suggestIsWaiting) { // do request only if text is empty or it has changed or is a combo
  if (!suggestIsWaiting) {
    var pos = suggestFindAbsolutePosition(src);
    if (pos.left<0) {
      pos.left = 0;
    }

    //
    suggestSrcBox = {x:pos.left, y:pos.top, w:src.offsetWidth, h:src.offsetHeight};
    suggestPrepareList(val);

    // START POLLER
    if (doCombo && doComboSearch) {
      suggestComboStartFilterPoller();
    }

    //
    var style = src.style;
    style.backgroundPosition = "top right";
    style.backgroundRepeat = "no-repeat";
    style.backgroundImage = "url(/public/_common_look/ios/resource/images/suggest/loading.gif)";
  }

  //
  return true;
}

/**
 * Handles control keys (down key event) such as up, down, end, home, etc.
 * @return {processedKey: boolean, returnValue: boolean}
 **************************************/
function suggestHandleControlKeysDown(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  // pre filter
  if (
    key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  ) {
    return {processedKey: true, returnValue: true};
  }

  // handle list selection
  if (suggestIsShowing()) {
    if (key==KeyEvent.KEY_DOWN) {
      suggestDisableMouse();
      suggestItemList.selectNextItem();
      suggestItemList.focusSelectedItem();

      //
      event.consume();
      return {processedKey: true, returnValue: false};
    }

    else if (key==KeyEvent.KEY_UP) {
      suggestDisableMouse();
      suggestItemList.selectPrevItem();
      suggestItemList.focusSelectedItem();

      //
      event.consume();
      return {processedKey: true, returnValue: false};
    }

    else if (key==KeyEvent.KEY_ENTER || key==KeyEvent.KEY_ESC) {
      event.consume();
      return {processedKey: true, returnValue: false};
    }
  }

  //
  /*
  else if (key==KeyEvent.KEY_DOWN) {
    return {processedKey: true, returnValue: true};
  }
  */

  // post filter
  if (
    key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_ESC
  ) {
    return {processedKey: true, returnValue: true};
  }

  //
  return {processedKey: false, returnValue: true};
}

/**
 * Handles control keys (key up event) such as up, down, end, home, etc.
 * @return {processedKey: boolean, returnValue: boolean}
 **************************************/
function suggestHandleControlKeysUp(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  //doDebug("key up,  suggestIsShowing="+suggestIsShowing()+", suggestTextChanged="+suggestTextChanged);

  // pre filter
  if (
    key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
    || key==KeyEvent.KEY_UP
  ) {
    return {processedKey: true, returnValue: true};
  }

  // handle list selection
  if (suggestIsShowing()) {
    if (key==KeyEvent.KEY_ENTER) {
      suggestCommitSelection();

      //
      event.consume();
      return {processedKey: true, returnValue: false};
    }

    else if (key==KeyEvent.KEY_ESC) {
      suggestHideAll();
      suggestLastGUID = null;
      suggestIsWaiting = false;

      //
      event.consume();
      return {processedKey: true, returnValue: false};
    }
  }

  // no list visible
  else if (key==KeyEvent.KEY_DOWN) {
    // make a request
    suggestDoTextRequest(ev);

    //
    event.consume();
    return {processedKey: true, returnValue: false};
  }

  // post filter
  if (
    key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_ESC
  ) {
    return {processedKey: true, returnValue: true};
  }

  //
  return {processedKey: false, returnValue: true};
}

/**
 * Filters control keys such as up, down, end, home, etc.
 * @return {processedKey: boolean, returnValue: boolean}
 **************************************/
function suggestFilterControlKeys(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  // pre filter
  if (
    key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  ) {
    return {processedKey: true, returnValue: true};
  }

  // handle list selection
  if (suggestIsShowing()) {
    if (
      key==KeyEvent.KEY_UP
      || key==KeyEvent.KEY_DOWN
      || key==KeyEvent.KEY_ENTER
      || key==KeyEvent.KEY_ESC
    )
    {
      return {processedKey: false, returnValue: true};
    }
  }

  // post filter
  if (
    key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_ESC
  ) {
    return {processedKey: true, returnValue: true};
  }

  //
  return {processedKey: false, returnValue: true};
}

/**
 *
 **************************************/
function suggestDoTextChange(ev,doCombo,fireEvent,submitCommit,doComboSearch) {
  if (!isEnabledEvents()) {
    return false;
  }
  //if (suggestIsWaiting) return false;

  //
  suggestIsCombo = doCombo;
  suggestComboFireEvent = fireEvent;
  suggestComboSubmitOnCommit = submitCommit;
  suggestComboSearch = doComboSearch;

  //
  if (doCombo) {
    if (suggestComboPreparingList) {
      return false;
    }
    //
    suggestComboPreparingList = true;
  }

  // prepare event
  var event = new GenericEvent(ev);
  var src = event.src;
  var srcId = src.id;

  // stop a possible previous update
  if (suggestLastGUID!=srcId) {
    if (suggestLastGUID) {
      forceCancelUpdate();
      suggestForceFinishUpdate(suggestLastGUID);
    }
    suggestLastGUID = srcId;
    suggestGUID = suggestLastGUID;
  }

  //
  suggestTextChanged = true;

  // NOT A COMBO - just show the change
  if (!doCombo) {
    var style = src.style;
    style.backgroundPosition = "top right";
    style.backgroundRepeat = "no-repeat";

    //
    style.backgroundImage = "url(/public/_common_look/ios/resource/icons/suggestion/sugg_key_down_anim.gif)";
    //style.backgroundImage = "url(/public/_common_look/ios/resource/images/suggest/loading.gif)";

    //
    if (suggestIsShowing()) {
      suggestHideAll();
    }
  }

  // COMBO - open the search field, start the poller and make a default request
  else {
    var pos = suggestFindAbsolutePosition(src);
    if (pos.left<0) {
      pos.left = 0;
    }
    suggestSrcBox = {x:pos.left, y:pos.top, w:src.offsetWidth, h:src.offsetHeight};

    //
    suggestPrepareList(src.value);

    //
    if (doComboSearch) {
      suggestComboStartFilterPoller();
    }
  }

  //
  return true;
}

/**
 * Sugg. text field - user made a request pressing the down button
 **************************************/
function suggestDoTextRequest(ev) {
  if (!isEnabledEvents()) {
    return false;
  }
  if (!suggestTextChanged) {
    return false;
  }

  //
  suggestIsCombo = false;
  suggestComboFireEvent = false;
  suggestComboSubmitOnCommit = false;

  // prepare event
  var event = new GenericEvent(ev);
  var src = event.src;
  var srcId = src.id;

  // stop a possible previous update
  if (suggestLastGUID!=srcId) {
    if (suggestLastGUID) {
      forceCancelUpdate();
      suggestForceFinishUpdate(suggestLastGUID);
    }
    suggestLastGUID = srcId;
    suggestGUID = suggestLastGUID;
  }

  //
  suggestHideAll();

  //
  var style = src.style;
  style.backgroundPosition = "top right";
  style.backgroundRepeat = "no-repeat";
  style.backgroundImage = "url(/public/_common_look/ios/resource/images/suggest/loading.gif)";


  //
  var pos = suggestFindAbsolutePosition(src);
  if (pos.left<0) {
    pos.left = 0;
  }
  suggestSrcBox = {x:pos.left, y:pos.top, w:src.offsetWidth, h:src.offsetHeight};

  //
  suggestPrepareList(src.value);

  //
  return true;
}

/**
 *
 **************************************/
function suggestLostFocus(ev) {
  var event = new GenericEvent(ev);
  
  //
  //window.alert("suggestLostFocus");

  // only when focus lost and no other suggest gained focus
  if (!suggestIsCombo && event.src.id==suggestLastGUID) {
    suggestHideAll();
    suggestForceFinishUpdate(suggestLastGUID);
  }
}

/**
 * 
 **************************************/
function suggestDisableBlurEventsOnce(guid) {
  document.getElementById(guid)._disableFocusEventsOnce=true;
}

/**
 * 
 **************************************/
function suggestEnableBlurEvents(guid) {
  document.getElementById(guid)._disableFocusEventsOnce=false;
}

/**
 *
 **************************************/
function suggestIsShowing() {
  return !suggestIsWaiting && suggestItemList!=null;
}

/**
 *
 **************************************/
function suggestIsListItemSelected() {
  return suggestIsShowing() && suggestItemList.getSelectedItem()!=null && !suggestItemList.getSelectedItem().isAlert;
}

/**
 *
 **************************************/
function suggestCheckClose(ev) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  var event = new GenericEvent(ev);
  var src = event.src;
  if (src.id && src.id=="_suggest_outer_") {
    suggestHideAll();
    suggestLastGUID = null;
    suggestIsWaiting = false;
  }
}

/**
 *
 **************************************/
function suggestPrepareList(text) {

  var sx = suggestSrcBox.x;
  var sy = suggestSrcBox.y;
  var sw = suggestSrcBox.w;
  if (sw<SUGGEST_FILTER_MIN_WIDTH) {
    sw = SUGGEST_FILTER_MIN_WIDTH;
  }
  var sh = suggestSrcBox.h;

  //
  var guid = suggestGUID;
  var wPos = getWindowScroll();
  var wSize = getWindowSize();
  wSize[0] -= 20;
  wSize[1] -= 20;

  //
  var div = document.getElementById("_suggest_outer_");

  // outer div
  if (!div) {
    div = document.createElement("div");
    div.id = "_suggest_outer_";
    div.style.cssText = "z-index:900000000;background-image:url('/public/_common_look/px.gif');position:absolute; left:"+wPos[0]+"px;top:"+wPos[1]+"px;width:"+(wSize[0]-4)+"px;height:"+(wSize[1]-4)+"px";
    injectEvent(div,"onclick","suggestCheckClose(event);");
    //
    document.appform.appendChild(div);
  }

  //
  if (!suggestIsCombo) {
    div.style.display = 'none'; // FIXME
  }

  // inner div
  var listDiv = document.getElementById("_suggest_list_");
  if (!listDiv) {
    listDiv = document.createElement("div");
    listDiv.id = "_suggest_list_";
    listDiv.className = "gui_sugg_tf";
    div.appendChild(listDiv);
  }
  //
  listDiv.style.cssText = "left:"+sx+"px;top:"+(sy+sh)+"px;";

  //
  if (suggestIsCombo) {
    listDiv.style.display = 'none';
  }

  // combo
  var filterInput = document.getElementById("_suggest_filter_input_");
  if (suggestIsCombo) {
    if (suggestComboSearch) {
      if (!filterInput) {
        filterInput = document.createElement("input");
        filterInput.type = "text";
        filterInput.id = "_suggest_filter_input_";
        filterInput.name = "_suggest_filter_input_";
        injectEvent(filterInput,"onfocus","suggestComboGainedFocus(event);");
        injectEvent(filterInput,"onblur","suggestComboLostFocus(event);");
        injectEvent(filterInput,"onkeydown","return !suggestComboFInputHandleControlKeysDown(event);");
        injectEvent(filterInput,"onkeyup","return !suggestComboFInputHandleControlKeysUp(event);");
        injectEvent(filterInput,"onkeypress","return !suggestComboFInputFilterControlKeys(event);");
        injectEvent(filterInput,"onmousedown","suggestComboFilterInputMouseDown(event);");
        div.appendChild(filterInput);
      }
      //
      filterInput.className = "gui_sugg_filter_loading";
      filterInput.style.display = "inline";
      filterInput.style.left = sx + "px";
      filterInput.style.top = (sy + sh) + "px";
      filterInput.style.width = sw + "px";
      filterInput.style.backgroundImage = "";
      //filterInput.style.backgroundImage = "url(/public/_common_look/ios/resource/images/suggest/loading.gif)";

      //
      div.style.display = 'block';

      //
      setTimeout(function() {filterInput.focus(); suggestComboPreparingList = false;}, 1);
    }

    //
    else {
      listDiv.tabIndex = 0;
      injectEvent(listDiv,"onkeydown","return !suggestComboFInputHandleControlKeysDown(event);");
      injectEvent(listDiv,"onkeyup","return !suggestComboFInputHandleControlKeysUp(event);");
      injectEvent(listDiv,"onkeypress","return !suggestComboFInputFilterControlKeys(event);");

      //
      suggestComboPreparingList = false;
    }
  }

  // text field
  else {
    if (filterInput) {
      filterInput.style.display = "none";
      filterInput.value = "";
    }
  }

  //
  suggestIsWaiting = true;
  suggestSubmit(guid);
}

/**
 *
 **************************************/
function suggestSubmit(guid) {
  suggestDoSubmit(guid);
}

/**
 *
 **************************************/
function suggestDoSubmitFavorite(guid,id) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  favoriteButtonDoCancelTooltip();

  //
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(guid+"_UpDtP_",id);
  setFormWillUpdate(false);
  form2self();
}

/**
 *
 **************************************/
function suggestDoSubmit(guid) {
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(guid+"_UpDtP_","list");
  setFormWillUpdate(false);
  form2self();

  // REMOVED: as we wan't to wait for the response before submitting again
  // enable events here as we don't want to wait for the response in order to be able to submit again
  //hideLoading();
  //setEnabledEvents(true);

  //
  suggestTextChanged = false;
}

/**
 *
 **************************************/
function suggestDoUpdateList(html,w,h) {
  var outer = document.getElementById("_suggest_outer_");
  if (outer) {
    var inner = document.getElementById("_suggest_list_");
    if (inner) {
      var style = inner.style;
      style.visibility = "hidden";
      style.display = "block";
      inner.innerHTML = html;
      outer.style.display = "block";

      //
      style.width = "";
      style.height = "";
      style.visibility = "";

      //
      var box = suggestDoCalculateBox(inner);
      style.left = box.x + "px";
      style.top = box.y + "px";
      style.width = box.w + "px";
      style.height = box.h + "px";
      style.visibility = "visible";

      // combo
      if (suggestIsCombo) {
        var filterInput = document.getElementById("_suggest_filter_input_");
        if (filterInput) {
          // pull down menu FIXME - if and else are the same, remove if/else
          if (box.isDown) {
            filterInput.style.left = box.x + "px";
            filterInput.style.top = box.y + "px";
          }
          // pull up menu
          else {
            filterInput.style.left = box.x + "px";
            filterInput.style.top = box.y + "px";
          }

          //
          filterInput.style.width = (box.w - SUGGEST_FILTER_GAP) + "px";
          filterInput.className = "gui_sugg_filter";
        }
      }

      // text field
      else {
        var filterInput = document.getElementById("_suggest_filter_input_");
        if (filterInput) {
          filterInput.parentNode.removeChild(filterInput);
        }
      }

      //
      suggestDisableMouse();
    }
  }
}

/**
 *
 **************************************/
function suggestDoCalculateBox(el) {
  var xx = el.offsetLeft;
  var yy = el.offsetTop;
  var ww = el.offsetWidth + SUGGEST_FILTER_GAP;
  if (suggestSrcBox && ww<suggestSrcBox.w) {
    ww = suggestSrcBox.w;
  }
  var hh = el.offsetHeight;

  //
  var win = getWindowSize();
  var winW = win[0] - 20;
  var winH = win[1] - 20;
  //var winScroll = getWindowScroll();
  //var winSX = winScroll[0];
  //var winSY = winScroll[1];

  //
  var right = xx + ww;
  var bottom = yy + hh;
  var GAP = 10;
  var MIN_H = 100;
  var INPUT_HEIGHT = suggestSrcBox.h;

  // left
  if (right>winW-GAP) {
    xx -= right - winW - GAP;
    if (xx<GAP) {
      xx = GAP;
    }
    if (xx+ww>winW-GAP) {
      ww = winW - GAP - xx;
    }
  }

  //
  var down = true;

  // top and height
  if (bottom>winH-GAP) {
    // just shrink the height
    if (winH-GAP-yy>=MIN_H) {
      hh = winH - GAP - yy;
    }
    // try to move the entire list up
    else if (yy-GAP>=MIN_H){
      if (hh>yy-GAP) {
        hh = yy - GAP - INPUT_HEIGHT;
      }
      yy -= INPUT_HEIGHT + hh;
      down = false;
    }
    // no room - stick with the original plan
    else {
      hh = winH - GAP - yy;
    }
  }

  //
  return {x:xx, y:yy, w:ww, h:hh, isDown:down};
}

/**
 *
 **************************************/
function suggestHideAll() {
  // try STOP POLLER
  suggestComboStopFilterPoller();
  forceCancelUpdate();

  //
  var div = document.getElementById("_suggest_outer_");
  if (div) {
    //div.innerHTML = '';
    var inner = document.getElementById("_suggest_list_");
    if (inner) {
      inner.scrollTop = 0;
      div.style.display='none';
    }

    //
    var filterInput = document.getElementById("_suggest_filter_input_");
    if (filterInput) {
      filterInput.style.display = 'none';
      filterInput.className = "gui_sugg_filter";
      filterInput.value = '';
    }
  }

  //
  suggestItemList = null;
}

/**
 *
 **************************************/
function suggestHideList() {
  var div = document.getElementById("_suggest_outer_");
  if (div) {
    //div.innerHTML = '';
    var inner = document.getElementById("_suggest_list_");
    if (inner) {
      inner.scrollTop = 0;
      inner.style.display='none';
    }

    //
    var filterInput = document.getElementById("_suggest_filter_input_");
    if (filterInput) {
      filterInput.className = "gui_sugg_filter";
    }
  }

  //
  suggestItemList = null;
}

/**
 *
 **************************************/
function suggestFinishUpdate(guid) {
  //var src = document.getElementById(guid);

  /*
  // something changed while updating the list - repeat the query
  if (suggestTextChanged) {
    var inner = document.getElementById("_suggest_list_");
    if (inner) {
      inner.innerHTML = "";
      // combo hide list
      if (suggestIsCombo) {
        suggestHideList();
        var filterInput = document.getElementById("_suggest_filter_input_");
        if (filterInput) {
          filterInput.className = "gui_sugg_filter_loading";
        }
      }
      // text-field hide all
      else {
        suggestHideAll();
      }
    }

    // repeat the query
    suggestSubmit(guid);
  }
  */

  //
  if (!suggestTextChanged) {
    suggestForceFinishUpdate(guid);
  }
  else {
    suggestHideList();
    suggestLastGUID = null;
    suggestIsWaiting = false;
  }

}

/**
 *
 **************************************/
function suggestForceFinishUpdate(guid) {
  // finish update
  var src = document.getElementById(guid);
  src.style.backgroundImage = "";
  suggestLastGUID = null;
  suggestIsWaiting = false;

  //
  if (suggestIsCombo) {
    // search field
    if (suggestComboSearch) {
      var filterInput = document.getElementById("_suggest_filter_input_");
      if (filterInput) {
        filterInput.style.backgroundImage = "";
        filterInput.className = "gui_sugg_filter";
        setTimeout(function(){filterInput.focus();},1);
      }
    }

    // list only
    else {
      // we have a valid list
      if (suggestItemList && suggestItemList.getItemCount()>0) {
        var list = document.getElementById("_suggest_list_");
        if (list) {
          setTimeout(function(){list.focus();},1);
        }
      }

      // we don't have a list or it is empty
      else {
        suggestHideList();
      }
    }
  }

  //
  hideLoading();
  setEnabledEvents(true);
}

/**
 *
 **************************************/
function suggestFinishFavoriteUpdate() {
  hideLoading();
  setEnabledEvents(true);
}

/**
 *
 **************************************/
function suggestFindAbsolutePosition(obj) {
 var curleft = 0;
 var curtop = 0;

 //
 var theObj = obj;

 //
 if (theObj.offsetParent) {
   while (theObj.offsetParent) {

     if (theObj==obj) {
       curleft += theObj.offsetLeft;
       curtop += theObj.offsetTop;
     }
     else {
       curleft += theObj.offsetLeft - theObj.scrollLeft;
       curtop += theObj.offsetTop - theObj.scrollTop;
     }

     //
     /* */
     while (theObj.parentNode!=theObj.offsetParent) {
       theObj=theObj.parentNode;
       curleft -= theObj.scrollLeft;
       curtop -= theObj.scrollTop;
     }
     /* */

     //
     theObj = theObj.offsetParent;
   }
 }

 //
 else {
   if (obj.x) {
     curleft += obj.x;
   }
   if (obj.y) {
     curtop += obj.y;
   }
 }

 //
 return {left:curleft,top:curtop};
}


/******************************************************************************/
/******************************************************************************/

/**
 * Handles control keys such as up, down, end, home, etc.
 * @return true if this handler accepted and processed the key
 **************************************/
function suggestComboHandleControlKeys(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  // pre filter
  if (
    key==KeyEvent.KEY_TAB
    || key==KeyEvent.KEY_ESC
    || key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  )
  {
    return true;
  }

  //
  event.consume();
  return false;
}

/**
 * Filters control keys such as up, down, end, home, etc.
 * @return true if this handler stopped the key
 **************************************/
function suggestComboFilterControlKeys(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  //
  var doFilter = (
    key==KeyEvent.KEY_TAB
    || key==KeyEvent.KEY_ESC
    || key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_UP
    || key==KeyEvent.KEY_DOWN
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  );

  //
  return doFilter;
}

/**
 * Handles control keys such as up, down, end, home, etc.
 * @return true if this handler accepted and processed the key
 **************************************/
function suggestComboFInputHandleControlKeysDown(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  //
  if (!isEnabledEvents()) {
    event.consume();
    return true;
  }

  // pre filter
  if (
    key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  ) {
    return false;
  }

  //
  if (key==KeyEvent.KEY_TAB) {
    suggestHideAll();
    var el = document.getElementById(suggestGUID);
    el.focus();
    setCaretPosition(el,0);
  }

  // handle list selection
  if (suggestIsShowing()) {
    if (key==KeyEvent.KEY_DOWN) {
      suggestDisableMouse();
      suggestItemList.selectNextItem();
      suggestItemList.focusSelectedItem();
    }

    else if (key==KeyEvent.KEY_UP) {
      suggestDisableMouse();
      suggestItemList.selectPrevItem();
      suggestItemList.focusSelectedItem();
    }
  }

  // post filter
  if (
    key==KeyEvent.KEY_TAB
    || key==KeyEvent.KEY_ESC
    || key==KeyEvent.KEY_UP
    || key==KeyEvent.KEY_DOWN
    || key==KeyEvent.KEY_ENTER // consume anyway
  )
  {
    event.consume();
    return true;
  }

  //
  return false;
}

/**
 * Filters control keys such as up, down, end, home, etc.
 * @return true if this handler stopped the key
 **************************************/
function suggestComboFInputHandleControlKeysUp(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  //
  if (!isEnabledEvents()) {
    event.consume();
    return true;
  }

  //
  if (key==KeyEvent.KEY_ESC) {
    var el = document.getElementById(suggestGUID);
    el.focus();
    setCaretPosition(el,0);

    //
    suggestHideAll();
    suggestLastGUID = null;
    suggestIsWaiting = false;
  }

  // handle list selection
  if (suggestIsListItemSelected()) {
    if (key==KeyEvent.KEY_ENTER) {
      suggestCommitSelection();
    }
  }

  // no item selected and text changed
  else if (suggestTextChanged) {
    if (key==KeyEvent.KEY_DOWN || key==KeyEvent.KEY_ENTER) {
      var filterInput = document.getElementById("_suggest_filter_input_");
      suggestPrepareList(filterInput.value);
    }
  }

  //
  var doFilter = (
    key==KeyEvent.KEY_ESC
    || key==KeyEvent.KEY_ENTER
    || key==KeyEvent.KEY_LEFT
    || key==KeyEvent.KEY_RIGHT
    || key==KeyEvent.KEY_UP
    || key==KeyEvent.KEY_DOWN
    || key==KeyEvent.KEY_HOME
    || key==KeyEvent.KEY_END
    || key==KeyEvent.KEY_SHIFT
    || key==KeyEvent.KEY_CTRL
    || key==KeyEvent.KEY_ALT
    || key==KeyEvent.KEY_PGUP
    || key==KeyEvent.KEY_PGDOWN
  );

  //
  if (doFilter) {
    event.consume();
  }
  return doFilter;
}

/**
 *
 **************************************/
function suggestComboFilterInputMouseDown(ev) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  if (suggestTextChanged) {
    var filterInput = document.getElementById("_suggest_filter_input_");
    if (filterInput) {
      suggestPrepareList(filterInput.value);
    }
  }
}


/**
 * Filters control keys such as up, down, end, home, etc.
 * @return true if this handler stopped the key
 **************************************/
function suggestComboFInputFilterControlKeys(ev) {
  var event = new KeyEvent(ev);
  var key = event.keyNum;

  //
  if (!isEnabledEvents()) {
    event.consume();
    return true;
  }

  //
  var doFilter = (
    key==KeyEvent.KEY_ENTER
  );

  //
  if (doFilter) {
    event.consume();
  }
  return doFilter;
}

/**
 *
 **************************************/
function suggestComboStartFilterPoller() {
  if (suggestPollerTimer) {
    suggestComboStopFilterPoller();
  }

  //
  var filterInput = document.getElementById("_suggest_filter_input_");
  if (filterInput) {
    //doDebug("START POLLER");
    suggestPollerTimer = setTimeout('suggestComboPoll();',SUGGEST_POLL_DELAY);
    suggestComboLastValue = filterInput.value;
  }
}

/**
 *
 **************************************/
function suggestComboStopFilterPoller() {
  if (suggestPollerTimer) {
    //doDebug("STOP POLLER");
    suggestPollerTimer = null;
    suggestComboLastValue = null;
    clearTimeout(suggestPollerTimer);
  }
}

/**
 *
 **************************************/
function suggestComboPoll() {
  var filterInput = document.getElementById("_suggest_filter_input_");
  if (filterInput) {
    if (suggestPollerTimer) {
      var val = filterInput.value;

      //
      if (suggestComboLastValue!=val) {
        suggestComboDoTextChange(); // trigger text change
      }

      //
      suggestComboLastValue = val;
      suggestPollerTimer = setTimeout('suggestComboPoll();',SUGGEST_POLL_DELAY);
    }
  }
}

/**
 *
 **************************************/
function suggestComboGainedFocus(ev) {
  block_backspace = false;
}

/**
 *
 **************************************/
function suggestComboLostFocus(ev) {
  block_backspace = true;
}

/**
 *
 **************************************/
function suggestComboDoTextChange() {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  suggestLastGUID = suggestGUID;
  suggestTextChanged = true;

  //
  /*


  //
  if (!suggestIsWaiting) {
    var filterInput = document.getElementById("_suggest_filter_input_");
    suggestPrepareList(filterInput.value);
    filterInput.className = "gui_sugg_filter_loading";
  }
  */

  // reset filter input
  var filterInput = document.getElementById("_suggest_filter_input_");
  filterInput.style.backgroundImage = "url(/public/_common_look/ios/resource/icons/suggestion/sugg_key_down_anim.gif)";

  //
  suggestHideList();

  //
  var sx = suggestSrcBox.x;
  var sy = suggestSrcBox.y;
  var sw = suggestSrcBox.w;
  if (sw<SUGGEST_FILTER_MIN_WIDTH) {
    sw = SUGGEST_FILTER_MIN_WIDTH;
  }
  var sh = suggestSrcBox.h;

  //
  filterInput.style.left = sx + "px";
  filterInput.style.top = (sy + sh) + "px";
  filterInput.style.width = sw + "px";

  //
  return true;
}

/******************************************************************************/
/* WebClientDocumentEdit                                                      */
/******************************************************************************/

/**
 * @author VINP
 * @param urLRoot an url representing the root of all server modules
 **************************************/
function docClientEditStart(fileName, docName, urlGetDoc, urlWriteDoc, urlLogInfo, urlLogError, urlEventStart, urlEventDone, urlEventCancelled) {
  var applet = document.getElementById("_$doc_client_edit$_");
  if (!applet) {
    var div = document.createElement("div");
    div.id = "_$doc_client_edit$_";
    div.style.cssText = "position:absolute;left:0px;top:0px;width:1px;height:1px;overflow:hidden;";

    //
    var html = "<iframe id='_$doc_client_edit_frame$_' name='_$doc_client_edit_frame$_' style='border: 0px none;overflow:hidden;width:1px;height:1px;z-index:-1;' scrolling='no' frameborder='no' src='/public/_common_look/px.gif'></iframe>"
             + "<applet code='si.triport.apps.client_document_edit.gui.AppletDocEditListener' archive='/public/_common_look/ios/resource/applet/client_edit/client_edit.jar' width='1px' height='1px'>"
             + "<param name='DOC_NAME' value='"+docName+"'>"
             + "<param name='DOC_FILE_NAME' value='"+fileName+"'>"
             + "<param name='GET_DOCUMENT_URL' value='"+urlGetDoc+"'>"
             + "<param name='WRITE_DOCUMENT_URL' value='"+urlWriteDoc+"'>"
             + "<param name='LOG_CLIENT_INFO_URL' value='"+urlLogInfo+"'>"
             + "<param name='LOG_CLIENT_ERROR_URL' value='"+urlLogError+"'>"
             + "<param name='EVENT_TARGET' value='_$doc_client_edit_frame$_'>"
             + "<param name='EVENT_STARTED_URL' value='"+urlEventStart+"'>"
             + "<param name='EVENT_UPLOAD_DONE_URL' value='"+urlEventDone+"'>"
             + "<param name='EVENT_CANCELLED_URL' value='"+urlEventCancelled+"'>"
             + "<param name='BG_COLOR' value='#ffffff'>"
             + "</applet>";
    div.innerHTML = html;

    //
    document.body.appendChild(div);

    // DEVELOPMENT disable events ad wait 30 seconds until the applet notifies its start
    showLoading();

  }
}

/**
 *
 **************************************/
function docClientEditEvent(eventId, docId) {
  forceSubmitSelectionOnce("_$doc_client_event$_",eventId+","+docId);
}

/**
 *
 **************************************/
function docClientEditEventStarted(eventId, docId) {
  hideLoading();
}

/******************************************************************************/
/* WebClientPDFSign                                                           */
/******************************************************************************/

/**
 *
 **************************************/
function docClientPDFSignEvent(eventId,id) {
  //window.alert(eventId+"   "+id);
  var element = document.getElementById(id);
  if (element) {
    element.parentNode.removeChild(element);
  }

  //
  forceSubmitSelectionOnce(id+"_$pdf_sign_event$_",eventId);
}

/******************************************************************************/
/* WebArea.WebAreaActionContainer                                             */
/******************************************************************************/

var AREA_ACTIONS_CLOSE_DELAY = 500;
var AREA_ACTIONS_OPENING_INTERVAL = 400;

var areaActionsResizer = null;
var areaActionsLoaded = null;
var areaActionsLoading = false;
var areaActionsMouseIn = false;
var areaActionsWaitTimer = null;

/**
 *
 ***************************************/
function areaActionsWaitOut(guid) {
  if (areaActionsWaitTimer) {
    areaActionsCancelWaitOut(guid);
  }

  //
  areaActionsWaitTimer = setTimeout("areaActionsDoOut('"+guid+"');",AREA_ACTIONS_CLOSE_DELAY);
}

/**
 *
 **************************************/
function areaActionsCancelWaitOut(guid) {
  if (areaActionsWaitTimer) {
    clearTimeout(areaActionsWaitTimer);
    areaActionsWaitTimer = null;
  }
}

/**
 *
 **************************************/
function areaActionsDoOut(guid) {
  //if (areaActionsWaitTimer==null) return; // NEW
  areaActionsCollapseList(guid);

  //
  var button = document.getElementById(guid);
  if (button) {
    removeElementOver(button.rows[0].cells[1]);
  }

  //
  areaActionsCancelWaitOut(guid);
}

/**
 *
 **************************************/
function areaActionsExpandList(guid) {
  if (!areaActionsLoaded) {
    return;
  }
  if (areaActionsResizer!=null) {
    areaActionsResizer.stop(); // this also sets the resizer to null
  }

  //
  var button = document.getElementById(guid);
  var listOuter = document.getElementById(guid+"_lIsT_");
  listOuter.style.display = "block";
  listOuter.style.width = "";

  //
  var bWidth = button.offsetWidth;
  if (listOuter.offsetWidth<bWidth) {
    listOuter.style.width = (bWidth-2) + "px";

    //
    var table = listOuter.firstChild;
    if (table) {
      if (table.offsetWidth<bWidth) {
        table.style.width = (bWidth-2) + "px";
      }
    }
  }

  //
  areaActionsResizer = new Resizer(
    listOuter,
    'h',
    'open',
    AREA_ACTIONS_OPENING_INTERVAL
  );

  //
  areaActionsResizer.setResizeHandler(
    function() {
      listOuter.scrollTop = listOuter.scrollHeight;
    }
  );

  //
  areaActionsResizer.setStopHandler(
    function() {
      areaActionsResizer = null;
    }
  );

  //
  areaActionsResizer.start();
}

/**
 *
 **************************************/
function areaActionsCollapseList(guid) {
  if (!areaActionsLoaded) {
    return;
  }
  if (areaActionsResizer!=null) {
    areaActionsResizer.stop(); // this also sets the resizer to null
  }

  var listOuter = document.getElementById(guid+"_lIsT_");
  if (listOuter) {
    areaActionsResizer = new Resizer(
      listOuter,
      'h',
      'close',
      AREA_ACTIONS_OPENING_INTERVAL
    );

    //
    areaActionsResizer.setResizeHandler(
      function() {
        listOuter.scrollTop = listOuter.scrollHeight;
      }
    );

    //
    areaActionsResizer.setStopHandler(
      function() {
        if (areaActionsResizer && areaActionsResizer.isFinished()) {
          //areaActionsLoaded = false;
          areaActionsKillList(guid);
        }
        areaActionsResizer = null;
      }
    );

    //
    areaActionsResizer.start();
  }
}

/**
 * Mouse over action
 **************************************/
function areaActionsOver(event, guid) {
  areaActionsMouseIn = true;
  areaActionsCancelWaitOut(guid);

  //
  if (!isEnabledEvents() || areaActionsLoading) {
    return false;
  }

  //
  var e = new MouseEvent(event);
  addElementOver(e.source);

  //
  if (areaActionsLoaded) {
    // just expand actions
    areaActionsExpandList(guid);
  }
  else {
    // load actions
    areaActionsSubmitRequest(guid);
  }
}

/**
 * Mouse out action
 **************************************/
function areaActionsOut(event, guid) {
  areaActionsMouseIn = false;
  areaActionsWaitOut(guid);
}

/**
 * Mouse over list action
 **************************************/
function areaActionsListOver(event,guid) {
  areaActionsMouseIn = true;
  areaActionsCancelWaitOut(guid);
}

/**
 * Mouse over list action
 **************************************/
function areaActionsListOut(event,guid) {
  areaActionsMouseIn = false;
  areaActionsWaitOut(guid);
}

/**
 *
 **************************************/
function areaActionsKillList(guid) {
  var listOuter = document.getElementById(guid+"_lIsT_");

  //
  if (listOuter) {
    listOuter.style.height = "1px";
    listOuter.innerHTML = "";
    listOuter.style.display = "none";
  }

  //
  areaActionsLoading = false;
  areaActionsLoaded  = false;

  //
  areaActionsDoOut(guid);
}

/**
 * ROW click action
 **************************************/
function areaActionsRowClicked(event, guid, rowId) {
  var value = rowId;

  //
  var listOuter = document.getElementById(guid+"_lIsT_");

  //
  var aButton = document.getElementById(guid);
  if (aButton) {
    var bPos = menuFindAbsolutePosition(aButton);
    var listPos = menuFindAbsolutePosition(listOuter);

    //
    value += ',' + (bPos.left + parseInt(aButton.offsetWidth/2))
           + ',' + (bPos.top  + parseInt(aButton.offsetHeight/2))
           + ',' + (listPos.left + parseInt(listOuter.offsetWidth/2))
           + ',' + (listPos.top + parseInt(listOuter.offsetHeight/2));
  }

  //
  forceSubmitSelectionOnce(guid+"_vAl_",value);

  //
  if (listOuter) {
    areaActionsKillList(guid);
  }
  //
  //doDebug("submit: "+guid+"_vAl_="+value);
}

/**
 *
 **************************************/
function areaActionsSubmitRequest(guid) {
  areaActionsLoading = true;
  areaActionsLoaded  = false;

  //
  form2frame();
  setFormWillUpdate(true);
  forceSubmitSelectionOnce(guid+"_uPd_","1");
  setFormWillUpdate(false);
  form2self();

  //
  hideLoading();
  setEnabledEvents(true);
}

/**
 *
 **************************************/
function areaActionsUpdateFromServer(guid) {
  if (areaActionsMouseIn) {
    areaActionsLoading = false;
    areaActionsLoaded  = true;
    areaActionsExpandList(guid);
  }
  else {
    areaActionsKillList(guid);
  }
}

/**
 * ROW over action
 **************************************/
function areaActionsRowOver(event) {
  if (!isEnabledEvents()) {
    return false;
  }

  //
  var e = new MouseEvent(event);

  //
  var element = DOMUtil.findElementByTagName(e.source,"TR");
  if (element) {
    addElementOver(element);
  }

  //
  return true;
}

/**
 * ROW out action
 **************************************/
function areaActionsRowOut(event) {
  var e = new MouseEvent(event);

  //
  var element = DOMUtil.findElementByTagName(e.source,"TR");
  if (element) {
    removeElementOver(element);
  }

  //
  return true;
}

/******************************************************************************/
/* WebApplication - WebEcho                                                   */
/******************************************************************************/

var echoAlpha = null;
var echoTimer = null;
var ECHO_ANIM_TICK = 15;
var lastEchoId = 0;

/**
 * WebEcho: constructor
 *************************************/
function WebEcho() {
  this.echoList = [];
  this.totalTime = 0;
  this.html = '';
  this.id = (++lastEchoId);
}

/**
 * @param text - mandatory
 * @param iconUrl - may be null
 * @param time - mandatory
 *************************************/
WebEcho.prototype.addEcho = function(text,iconUrl,time) {
  this.totalTime += time;

  //
  this.html += "<tr><td>";
  if (iconUrl) {
    this.html += "<img src='"+iconUrl+"'>";
  }
  this.html += "</td><td>";

  //
  this.html += text;

  //
  this.html += "</td></tr>";
};

/**
 *
 *************************************/
WebEcho.prototype.show = function(doc) {
  var el = doc.getElementById("_app_echo_");
  if (el) {
    el.parentNode.removeChild(el);
  }

  //
  //
  el = doc.createElement("div");
  el.id = "_app_echo_";
  el.className = "echo";

  //
  var header = "<tr><td style='border-bottom:1px dashed #c0c0c0' colspan='2'>"
                 + "<img style='float:right' src='/public/_common_look/ios/resource/icons/desktop/area_ctrl_x.gif'"
                   + " onmouseover='this.src=\"/public/_common_look/ios/resource/icons/desktop/area_ctrl_x_o.gif\";'"
                   + " onmouseout='this.src=\"/public/_common_look/ios/resource/icons/desktop/area_ctrl_x.gif\";'"
                   + " onmousedown='doEchoHide("+this.id+",document.getElementById(\"_app_echo_\").scrollHeight,1);'"
                 + ">"
               + "</td></tr>";

  //
  var htmlOut = "<table style='background-color:white;filter:alpha(opacity=100);opacity:1;' border='0' cellpsacing='0' cellpadding='5px'>" + header + this.html + "</table>";

  //
  doc.appform.appendChild(el);
  el.innerHTML = htmlOut;

  //
  doEchoShow(this.id,1,el.scrollHeight,this.totalTime);
};

/**
 *
 **************************************/
function doEchoShow(echoId,startH,endH,showTime) {
  if (lastEchoId>echoId) {
    return;
  }

  //
  if (echoTimer) {
    clearTimeout(echoTimer);
  }

  //
  if (echoAlpha==null) {
    echoAlpha = new Alpha((new Date()).getTime(),400);
    echoTimer = setTimeout("doEchoShow("+echoId+","+startH+","+endH+","+showTime+");",ECHO_ANIM_TICK);
  }

  //
  else {
    var el = document.getElementById("_app_echo_");
    var val = echoAlpha.getValue((new Date()).getTime());

    //
    if (val<1) {
      var amount = (startH - endH) * Math.sin(val*Math.PI/2);
      el.style.height = (startH - amount)+"px";

      //
      echoTimer = setTimeout("doEchoShow("+echoId+","+startH+","+endH+","+showTime+");",ECHO_ANIM_TICK);
    }
    else {
      el.style.height = endH + "px";
      echoAlpha = null;
      echoTimer = setTimeout("doEchoHide("+echoId+","+endH+","+startH+");",showTime);
    }
  }
}

/**
 *
 **************************************/
function doEchoHide(echoId,startH,endH) {
  if (lastEchoId>echoId) {
    return;
  }

  //
  if (echoAlpha==null) {
    if (echoTimer) {
      clearTimeout(echoTimer);
    }
    echoAlpha = new Alpha((new Date()).getTime(),400);
    echoTimer = setTimeout("doEchoHide("+echoId+","+startH+","+endH+");",ECHO_ANIM_TICK);
  }

  //
  else {
    var el = document.getElementById("_app_echo_");
    var val = echoAlpha.getValue((new Date()).getTime());

    //
    if (val<1) {
      el.style.backgroundColor = "transparent";
      el.style.borderColor = "transparent";
      setOpacity(el.firstChild,1-val);

      //
      var amount = (startH - endH) * Math.sin(val*Math.PI/2);
      el.style.height = (startH - amount/2)+"px";

      //
      echoTimer = setTimeout("doEchoHide("+echoId+","+startH+","+endH+");",ECHO_ANIM_TICK);
    }
    else {
      echoAlpha = null;
      el.parentNode.removeChild(el);
    }
  }
}

/******************************************************************************/
/* WebPaneltabView                                                            */
/******************************************************************************/

var TAB_VIEW_ANIM_SPEED = 500;
var TAB_VIEW_OVERFLOW_GAP = 50;

/**
 *
 **************************************/
function tabViewTabOver(ev) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var event = new MouseEvent(ev);
  addElementOver(event.source);
}

/**
 *
 **************************************/
function tabViewTabOut(ev) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var event = new MouseEvent(ev);
  removeElementOver(event.source);
}

/**
 *
 **************************************/
function tabViewTabClick(ev,guid,value) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var event = new MouseEvent(ev);
  removeElementOver(event.source);
  //addElementSelection(event.source);

  //
  forceSubmitSelectionOnce(guid,value);
}

/**
 * Prepares overflow buttons of tab-view
 * @param guid the id of the tab list
 * @param delay delay in millis to wait before overflow will be checked
 **************************************/
function tabViewPrepareOverflow(guid,delay) {
  var handler = function() {
    tabViewDoPrepareOverflow(guid);
  }

  //
  var d = new Delay(delay,handler);
  d.start();
}

/**
 *
 **************************************/
function tabViewLeftOver(event) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var e = new MouseEvent(event);
  e.source.style.backgroundImage = "url('/public/_common_look/ios/resource/images/tab_view/button_left_o.gif')";
}

/**
 *
 **************************************/
function tabViewLeftOut(event) {
  tabViewLeftUp(event);

  //
  var e = new MouseEvent(event);
  e.source.style.backgroundImage = "url('/public/_common_look/ios/resource/images/tab_view/button_left.gif')";
}

/**
 *
 **************************************/
function tabViewLeftDown(event) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var e = new MouseEvent(event);
  //doDebug("left over "+e.source.id);

  //
  var src = e.source;
  var innerList = document.getElementById(src.guid+"_TabsInR_");
  if (innerList) {
    var root = src.parentNode;
    var outerWidth = root.offsetWidth;
    var innerWidth = innerList.offsetWidth;
    var innerLeft = innerList.offsetLeft;

    //
    if (root.animator) {
      root.animator.stop();
      root.animator = null;
    }

    //
    root.animator = new Animator(innerLeft,TAB_VIEW_ANIM_SPEED);

    //
    root.animator.setAnimHandler(
      function(value) {
        if (value>=0) {
          value = 0;
          tabViewDoScrollSelected(src.guid,value);
          root.animator.stop();
          root.animator = null;
        }
        else {
          tabViewDoScrollSelected(src.guid,value);
        }
      }
    );

    //
    root.animator.setStopHandler(
      function(value) {
        root.animator = null;
        tabViewDoCheckOverflow(src.guid);
      }
    );

    // disable text selection
    document.body.ondrag = returnFalse;
    document.body.onselectstart = returnFalse;

    //
    root.animator.start();
  }

}

/**
 *
 **************************************/
function tabViewLeftUp(event) {
  var e = new MouseEvent(event);
  var root = e.source.parentNode;
  if (root.animator) {
    root.animator.stop();
    root.animator = null;
  }

  // enable text selection
  document.body.ondrag = returnTrue;
  document.body.onselectstart = returnTrue;
}


/**
 *
 **************************************/
function tabViewRightOver(event) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var e = new MouseEvent(event);
  e.source.style.backgroundImage = "url('/public/_common_look/ios/resource/images/tab_view/button_right_o.gif')";
}

/**
 *
 **************************************/
function tabViewRightOut(event) {
  tabViewRightUp(event);

  //
  var e = new MouseEvent(event);
  e.source.style.backgroundImage = "url('/public/_common_look/ios/resource/images/tab_view/button_right.gif')";
}

/**
 *
 **************************************/
function tabViewRightDown(event) {
  if (!isEnabledEvents()) {
    return;
  }

  //
  var e = new MouseEvent(event);
  //doDebug("right over "+e.source.id);

  //
  var src = e.source;
  var innerList = document.getElementById(src.guid+"_TabsInR_");
  if (innerList) {
    var root = src.parentNode;
    var outerWidth = root.offsetWidth;
    var innerWidth = innerList.offsetWidth;
    var innerLeft = innerList.offsetLeft;

    //
    if (root.animator) {
      root.animator.stop();
      root.animator = null;
    }

    //
    root.animator = new Animator(innerLeft,-TAB_VIEW_ANIM_SPEED);

    //
    root.animator.setAnimHandler(
      function(value) {
        if (value+innerWidth<=outerWidth-TAB_VIEW_OVERFLOW_GAP) {
          value = outerWidth - TAB_VIEW_OVERFLOW_GAP - innerWidth;
          tabViewDoScrollSelected(src.guid,value);
          root.animator.stop();
        }
        else tabViewDoScrollSelected(src.guid,value);
      }
    );

    //
    root.animator.setStopHandler(
      function(value) {
        root.animator = null;
        tabViewDoCheckOverflow(src.guid);
      }
    );

    // disable text selection
    document.body.ondrag = returnFalse;
    document.body.onselectstart = returnFalse;

    //
    root.animator.start();
  }

}

/**
 *
 **************************************/
function tabViewRightUp(event) {
  var e = new MouseEvent(event);
  var root = e.source.parentNode;
  if (root.animator) {
    root.animator.stop();
    root.animator = null;
  }

  // enable text selection
  document.body.ondrag = returnTrue;
  document.body.onselectstart = returnTrue;
}

/**
 *
 **************************************/
function tabViewDoPrepareOverflowLeft(guid) {
  var root = document.getElementById(guid+"_TaBs_");
  if (root) {
    var button = document.getElementById(guid+"_TaBsLeFt_");
    if (!button) {
      button = document.createElement("div");
      button.id = guid+"_TaBsLeFt_";
      button.guid = guid;
      button.style.cssText = "position:absolute;left:0px;top:0px;width:23px;height:23px;z-index:2;background-image:url('/public/_common_look/ios/resource/images/tab_view/button_left.gif')";
      injectEvent(button,"onmouseover","tabViewLeftOver(event); return false;");
      injectEvent(button,"onmouseout","tabViewLeftOut(event); return false;");
      injectEvent(button,"onmousedown","tabViewLeftDown(event); return false;");
      injectEvent(button,"onmouseup","tabViewLeftUp(event); return false;");
      root.appendChild(button);
    }
    button.style.visibility = "visible";
  }
}

/**
 *
 **************************************/
function tabViewDoHideOverflowLeft(guid) {
  var root = document.getElementById(guid+"_TaBs_");
  if (root) {
    var button = document.getElementById(guid+"_TaBsLeFt_");
    if (button) {
      button.style.visibility = "hidden";
    }
  }
}

/**
*
**************************************/
function tabViewDoPrepareOverflowRight(guid) {
  var root = document.getElementById(guid+"_TaBs_");
  if (root) {
    var button = document.getElementById(guid+"_TaBsRiGhT_");
    if (!button) {
      button = document.createElement("div");
      button.id = guid+"_TaBsRiGhT_";
      button.guid = guid;
      button.style.cssText = "position:absolute;right:0px;top:0px;width:23px;height:23px;z-index:2;background-image:url('/public/_common_look/ios/resource/images/tab_view/button_right.gif')";
      injectEvent(button,"onmouseover","tabViewRightOver(event); return false;");
      injectEvent(button,"onmouseout","tabViewRightOut(event); return false;");
      injectEvent(button,"onmousedown","tabViewRightDown(event); return false;");
      injectEvent(button,"onmouseup","tabViewRightUp(event); return false;");
      root.appendChild(button);
    }
    button.style.visibility = "visible";
  }
}

/**
 *
 **************************************/
function tabViewDoHideOverflowRight(guid) {
 var root = document.getElementById(guid+"_TaBs_");
 if (root) {
   var button = document.getElementById(guid+"_TaBsRiGhT_");
   if (button) {
     button.style.visibility = "hidden";
   }
 }
}

/**
 * Scrolls the selected tab inside the visible area or does nothing if it is
 * already inside the visible area. Saves the scroll info as well.
 **************************************/
function tabViewDoScrollSelected(guid,scroll) {
  prepareSubmitParameter(guid+"_$sCrL$_",scroll,true);
  var inner = document.getElementById(guid+"_TabsInR_");
  if (inner) {
    inner.style.left = scroll+"px";
  }
}

/**
 *
 **************************************/
function tabViewDoCheckOverflow(guid) {
  var root = document.getElementById(guid+"_TaBs_");
  if (root) {
    var innerList = document.getElementById(guid+"_TabsInR_");
    if (innerList) {
      var outerWidth = root.offsetWidth;
      var innerWidth = innerList.offsetWidth;
      var innerLeft = innerList.offsetLeft;

      // -2 is a safety gap because FF tends to set the innerLeft to -1 rather than 0.
      if (innerLeft<-2) {
        tabViewDoPrepareOverflowLeft(guid);
      }
      else {
        tabViewDoHideOverflowLeft(guid);
      }

      //
      if (innerLeft+innerWidth>outerWidth) {
        tabViewDoPrepareOverflowRight(guid);
      }
      else {
        tabViewDoHideOverflowRight(guid);
      }
    }
  }
}

/**
 *
 **************************************/
function tabViewDoPrepareOverflow(guid) {
  //window.alert(guid);

  var root = document.getElementById(guid+"_TaBs_");
  if (root) {
    var innerList = document.getElementById(guid+"_TabsInR_");
    if (innerList) {
      var outerWidth = root.offsetWidth;
      var innerWidth = innerList.offsetWidth;
      var innerLeft = innerList.offsetLeft;

      //  check selected inside view port
      first = innerList.firstChild.firstChild.firstChild; // table.tbody.tr.td

      //
      for (var c = first; c; c = c.nextSibling) {
        if (c.className && c.className=="left_selected") {
          var x = c.offsetLeft;
          var rightCap = c.nextSibling.nextSibling;
          var w = rightCap.offsetLeft + rightCap.offsetWidth - x;

          //
          if (innerLeft+x+w > outerWidth-TAB_VIEW_OVERFLOW_GAP) {
            innerLeft = outerWidth - TAB_VIEW_OVERFLOW_GAP - x - w;
            tabViewDoScrollSelected(guid,innerLeft);
          }
          if (innerLeft+x<0) {
            innerLeft -= innerLeft + x - TAB_VIEW_OVERFLOW_GAP ;
            if (innerLeft>0) {
              innerLeft = 0;
            }
            tabViewDoScrollSelected(guid,innerLeft);
          }
          break;
        }
      }

      tabViewDoCheckOverflow(guid);
    }
  }
}

/******************************************************************************/
/* Checkbox and radio events                                                  */
/******************************************************************************/

function checkboxDoChanged(guid) {
  setTimeout("forceSubmitSelectionOnce('"+guid+"_cbAe_','1');",10);
}

function radioDoChanged(guid,el) {
  if (el && !el.checked) {
    // has been unchecked by code
    setTimeout("forceSubmitSelectionOnce('"+guid+"_cbAe_','0');",10);
  }
  else setTimeout("forceSubmitSelectionOnce('"+guid+"_cbAe_','1');",10);
}

/******************************************************************************/
/* WebApplication debug                                                       */
/******************************************************************************/

/**
 *
 **************************************/
function DebugClient(idPath, javaClassNames, parentJavaClassNames, guiPropertiesPathList, layoutPath, parentLayoutPath, iconData, classFields) {
  this.idPath = idPath;
  this.javaClassNames = javaClassNames;
  this.parentJavaClassNames = parentJavaClassNames;
  this.guiPropertiesPathList = guiPropertiesPathList;
  this.layoutPath = layoutPath;
  this.parentLayoutPath = parentLayoutPath;
  this.iconData = iconData;
  this.classFields = classFields;
  this.uniqueId = 0;
}

/**
 *
 **************************************/
DebugClient.prototype.show = function() {
  var outer = document.createElement("div");
  outer.id = "_debug_client_floater";
  outer.className = "debug_client_floating";
  document.appform.appendChild(outer);

  //
  var html = "<div><div style='text-align:center;padding:3px;border:1px solid #8080A0;background-color:#e0e0ff;font-size:12px;font-family: verdana; position: static; cursor:pointer' onclick='document.getElementById(\"_debug_client_floater\").parentNode.removeChild(document.getElementById(\"_debug_client_floater\"));return false;'>CLOSE</div><hr size='1' style='color:#f0f0ff'><p><center>Component debug info</center></p><table>";
  html += "<tr><td class=\"label\">id path</td><td class=\"value\">"+this.decoratePath(this.idPath,'.')+"</td></tr>";
  html += "<tr><td class=\"label\">java class</td><td class=\"value\">"+this.getJavaClassNamesHTML()+"</td></tr>";
  html += "<tr><td class=\"label\">parent container <br> java class</td><td class=\"value\">"+this.getParentJavaClassNamesHTML()+"</td></tr>";
  html += "<tr><td class=\"label\">GUI path list</td><td class=\"value\">"+this.getPropertiesHTML()+"</td></tr>";

  // link to visual layout editor - its hard-coded, but it's for debug purposes anyway
  if (TextUtil.endsWith(this.layoutPath,".vis.xml")) {
    html += "<tr><td class=\"label\">layout path</td><td class=\"value\">"+this.prepareIDEButtonFile(this.layoutPath)+"<a href=\"/public/program_files/ios/layout_editor/index.layout?file="+this.layoutPath+"?raw=true\" target=\"IOSVISUALLAYOUTEDITOR\">"+this.decoratePath(this.layoutPath,'/')+"</a></td></tr>";
  }
  else {
    html += "<tr><td class=\"label\">layout path</td><td class=\"value\">"+this.prepareIDEButtonFile(this.layoutPath)+this.decoratePath(this.layoutPath,'/')+"</td></tr>";
  }

  //

  // link to visual layout editor - its hard-coded, but it's for debug purposes anyway
  if (TextUtil.endsWith(this.parentLayoutPath,".vis.xml")) {
    html += "<tr><td class=\"label\">parent container <br> layout path</td><td class=\"value\">"+this.prepareIDEButtonFile(this.parentLayoutPath)+"<a href=\"/public/program_files/ios/layout_editor/index.layout?file="+this.parentLayoutPath+"\" target=\"IOSVISUALLAYOUTEDITOR\">"+this.decoratePath(this.parentLayoutPath,'/')+"</a></td></tr>";
  }
  else {
    html += "<tr><td class=\"label\">parent container <br> layout path</td><td class=\"value\">"+this.prepareIDEButtonFile(this.parentLayoutPath)+this.decoratePath(this.parentLayoutPath,'/')+"</td></tr>";
  }

  //
  html += "<tr><td class=\"label\">icon</td><td class=\"value\">"+this.getIconHTML()+"</td></tr>";

  // class fields
  html += "<tr><td class=\"label\" colspan=\"2\" style=\"text-align:center\">Object fields</td></tr>";
  html += "<tr><td class=\"value\" colspan=\"2\">"+this.getClassFieldsHTML()+"</td></tr>";
  html += "</table>";
  
  html += "<hr size='1' style='color:#f0f0ff'><div style='text-align:center;padding:3px;border:1px solid #8080A0;background-color:#e0e0ff;font-size:12px;font-family: verdana; position: static; cursor:pointer' onclick='document.getElementById(\"_debug_client_floater\").parentNode.removeChild(document.getElementById(\"_debug_client_floater\"));return false;'>CLOSE</div>";
  html += "</div>";

  //
  outer.innerHTML = html;

  //
  injectEvent(outer,"onclick","if ((new MouseEvent(event)).source.tagName.toLowerCase()=='div') this.parentNode.removeChild(this);");

  //
  var inner = outer.firstChild;
  var wSize = getWindowSize();
  var w = inner.offsetWidth;
  var h = inner.offsetHeight;

  //
  var left = (wSize[0]-w)/2;
  if (left<0) {
    left=0;
  }
  var top = (wSize[1]-h)/3;
  if (top<0) {
    top=0;
  }

  //
  inner.style.left = left + "px";
  inner.style.top = top + "px";
};

/**
 *
 **************************************/
DebugClient.prototype.decoratePath = function(path,separator) {
  if (path=='n/a') {
    return path;
  }

  //
  var splitted = path.split(separator);

  //
  var html = "";
  var len = splitted.length;
  for (var i=0; i<len; i++) {
    if (i>0) {
      html += '<b style="background-color:#f0f0f0">'+separator+'</b>';
    }
    if (i<len-1) {
      html += '<span style="color:#404040;background-color:#ffffff">'+splitted[i]+'</span>';
    }
    else {
      html += '<b style="background-color:#ffffff">'+splitted[i]+'</b>';
    }
  }

  //
  return html;
};

/**
 * static - opens the given file in the IDE (Eclipse).
 **************************************/
DebugClient.callIDEOpenResource = function(buttonId,path,paramName) {
  var button = document.getElementById(buttonId);
  button.innerHTML = "... loading ...";

  // send request to IDE
  AJAX.sendRequest(
    "/public/_common_look/ios/servlet/development/dev.srv?"+paramName+"="+path,

    // success
    function (req) {
      var txt = req.responseText;
      if (txt) {
        if (txt=="[ok]") {
          button.innerHTML = "OK";
        }
        else if (TextUtil.startsWith(txt,"[err]:")) {
          button.innerHTML = "ERROR";
          button.title = TextUtil.cropStringStart(txt,"[err]:");
        }
        else {
          button.innerHTML = "ERROR";
          button.title = txt;
        }
      }
    },

    // error
    function (req) {
      button.innerHTML = "ERROR";
      button.title = req.statusText;
    }
  );
}

/**
 * Preapres the IDE integration button that opens the given source in the IDE in
 * runtime (development mode only).
 * @return a HTML chunk that contains the button definition
 **************************************/
DebugClient.prototype.prepareIDEButtonFile = function(path) {
  var id = "_IDE_button_"+(this.uniqueId++);

  var html = "<button title='open in IDE' id='"+id+"' class='gui_button' onmouseover='this.className=\"gui_button_over\"' onmouseout='this.className=\"gui_button\"' onclick='DebugClient.callIDEOpenResource(\""+id+"\",\""+path+"\",\"file\"); return false;'>...</button>&nbsp;&nbsp;-&nbsp;";

  //
  return html;
}

/**
 * Preapres the IDE integration button that opens the given source in the IDE in
 * runtime (development mode only).
 * @return a HTML chunk that contains the button definition
 **************************************/
DebugClient.prototype.prepareIDEButtonSourceFile = function(path) {
  var id = "_IDE_button_"+(this.uniqueId++);

  var html = "<button title='open in IDE' id='"+id+"' class='gui_button' onmouseover='this.className=\"gui_button_over\"' onmouseout='this.className=\"gui_button\"' onclick='DebugClient.callIDEOpenResource(\""+id+"\",\""+path+"\",\"class\"); return false;'>...</button>&nbsp;&nbsp;-&nbsp;";

  //
  return html;
}

/**
 *
 **************************************/
DebugClient.prototype.getPropertiesHTML = function() {
 var html = "";

 //
 if (this.guiPropertiesPathList) {
   html += "<ul>";

   //
   var len = this.guiPropertiesPathList.length;
   for (var i=0; i<len; i++) {
     var pathList = this.guiPropertiesPathList[i].split(';');
     var len2 = pathList.length;
     
     //
     if (len2>1) {
       if (i==len-1) html += "<li style='list-style-type: disc'>GUI hierarchy: <ul>";
       else html += "<li>GUI hierarchy: <ul>";
     }
     
     //
     for (var j=0; j<len2; j++) {
       if (len2==1 && i==len-1) html += '<li style="list-style-type: disc">'+this.prepareIDEButtonFile(pathList[j])+this.decoratePath(pathList[j],'/')+"</li>";
       else html += '<li>'+this.prepareIDEButtonFile(pathList[j])+this.decoratePath(pathList[j],'/')+"</li>";
     }
     
     if (len2>1) html += "</ul></li>";
     
   }

   //
   html += "</ul>";
 }

 //
 if (html!="") {
   return html;
 }

 //
 return "n/a";
};

/**
 *
 **************************************/
DebugClient.prototype.getJavaClassNamesHTML = function() {
  var html = "";

  //
  if (this.javaClassNames) {
    html += "<ul>";

    //
    var len = this.javaClassNames.length;
    for (var i=0; i<len; i++) {
      if (i==len-1) {
        html += '<li style="list-style-type: disc">'+this.prepareIDEButtonSourceFile(this.javaClassNames[i])+this.decoratePath(this.javaClassNames[i],'.')+"</li>";
      }
      else {
        html += "<li>"+this.prepareIDEButtonSourceFile(this.javaClassNames[i])+this.decoratePath(this.javaClassNames[i],'.')+"</li>";
      }
    }

    //
    html += "</ul>";
  }

  //
  if (html!="") {
    return html;
  }

  //
  return "n/a";
};

/**
 *
 **************************************/
DebugClient.prototype.getClassFieldsHTML = function() {
  var html = "";

  //
  if (this.classFields) {
    html += "<ul>";

    //
    var len = this.classFields.length;
    for (var i=0; i<len; i++) {
      var f = this.classFields[i];
      if (f.n.indexOf("PROP_")==0 && f.n!="PROP_PREFIX") {
        html += "<li style='background-color:#FFFFC0'><span style='color:#808080'><b>"+f.m+"</b> "+f.t+"</span> "+f.n+" = <span style='color:blue'>"+f.v+"</span></li>";
      }
      else {
        html += "<li><span style='color:#808080'><b>"+f.m+"</b> "+f.t+"</span> "+f.n+" = <span style='color:blue'>"+f.v+"</span></li>";
      }
    }

    //
    html += "</ul>";
  }

  //
  if (html!="") {
    return html;
  }

  //
  return "n/a";
};

/**
 *
 **************************************/
DebugClient.prototype.getParentJavaClassNamesHTML = function() {
  var html = "";

  //
  if (this.parentJavaClassNames) {
    html += "<ul>";

    //
    var len = this.parentJavaClassNames.length;
    for (var i=0; i<len; i++) {
      if (i==len-1) {
        html += '<li style="list-style-type: disc">'+this.prepareIDEButtonSourceFile(this.parentJavaClassNames[i])+this.decoratePath(this.parentJavaClassNames[i],'.')+"</li>";
      }
      else {
        html += "<li>"+this.prepareIDEButtonSourceFile(this.parentJavaClassNames[i])+this.decoratePath(this.parentJavaClassNames[i],'.')+"</li>";
      }
    }

    //
    html += "</ul>";
  }

  //
  if (html!="") {
    return html;
  }

  //
  return "n/a";
};

/**
 *
 **************************************/
DebugClient.prototype.getIconHTML = function() {
  var html = "";

  //
  if (this.iconData) {
    html += "<img style=\"float:right\" src=\""+this.iconData.url+"\">";

    html += "<ul>";

    //
    html += "<li> <b>id</b>: "+this.iconData.id+"</li>";
    html += "<li> <b>url</b>: "+this.decoratePath(this.iconData.url,'/')+"</li>";
    html += "<li> <b>size</b>: "+this.iconData.w+" x "+this.iconData.h+"</li>";

    //
    html += "</ul>";
  }

  //
  if (html!="") {
    return html;
  }

  //
  return "n/a";
};

/**
 *
 **************************************/
function prepareDebugClient() {
  var el = document.getElementById("_debug_client_");

  //
  if (!el) {
    el = document.createElement("div");
    el.id = "_debug_client_";
    el.type = "button";

    //
    el.style.position = "absolute";
    el.style.overflow = "hidden";
    el.style.left = "0px";
    el.style.top = "0px";
    el.style.zIndex = 1999999990;

    //
    document.body.appendChild(el);
  }

  //
  el.innerHTML = '<button type="button" onclick="submitDebugClientRequest();">debug</button>';
}

/**
 *
 **************************************/
function submitDebugClientRequest() {
  var input = document.getElementById("_app_debug_client_");

  //
  if (!input) {
    input = document.createElement("input");
    input.id = "_app_debug_client_";
    input.name = "_app_debug_client_";
    input.type = "hidden";
    input.value = "1";
    document.appform.appendChild(input);
  }

  //
  submitSelection("_app_debug_client_","1");
  document.appform.removeChild(input);
}

/**
 *
 **************************************/
function prepareDebugClientOff() {
  var el = document.getElementById("_debug_client_");

  //
  if (!el) {
    el = document.createElement("div");
    el.id = "_debug_client_";
    el.type = "button";

    //
    el.style.position = "absolute";
    el.style.overflow = "hidden";
    el.style.left = "0px";
    el.style.top = "0px";
    el.style.zIndex = 1999999990;

    //
    document.body.appendChild(el);
  }

  //
  el.innerHTML = '<button type="button" onclick="submit();">debug off</button>';
}

/******************************************************************************/
/* Tooltip                                                                    */
/******************************************************************************/

/**
 * Tooltip: constructor
 ************************************/
function Tooltip(id) {
  this.POS_UP_RIGHT   = "UP_RIGHT";
  this.POS_UP_LEFT    = "UP_LEFT";
  this.POS_DOWN_RIGHT = "DOWN_RIGHT";
  this.POS_DOWN_LEFT  = "DOWN_LEFT";
  //
  this.DX = 10;
  this.DY = 10;
  this.TOOLTIP_DELAY = 600;
  this.TOOLTIP_TIME_RES = 100;
  this.MAX_WIDTH = 300;

  //
  this.tooltipTimerId = null;
  this.prepareVisibleTime = -1;
  this.prepareVisible = false;

  //
  if (id) {
    this.id = id;
  }
  else {
    this.id = "ttip_adv_";
  }
  this.x = 0;
  this.y = 0;
  this.visible = false;
  this.position = null;
}

/**
 *
 ************************************/
Tooltip.prototype.doTick = function() {
  if (!this.visible && this.prepareVisible) {
    var elapsed = (new Date()).getTime() - this.prepareVisibleTime;
    if (elapsed>this.TOOLTIP_DELAY) {
      this.show();
    }
  }
};

/**
 *
 ************************************/
Tooltip.prototype.getElement = function() {
  var element = document.getElementById(this.id);

  //
  if (!element) {
    var element = document.createElement("div");
    element.id = this.id;
    //
    element.style.visibility = "hidden";
    element.className = "gui_tooltip";
    //
    document.body.appendChild(element);

    // initialize timer
    this.tooltipTimerId = setInterval('defaultTooltip.doTick();',defaultTooltip.TOOLTIP_TIME_RES);
  }

  //
  return element;
};

/**
 *
 ************************************/
Tooltip.prototype.setContentHTML = function(html) {
  var element = this.getElement();

  //
  element.innerHTML = html;

  //

  /*
  var children = element.childNodes;
  var len = children.length;
  for (var i = 0; i<len; i++) {
    var child = children[i];
    if (child.style) {
      child.style.whiteSpace = "normal";
    }
  }
  */

  //
  /*
  if (element.offsetWidth>200) {
    element.style.width = "200px";
  }
  */
};

/**
 *
 ************************************/
Tooltip.prototype.move = function(x,y) {
  this.x = x;
  this.y = y;
  //
  var floaterElement = this.getElement();
  //
  var wSize = getWindowSize();
  var scroll = getWindowScroll();
  //
  var w = floaterElement.offsetWidth;
  var h = floaterElement.offsetHeight;

  //
  if (w>this.MAX_WIDTH) {
    floaterElement.style.width = this.MAX_WIDTH+"px";
    w = this.MAX_WIDTH;
  }

  //
  var newLeft = x+this.DX;
  if (this.position) {
    if (this.position==this.POS_UP_LEFT || this.position==this.POS_DOWN_LEFT) {
      newLeft = x-this.DX-w;
    }
  }
  if (newLeft+w-scroll[0]>wSize[0]) {
    newLeft = x-this.DX-w;
  }
  if (newLeft<0) {
    newLeft = x+this.DX;
  }
  //
  var newTop = y+this.DY;
  if (this.position) {
    if (this.position==POS_UP_LEFT || this.position==POS_UP_RIGHT) {
      newTop = y-this.DY-h;
    }
  }
  if (newTop+h-scroll[1]>wSize[1]) {
    newTop = y-this.DY-h;
  }
  if (newTop<0) {
    newTop = y+this.DY;
  }
  //
  floaterElement.style.left = newLeft+"px";
  floaterElement.style.top = newTop+"px";
};

/**
 *
 ************************************/
Tooltip.prototype.showLater = function() {
  this.prepareVisibleTime = (new Date()).getTime();
  this.prepareVisible = true;
};

/**
 *
 ************************************/
Tooltip.prototype.show = function() {
  var floaterElement = this.getElement();

  //
  var html = floaterElement.innerHTML;
  if (!html || html=="&nbsp;") {
    this.hide();
    return;
  }

  //
  this.move(this.x,this.y);
  this.visible = true;
  floaterElement.style.visibility = "visible";
};

/**
 *
 ************************************/
Tooltip.prototype.hide = function() {
  var floaterElement = this.getElement();
  floaterElement.style.visibility = "hidden";
  floaterElement.style.width = "";
  this.prepareVisible = false;
  this.visible = false;

  //
  this.setContentHTML("&nbsp;");
};


/******************************************************************************/
/******************************************************************************/

var defaultTooltip = new Tooltip();

/**
 *
 ************************************/
function tooltipDoOver(event, contentHTML, position) {
  if (!isEnabledEvents()) {
    return true;
  }
  var e = new MouseEvent(event);

  //
  defaultTooltip.setContentHTML(contentHTML);

  //
  defaultTooltip.x = e.x;
  defaultTooltip.y = e.y;
  //
  if (position) {
    defaultTooltip.position = position;
  }
  else {
    defaultTooltip.position = null;
  }
  //
  defaultTooltip.showLater();
}

/**
 *
 ************************************/
function tooltipDoOut(event) {
  if (!isEnabledEvents()) {
    return true;
  }

  //
  defaultTooltip.hide();
}

/**
 *
 ************************************/
function tooltipDoMove(event) {
  if (!isEnabledEvents()) {
    return true;
  }
  var e = new MouseEvent(event);

  //
  if (defaultTooltip.visible) {
    defaultTooltip.move(e.x,e.y);
  }
  else {
    defaultTooltip.x = e.x;
    defaultTooltip.y = e.y;
  }
}


/******************************************************************************/
/******************************************************************************/
/* UTIL ***********************************************************************/
/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function addElementOver(element) {
  var className = element.className;
  if (className) {
    if (!TextUtil.endsWith(className,"_over")) {
      element.className += "_over";
    }
  }
  else {
    element.className = "over";
  }
}

/**
 *
 **************************************/
function removeElementOver(element) {
  var className = element.className;
  if (TextUtil.endsWith(className,"_over")) {
    element.className = TextUtil.cropStringEnd(className,"_over");
  }
  else if (className=="over") {
    element.className = "";
  }
}

/**
 *
 **************************************/
function addElementSelection(element) {
  var className = element.className;
  if (className) {
    if (!TextUtil.endsWith(className,"_selected")) {
      element.className += "_selected";
    }
  }
  else {
    element.className = "selected";
  }
}

/**
 *
 **************************************/
function removeElementSelection(element) {
  var className = element.className;
  if (TextUtil.endsWith(className,"_selected")) {
    element.className = TextUtil.cropStringEnd(className,"_selected");
  }
  else if (className=="selected") {
    element.className = "";
  }
}


/**
 *
 **************************************/
function addElementClicked(element) {
  var className = element.className;
  if (className) {
    if (!TextUtil.endsWith(className,"_clicked")) {
      element.className += "_clicked";
    }
  }
  else {
    element.className = "clicked";
  }
}

/**
 *
 **************************************/
function removeElementClicked(element) {
  var className = element.className;
  if (TextUtil.endsWith(className,"_clicked")) {
    element.className = TextUtil.cropStringEnd(className,"_clicked");
  }
  else if (className=="clicked") {
    element.className = "";
  }
}

/**
 *
 **************************************/
function addImageUp(element) {
  var src = element.src;
  if (src) {
    if (TextUtil.endsWith(src,"_down.gif")) {
      element.src = TextUtil.cropStringEnd(src,"_down.gif")+"_up.gif";
    }
    else if (!TextUtil.endsWith(src,"_up.gif")) {
      element.src = TextUtil.cropStringEnd(src,".gif")+"_up.gif";
    }
  }
}

/**
 *
 **************************************/
function removeImageUp(element) {
  var src = element.src;
  if (src) {
    if (TextUtil.endsWith(src,"_up.gif")) {
      element.src = TextUtil.cropStringEnd(src,"_up.gif")+".gif";
    }
  }
}

/**
 *
 **************************************/
function addImageDown(element) {
  var src = element.src;
  if (src) {
    if (TextUtil.endsWith(src,"_up.gif")) {
      element.src = TextUtil.cropStringEnd(src,"_up.gif")+"_down.gif";
    }
    else if (!TextUtil.endsWith(src,"_down.gif")) {
      element.src = TextUtil.cropStringEnd(src,".gif")+"_down.gif";
    }
  }
}

/**
 *
 **************************************/
function removeImageDown(element) {
  var src = element.src;
  if (src) {
    if (TextUtil.endsWith(src,"_down.gif")) {
      element.src = TextUtil.cropStringEnd(src,"_down.gif")+".gif";
    }
  }
}

/**
 *
 **************************************/
function addImageOver(element) {
  var src = element.src;
  if (src) {
    if (!TextUtil.endsWith(src,"_o.gif")) {
      element.src = TextUtil.cropStringEnd(src,".gif")+"_o.gif";
    }
  }
}

/**
 *
 **************************************/
function removeImageOver(element) {
  var src = element.src;
  if (src) {
    if (TextUtil.endsWith(src,"_o.gif")) {
      element.src = TextUtil.cropStringEnd(src,"_o.gif")+".gif";
    }
  }
}

/**
 *
 **************************************/
function addBackImageDisabled(element) {
  var src = element.style.backgroundImage;
  if (src) {
    //
    if (TextUtil.endsWith(src,"\")") && !TextUtil.endsWith(src,"_d.gif\")")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,".gif\")")+"_d.gif\")";
    }
    //
    else if (TextUtil.endsWith(src,"')") && !TextUtil.endsWith(src,"_d.gif')")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,".gif')")+"_d.gif')";
    }
    //
    else if (!TextUtil.endsWith(src,"_d.gif)")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,".gif)")+"_d.gif)";
    }
  }
}

/**
 *
 **************************************/
function removeBackImageDisabled(element) {
  var src = element.style.backgroundImage;
  if (src) {
    //
    if (TextUtil.endsWith(src,"_d.gif\")")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,"_d.gif\")")+".gif\")";
    }
    //
    else if (TextUtil.endsWith(src,"_d.gif')")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,"_d.gif')")+".gif')";
    }
    //
    else if (TextUtil.endsWith(src,"_d.gif)")) {
      element.style.backgroundImage = TextUtil.cropStringEnd(src,"_d.gif)")+".gif)";
    }
  }
}

/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function setOpacity(node,val) {
  if (node.filters) {
    try {
      node.filters['alpha'].opacity = val*100;
    }
    catch (e) { }
  }
  else if (node.style.opacity) {
    node.style.opacity = val;
  }
}

/**
 *
 **************************************/
function getWindowSize() {
  var w = 0, h = 0;

  //Non-IE
  if( typeof(window.innerWidth) == 'number' ) {
    w = window.innerWidth;
    h = window.innerHeight;
  }

  //IE 6+ in 'standards compliant mode'
  else if( document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight) ) {
    w = document.documentElement.clientWidth;
    h = document.documentElement.clientHeight;
  }

  //IE 4 compatible
  else if( document.body && (document.body.clientWidth || document.body.clientHeight) ) {
    w = document.body.clientWidth;
    h = document.body.clientHeight;
  }

  //
  return [w,h];
}

/**
 *
 **************************************/
function saveWindowSize() {
  var size = getWindowSize();

  //
  var element = document.getElementById("_app_wnd_size_");
  if (!element) {
    element = document.createElement("input");
    element.type = "hidden";
    element.id = "_app_wnd_size_";
    element.name = "_app_wnd_size_";

    //
    document.appform.appendChild(element);
  }

  //
  element.value = size[0]+','+size[1];
}

/**
 *
 **************************************/
function getWindowScroll() {
  var x = 0;
  var y = 0;
  //
  if (document.body.scrollLeft) {
    x = document.body.scrollLeft;
  }
  else if (window.pageXOffset) {
    x = window.pageXOffset;
  }
  //
  if (document.body.scrollTop) {
    y = document.body.scrollTop;
  }
  else if (window.pageYOffset) {
    y = window.pageYOffset;
  }
  //
  return [x,y];
}

/**
 *
 **************************************/
function openWindow(url,id,w,h) {
  window.open(url,id,'width='+w+',height='+h+',toolbar=no,menubar=no,location=no,status=yes,directories=no,resizable=yes,scrollbars=yes');
}

/******************************************************************************/
/******************************************************************************/

/**
 * TextUtil :: constructor
 **************************************/
function TextUtil() {
}

/**
 *
 **************************************/
TextUtil.startsWith = function(text,startText) {
  var idx = text.indexOf(startText);
  return (idx==0);
};

/**
 *
 **************************************/
TextUtil.endsWith = function(text,endText) {
  var idx = text.lastIndexOf(endText);
  return (idx>=0 && idx==text.length-endText.length);
};

/**
 *
 **************************************/
TextUtil.cropStringStart = function(text,cropText) {
  if (TextUtil.startsWith(text,cropText)) {
    return text.substring(cropText.length);
  }

  //
  return text;
};

/**
 *
 **************************************/
TextUtil.cropStringEnd = function(text,cropText) {
  if (TextUtil.endsWith(text,cropText)) {
    return text.substring(0,text.length-cropText.length);
  }

  //
  return text;
};

/**
 *
 **************************************/
TextUtil.cutString = function(text,cutText) {
  var result = "";
  var start = 0;
  var end = text.indexOf(cutText);

  //
  while (end!=-1) {
    if (start<end) {
      result += text.substring(start,end);
    }
    start = end+cutText.length;
    end = text.indexOf(cutText,start);
  }
  if (start<text.length) {
    result += text.substring(start);
  }

  //
  return result;
};

/**
 *
 **************************************/
TextUtil.trimString = function(text) {
  return text.replace(/^\s+|\s+$/g,"");
};

/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function findAbsolutePosition(obj) {
 var curleft = 0;
 var curtop = 0;

 //
 if (obj.offsetParent) {
   while (obj.offsetParent) {
     curleft += obj.offsetLeft-obj.scrollLeft;
     curtop += obj.offsetTop-obj.scrollTop;
     var position='';
     if (obj.style&&obj.style.position) {
       position=obj.style.position.toLowerCase();
     }
     if ((position=='absolute')||(position=='relative')) {
       break;
     }
     while (obj.parentNode!=obj.offsetParent) {
       obj=obj.parentNode;
       curleft -= obj.scrollLeft;
       curtop -= obj.scrollTop;
     }
     obj = obj.offsetParent;
   }
 }

 //
 else {
   if (obj.x) {
     curleft += obj.x;
   }
   if (obj.y) {
     curtop += obj.y;
   }
 }

 //
 return {left:curleft,top:curtop};
}


/******************************************************************************/
/* events                                                                     */
/******************************************************************************/

var mouseDblClicked = false; // TODO - reset with full-ajax reset

/**
 * Post-ajax reinitialization.
 **************************************/
function mouseDoDoubleClickReinitialize() {
  mouseDblClicked = false;
}

/**
 * Delays a click delay and shows the loading animation.
 **************************************/
function mouseDoDoubleClickDelay(submitFun) {
  setTimeout(submitFun,350);
  showLoading();
}

/**
 * Use ondblclick="mouseDoDoubleClick(event);" and make a delay before
 * submitting something on single click (use mouseDoDoubleClickDelay).
 **************************************/
function mouseDoDoubleClick(e) {
  mouseDblClicked = true;
}

/**
 *
 **************************************/
function mouseWasDoubleClick() {
  var dblc = mouseDblClicked;
  mouseDblClicked = false;
  return dblc;
}

/******************************************************************************/
/******************************************************************************/

/**
 * MouseEvent :: constructor
 **************************************/
function MouseEvent(e) {
  this.x = 0;
  this.y = 0;

  //
  this.e = e ? e : window.event;

  //
  if (!e) {
    e = this.e; // added because IE doesn't send the e parameter after an injected event
  }

  //
  this.source = e.target ? e.target : e.srcElement;

  //
  if (e.pageX || e.pageY) {
    this.x = e.pageX;
    this.y = e.pageY;
  }
  else if (e.clientX || e.clientY) {
    this.x = e.clientX + document.body.scrollLeft;
    this.y = e.clientY + document.body.scrollTop;
  }

  //
  this.keyCTRL = e.ctrlKey;
  this.keySHIFT = e.shiftKey;
  this.keyALT = e.altKey;
}

/**
 *
 **************************************/
MouseEvent.prototype.consume = function() {
  consumeEvent(this.e);
};

/**
 *
 **************************************/
MouseEvent.addEventListener = function(target,type,func,bubbles) {
  if (document.addEventListener) {
    target.addEventListener(type,func,bubbles);
  }
  else if (document.attachEvent) {
    target.attachEvent("on"+type,func,bubbles);
  } else {
    target["on"+type] = func;
  }
};

/**
 *
 **************************************/
MouseEvent.removeEventListener = function(target,type,func,bubbles) {
  if (document.removeEventListener) {
    target.removeEventListener(type,func,bubbles);
  }
  else if (document.detachEvent) {
    target.detachEvent("on"+type,func,bubbles);
  }
  else {
    target["on"+type] = null;
  }
};

/**
 *
 **************************************/
KeyEvent.KEY_BACKSPACE = 8;
KeyEvent.KEY_TAB       = 9;
KeyEvent.KEY_ESC       = 27;
KeyEvent.KEY_ENTER     = 13;
KeyEvent.KEY_LEFT      = 37;
KeyEvent.KEY_UP        = 38;
KeyEvent.KEY_RIGHT     = 39;
KeyEvent.KEY_DOWN      = 40;
KeyEvent.KEY_HOME      = 36;
KeyEvent.KEY_END       = 35;
KeyEvent.KEY_SHIFT     = 16;
KeyEvent.KEY_CTRL      = 17;
KeyEvent.KEY_ALT       = 18;
KeyEvent.KEY_PGUP      = 33;
KeyEvent.KEY_PGDOWN    = 34;

/**
 * KeyEvent :: constructor
 **************************************/
function KeyEvent(event) {
  if (window.event) {
    this.nativeEvent = window.event;
    this.keyNum = window.event.keyCode;
    this.src = window.event.srcElement;
  }
  else if (event.keyCode) {
    this.nativeEvent = event;
    this.keyNum = event.keyCode;
    this.src = event.target;
  }
  else if (event.which) {
    this.nativeEvent = event;
    this.keyNum = event.which;
    this.src = event.target;
  }

  //
  this.keyChar = String.fromCharCode(this.keyNum);

  //
  this.isShift = (this.nativeEvent.shiftKey) ? true : false;
  this.isCtrl = (this.nativeEvent.ctrlKey) ? true : false;
  this.isAlt = (this.nativeEvent.altKey) ? true : false;
};

/**
 *
 **************************************/
KeyEvent.prototype.consume = function() {
  consumeEvent(this.nativeEvent);
};

/**
 *
 **************************************/
KeyEvent.prototype.getAllModifiers = function() {
  return (this.isShift ? "1" : "0") + (this.isCtrl ? "1" : "0") + (this.isAlt ? "1" : "0");
};

/**
 *
 **************************************/
KeyEvent.prototype.toString = function() {
  return "KeyEvent: keyNum="+this.keyNum+", keyChar="+this.keyChar+", isShift="+this.isShift+", isCtrl="+this.isCtrl+", isAlt="+this.isAlt;
};

/**
 * Generic event :: constructor
 **************************************/
function GenericEvent(event) {
  this.nativeEvent = event ? event : window.event;
  this.src = this.nativeEvent.target ? this.nativeEvent.target : this.nativeEvent.srcElement;
};

/**
 *
 **************************************/
GenericEvent.prototype.consume = function() {
  consumeEvent(this.nativeEvent);
};


/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function consumeEvent(event) {
  if (event.stopPropagation) {
    event.stopPropagation();
    event.preventDefault();
  }
  else {
    event.cancelBubble = true;
    event.returnValue  = false;
  }
}

/**
 *
 **************************************/
function addEventListener(target,type,func,bubbles) {
  if (document.addEventListener) {
    target.addEventListener(type,func,bubbles);
  }
  else if (document.attachEvent) {
    target.attachEvent("on"+type,func,bubbles);
  } else {
    target["on"+type] = func;
  }
}

/**
 *
 **************************************/
function removeEventListener(target,type,func,bubbles) {
  if (document.removeEventListener) {
    target.removeEventListener(type,func,bubbles);
  }
  else if (document.detachEvent) {
    target.detachEvent("on"+type,func,bubbles);
  }
  else {
    target["on"+type] = null;
  }
}

var T_EVENTS = false;

/**
 *
 **************************************/
function setEnabledEvents(enableEvents) {
  block_all_keys = !enableEvents;

  T_EVENTS = enableEvents;

  //
  //doDebug("events = "+enableEvents);
}

/**
 *
 **************************************/
function isEnabledEvents() {
  return T_EVENTS;
}

/**
 *
 **************************************/
function returnTrue(event) {
  return true;
}

/**
 *
 **************************************/
function returnFalse(event) {
  return false;
}

/******************************************************************************/
/* event injection                                                            */
/******************************************************************************/
function injectEvent(target,eventName,code) {
  target[eventName] = new Function("event",code);
}


/******************************************************************************/
/* real time                                                                  */
/******************************************************************************/

/**
 *
 ***************************************/
function Alpha(startTime,deltaTime) {
  this.startTime = startTime;
  this.deltaTime = deltaTime;
}

/**
 *
 ***************************************/
Alpha.prototype.reset = function(startTime, deltaTime) {
  this.startTime = startTime;
  this.deltaTime = deltaTime;
};

/**
 *
 ***************************************/
Alpha.prototype.getValue = function(currentTime) {
  var currentDeltaTime = currentTime-this.startTime;

  //
  return currentDeltaTime/(parseFloat(this.deltaTime));
};

/**
 *
 ***************************************/
function Beta(deltaMillis) {
  this.startTime = (new Date()).getTime();
  this.deltaMillis = deltaMillis;
}

/**
 *
 ***************************************/
Beta.prototype.reset = function() {
  this.startTime = (new Date()).getTime();
};

/**
 *
 ***************************************/
Beta.prototype.getValue = function() {
  var currentDeltaTime = (new Date()).getTime() - this.startTime;

  //
  return currentDeltaTime/(parseFloat(this.deltaMillis));
};

/**
 *
 ***************************************/
function Gamma(deltaMillis,sValue,eValue) {
  this.startTime = (new Date()).getTime();
  this.deltaMillis = deltaMillis;
  this.sValue = sValue;
  this.eValue = eValue;
  this.finished = false;
}

/**
 *
 ***************************************/
Gamma.prototype.reset = function() {
  this.startTime = (new Date()).getTime();
  this.finished = false;
};

/**
 * You should call the method getValue before this method to obtain a valid
 * response.
 ***************************************/
Gamma.prototype.isFinished = function() {
  return this.finished;
};

/**
 *
 ***************************************/
Gamma.prototype.getValue = function() {
  var currentDeltaTime = (new Date()).getTime() - this.startTime;
  var normalized = currentDeltaTime/(parseFloat(this.deltaMillis));

  //
  if (normalized>1) {
    this.finished = true;
    return this.eValue;
  }

  //
  var theSin = Math.sin(normalized*Math.PI/2);

  //
  return this.sValue + theSin * (this.eValue - this.sValue);
};

/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
var DOMUtil = {

  /**
   * Returns the first ancestor of the given element who's tag equals the given
   * tagName or null if no such ancestor found.
   */
  findAncestorByTagName: function (element, parentTag) {
    var parent = element.parentNode;

    //
    while (parent && parent.tagName.toUpperCase()!=parentTag.toUpperCase()) {
      parent = parent.parentNode;
    }

    //
    return parent ? parent : null;
  },

  /**
   * Returns the given element or the first of its ancestors in case its tag
   * equals the given tagName or null if no such parent or element found.
   */
  findElementByTagName: function (element, tag) {
    var parent = element;

    //
    while (parent && parent.tagName.toUpperCase()!=tag.toUpperCase()) {
      parent = parent.parentNode;
    }

    //
    return parent ? parent : null;
  }

};

/******************************************************************************/
/******************************************************************************/

/**
 * Resizes a dom element in a given period of time.
 * @param el - the element to be resized
 * @param axes - one of: 'w', 'h' or 'wh'
 * @param type - one of: 'open' or 'close'
 * @param time - the available time in milliseconds to perform the resizing
 ***************************************/
function Resizer(el,axes,type,time) {
  this.el = el;
  this.axes = axes;
  this.type = type;
  this.time = time;
  this.gammaW = null;
  this.gammaH = null;
  this.timer = null;

  //
  this.stopHandler = null;
  this.resizeHandler = null;

  //
  this.TICK_INTERVAL = 15;
}

/**
 *
 ***************************************/
Resizer.prototype.setStopHandler = function(stopHandler) {
  this.stopHandler = stopHandler;
};

/**
 *
 ***************************************/
Resizer.prototype.setResizeHandler = function(resizeHandler) {
  this.resizeHandler = resizeHandler;
};

/**
 * Forces the resizer to use the given end width.
 ***************************************/
Resizer.prototype.setEndWidth = function(endW) {
  this.endW = endW;
};

/**
 * Forces the resizer to use the given end height.
 ***************************************/
Resizer.prototype.setEndHeight = function(endH) {
  this.endH = endH;
};

/**
 * Starts the resizer
 ***************************************/
Resizer.prototype.start = function() {
  if (this.axes=='w' || this.axes=='wh') {
    var startW = this.el.offsetWidth;
    var endW = this.type=='open' ? (this.endW ? this.endW : this.el.scrollWidth) : 1;
    this.gammaW = new Gamma(this.time,startW,endW);
  }

  //
  if (this.axes=='h' || this.axes=='wh') {
    var startH = this.el.offsetHeight;
    var endH = this.type=='open' ? (this.endH ? this.endH : this.el.scrollHeight) : 1;
    this.gammaH = new Gamma(this.time,startH,endH);
  }

  //
  var self = this;
  this.timer = setTimeout(function() {self.tick();},this.TICK_INTERVAL);
};

/**
 * Forces the resizer to stop immediately. Leaves the element's size in the
 * current state. Notifies the eventHandler.
 ***************************************/
Resizer.prototype.stop = function() {
  if (!this.stopped) {
    clearTimeout(this.timer);
    this.gammaW = null;
    this.gammaH = null;
    this.stopped = true;
    //
    if (this.stopHandler) {
      this.stopHandler();
    }
  }
};

/**
 * Returns true if this resizer stopped and actually finished its work.
 ***************************************/
Resizer.prototype.isFinished = function() {
  return (this.finished);
};

/**
 *
 ***************************************/
Resizer.prototype.isResizing = function() {
  return (!this.stopped);
};

/**
 *
 ***************************************/
Resizer.prototype.isStopped = function() {
  return (this.stopped);
};

/**
 *
 ***************************************/
Resizer.prototype.tick = function() {
  if (this.stopped) {
    return;
  }

  //
  if (this.gammaW) {
    var w = parseInt(this.gammaW.getValue());
    this.el.style.width = w+"px";
  }

  //
  if (this.gammaH) {
    var h = parseInt(this.gammaH.getValue());
    this.el.style.height = h+"px";
  }

  //
  if (this.resizeHandler) {
    this.resizeHandler();
  }

  //
  if (this.gammaW) {
    if (this.gammaW.isFinished()) {
      if (this.gammaH) {
        if (this.gammaH.isFinished()) {
          this.finished = true;
          this.stop();
          return;
        }
      }
      else {
        this.finished = true;
        this.stop();
        return;
      }
    }
  }

  //
  else {
    if (this.gammaH && this.gammaH.isFinished()) {
      this.finished = true;
      this.stop();
      return;
    }
  }

  //
  var self = this;
  this.timer = setTimeout(function() {self.tick();},this.TICK_INTERVAL);
};

/******************************************************************************/
/******************************************************************************/

/**
 * Animates something notifying a given handler.
 * @param startValue a starting integer value
 * @param valuePerSecond a float value that the main value will be changed per
 * second
 * @param time animation time
 **************************************/
function Animator(startValue,valuePerSecond,time) {
  this.value = startValue;
  this.startValue = startValue;
  this.valuePerSecond = parseFloat(valuePerSecond);

  //
  if (time) {
    this.time = time;
  }
  else {
    this.time = null;
  }

  //
  this.TICK_INTERVAL = 15;
}

/**
 * A handler function with one integer parameter.
 **************************************/
Animator.prototype.setStopHandler = function(stopHandler) {
 this.stopHandler = stopHandler;
};

/**
 * A handler function with one integer parameter
 **************************************/
Animator.prototype.setAnimHandler = function(animHandler) {
 this.animHandler = animHandler;
};

/**
 *
 **************************************/
Animator.prototype.start = function() {
  this.startTime = (new Date()).getTime();

  //
  this.beta = new Beta(1000);

  //
  if (!this.timer) {
    var self = this;
    this.timer = setInterval(function() {self.tick();},this.TICK_INTERVAL);
  }
};

/**
 *
 **************************************/
Animator.prototype.stop = function() {
  if (this.timer) {
    clearInterval(this.timer);
    this.timer = null;
  }

  //
  if (this.stopHandler) {
    this.stopHandler(this.value);
  }
};

/**
 *
 **************************************/
Animator.prototype.tick = function() {
  if (this.time) {
    var currentTime = (new Date()).getTime();
    if (currentTime-this.startTime>=this.time) {
      this.value = parseInt(this.startValue + this.time/1000.0 * this.valuePerSecond);
      if (this.animHandler) {
        this.animHandler(this.value);
      }
      this.stop();

      //
      return;
    }
  }

  //
  this.value = parseInt(this.startValue + this.beta.getValue() * this.valuePerSecond);
  if (this.animHandler) {
    this.animHandler(this.value);
  }
};

/**
 *
 **************************************/
Animator.prototype.getValue = function() {
  return this.value;
};

/******************************************************************************/
/******************************************************************************/

/**
 * @param time - the delay time in milliseconds to wait
 * @param delayHandler - the handler to be called after the specified delay
 **************************************/
function Delay(time, delayHandler) {
  this.time = time;
  this.delayHandler = delayHandler;
  this.timer = null;
}

/**
 * Starts the delay
 **************************************/
Delay.prototype.start = function() {
  this.stop();

  //
  var self = this;
  this.timer = setTimeout(function() {self.doDelay();},this.time);
};

/**
 * Stops the delay - delayHandler won't be called.
 **************************************/
Delay.prototype.stop = function() {
  if (this.timer) {
    clearTimeout(this.timer);
    this.timer = null;
  }
};

/**
 * Does the delay notification and stops the delay. This actually forces the
 * delay to finish its job even if the delay has not expired yet.
 **************************************/
Delay.prototype.doDelay = function() {
  this.delayHandler();
  this.stop();
};


/*
var focusDelay = new Delay(500,forceFocus);
focusDelay.start();

function forceFocus() {
  var focusElement = document.getElementById("_app_comp_focus_");
  if (focusElement && focusElement.value) {
    var el = document.getElementById(focusElement.value);
    if (el) {
      el.focus();
      doDebug("forcing "+focusElement.value);
    }
  }
  
  //
  focusDelay.start();
}
*/


/******************************************************************************/
/******************************************************************************/

/**
 *
 **************************************/
function doDebug(text) {

  return;

  var console = document.getElementById("_debug_console_");

  //
  if (!console) {
    console = document.createElement("div");
    console.id = "_debug_console_";
    console.style.cssText = "filter:alpha(opacity=15);opacity:0.15;";

    console.style.fontFamily = "verdana";
    console.style.fontSize = "11px";
    console.style.padding = "2px";
    console.style.margin = "2px";
    console.style.position = "absolute";
    console.style.right = 0;
    console.style.bottom = 0;
    console.style.width = "780";
    console.style.height = "280";
    console.style.zIndex = 2000000000;
    console.style.border = "1px solid black";
    console.style.overflow = "auto";
    console.style.color = "white";
    console.style.backgroundColor = "blue";

    //
    injectEvent(console,"onmouseover","setOpacity(this,1)");
    injectEvent(console,"onmouseout","setOpacity(this,0.15)");

    injectEvent(console,"onclick","this.innerHTML='';");

    //
    document.body.appendChild(console);
  }

  //
  console.appendChild(document.createTextNode(text));
  console.appendChild(document.createElement("br"));
  console.scrollTop = 1000000;
}
