/*****************************************************************************
 * $Header$
 * $Author$
 * $Revision$
 * $Date$
 *
 * UI controller.
 *
 * Copyright: Neolane 2001-2007
 *****************************************************************************/

// 
// browser detection
// 

// browser engine name
var isGecko = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsGecko = (ua.indexOf('gecko') != -1 && ua.indexOf('safari') == -1);
  return function(){return bIsGecko;}
}();

var isAppleWebKit = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsAppleWebKit = (ua.indexOf('applewebkit') != -1);
  return function(){return bIsAppleWebKit;}
}();

// browser name
var isKonqueror = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsKonqueror = (ua.indexOf('konqueror') != -1);
  return function(){return bIsKonqueror;}
}();

var isSafari = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsSafari = (ua.indexOf('safari') != - 1);
  return function(){return bIsSafari;}
}();

var isOmniweb = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsOmniweb = (ua.indexOf('omniweb') != - 1);
  return function(){return bIsOmniweb;}
}();

var isOpera = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsOpera = (ua.indexOf('opera') != -1);
  return function(){return bIsOpera;}
}();

var isAol = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsAol = (ua.indexOf('aol') != -1);
  return function(){return bIsAol;}
}();

var isIE = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsIE = (ua.indexOf('msie') != -1 && !isOpera() && (ua.indexOf('webtv') == -1) );
  return function(){return bIsIE;}
}();

var isMozilla = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsMozilla = (isGecko() && ua.indexOf('gecko/') + 14 == ua.length);
  return function(){return bIsMozilla;}
}();

var isNS = function()
{
  var ua = navigator.userAgent.toLowerCase(); 
  var bIsNS = ( isGecko() ? (ua.indexOf('netscape') != -1) : ( (ua.indexOf('mozilla') != -1) && !isOpera() && !isSafari() && (ua.indexOf('spoofer') == -1) && (ua.indexOf('compatible') == -1) && (ua.indexOf('webtv') == -1) && (ua.indexOf('hotjava') == -1) ) );
  return function(){return bIsNS;}
}();

/** Helper function used to fix transparency bug under IE */
function IEFixImage()
{
  if ( this.src.indexOf(".png") != -1 )
  { // fix the PNG transparency bug under IE
    var src = this.src;
	  this.src = "/xtk/img/blank.gif";
	  this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft." +
				  "AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
	}
  else if ( this.src.indexOf(".bmp") != -1 )
    // make magenta color transparent
    this.style.filter = "progid:DXImageTransform.Microsoft.Chroma(color=#FF00FF)";
}

function Navigator() {}

Navigator.prototype.fixImage = function(img)
{
  if (isIE())
    img.onload = IEFixImage;
}

/** Constructor
  *
  */
function UIController()
{
  this.ctx = null                       /* the XML context */
  this.aFileXPath = new Array()
  this.soapRouterUrl = window.location.protocol + "//" + window.location.host  + "/nl/jsp/soaprouter.jsp"
   
/** Map of observers (using xpath as key).
  * 
  * Observer properties are:
  *
  * object: object to notify.
  * method: method of the object 'object' to call.
  * name:   name of the observer (must be unique). Used to avoid to notify the 
  *         observer which has modified the context. 
  * 
  * Remarks:
  * - if the object 'object' has a method onSubmit(), that method is 
  *   automatically called on form submission. */
  this.observers = new Hash()
  
  this.childrenObservers = new Array()  /* observers registered on OBSERVE_CHILDREN mode */
}

UIController.prototype = new Navigator()

UIController.prototype.OBSERVE_XPATH      = 1
UIController.prototype.OBSERVE_CHILDREN   = 2

/** Register an XPATH observer.
  *
  * @xpath  XPATH to observe.
  * @object object to notify.
  * @mode   registration mode (OBSERVE_XPATH or OBSERVE_CHILDREN). */ 
UIController.prototype.registerObserver = function(xpath, obj, method, name, mode)
{
  var observer = { "object": obj, "method": method, "name": name, "mode": mode }
  if ( mode == this.OBSERVE_XPATH )
  {
    var registration = this.observers.get(xpath)
    if ( registration == undefined )
    { // first registration on this xpath
      // => we create a array to store registrations
      registration = new Array(observer)
      this.observers.set(xpath, registration)
    }
    else
      registration.push(observer)
  }
  else
  {
    observer.xpath = xpath
    this.childrenObservers.push(observer)
  }
  
  return observer
}

/** Notify all observers.
  *
  * @startPath      if defined limit the notification of observers registered
  *                 on a path starting by startPath. 
  * @value          new value for the given XPath. Can be undefined.
  * @ignoreObserver name of an observer to ignore. */
UIController.prototype.notifyAllObservers = function(startPath, value, ignoreObserver)
{
  for (var xpath in this.observers.items)
  {
    if ( startPath != undefined && xpath.indexOf(startPath) != 0 )
      // skip this xpath
      continue
      
    var observers = this.observers.get(xpath)
    var newValue = value
    if ( newValue == undefined || xpath!=startPath)
      // get value from the context
      newValue = getXPathValue(this.ctx, xpath)
    
    for (var i=0; i < observers.length; i++)
    {
      // get the value from the context
      var observer = observers[i]
      if ( ignoreObserver != observer.name )
        // notify the observer
        observer.method.call(observer.object, xpath, newValue)
    }
  }
  
  // notify observers registered on mode OBSERVE_CHILDREN
  var nObservers = this.childrenObservers.length;
  for (var i=0; i < nObservers; i++)
  {
    var observer = this.childrenObservers[i]
    var newValue = value
    if ( newValue == undefined || observer.xpath!=startPath)
      // get value from the context
      newValue = findElement(this.ctx, observer.xpath)
    if ( ignoreObserver != observer.name && (startPath == undefined || startPath.indexOf(observer.xpath) == 0) )
      observer.method.call(observer.object, startPath ? startPath : observer.xpath, newValue)
  }
}

/** Set a value in the context.
  *
  * @xpath  XPATH to modify.
  * @value  value to set. */ 
UIController.prototype.setValue = function(xpath, value, ignoreObserver, asCDATA)
{
  setXPathValue(this.ctx.documentElement, xpath, value, asCDATA)
  this.notifyAllObservers(xpath, value, ignoreObserver)
}


/** Set a date part in the context. (from a combo)
  *
  * @xpath    XPATH to modify.
  * @value    value to set. 
  * @datePart date part to update (DD, MM, MN, Y2, Y4, H4, H2, MI) */ 
UIController.prototype.setDateValue = function(xpath, value, datePart, ignoreObserver)
{
  var strSettedDate = getXPathValue(this.ctx, xpath)
  var aDate, aTime
  if( strSettedDate != null && strSettedDate.length > 0 )
  {
    var aDateTime = strSettedDate.split(" ")
    aDate = aDateTime[0].split("/")
    var aTime = null
    if (aDateTime[1])
      aTime = aDateTime[1].split(":")
    else
      aTime = ["XX", "XX", "XX"]
  }
  else
  {
    aDate = ["XXXX", "XX", "XX"]
    aTime = ["XX", "XX", "00"]
  }
  
  if( value == "" )
    value = "XX"
    
  if( datePart == "Y4" || datePart == "Y2" )
  {
    if( value == "XX" )      
      aDate[0] = "XXXX"
    else
      aDate[0] = value
  }
  else if( datePart == "MN" || datePart == "MM" )
    aDate[1] = value
  else if( datePart == "DD" )
    aDate[2] = value
  else if( datePart == "H4" || datePart == "H2" )
    aTime[0] = value
  else if( datePart == "MI" )
    aTime[1] = value
  
  this.setValue(xpath, aDate[0] + "/" + aDate[1].padLeft(2) + "/" + aDate[2].padLeft(2) + " " 
                     + aTime[0].padLeft(2) + ":" + aTime[1].padLeft(2) + ":" + aTime[2].padLeft(2), 
                ignoreObserver)
}


/** Get a value from the context.
  *
  * @xpath  XPATH to get.
  * @return the value. */
UIController.prototype.getValue = function(xpath)
{
  return getXPathValue(this.ctx, xpath)
}

/** Show or hide a cell containing a container, an input or a static.
  *
  * @name name of the cell to hide.
  * @visible true or false. */
UIController.prototype.setCellVisibility = function(name, visible)
{
  function show(node, visible)
  {
    if ( node != null && node.style != undefined )
      node.style.display = visible ? "" : "none";
  }
  
  show(document.getElementById(name + "-label-left"), visible);
  show(document.getElementById(name + "-cell"), visible);
  show(document.getElementById(name + "-label-right"), visible);
}

/** Submit the page to the server.
  *
  * @strAction      action at the origin of the submition (next, previous, validate, refresh). 
  * @strTarget      target for the form submission (_blank, _parent, ...).
  * @strTransition  name of the ougoing transition in diagram design. *
  * @bForceNoFile   file are already uploaded, don't reupload it 
  * @bNoWait        don't display waiting box*/
  
UIController.prototype.submit = function(strAction, strTarget, strTransition, bForceNoFile, bNoWait)
{
  // call the method onSubmit for all registered observers
  for (var xpath in this.observers.items)
  {
    var observers = this.observers.get(xpath)
    for (var i=0; i < observers.length; i++)
      if ( observers[i].object.onSubmit != undefined )
        if ( observers[i].object.onSubmit.call(observers[i].object) == false )
          return false
  }
  
  var nObservers = this.childrenObservers.length;
  for (var i=0; i < nObservers; i++)
  {
    var observer = this.childrenObservers[i]
    if ( observer.object.onSubmit != undefined )
      if ( observer.object.onSubmit.call(observer.object) == false )
        return false
  }
  
  var formObject = document.getElementById("page-form")  
  var bHasFile = false
  if( !bForceNoFile )
  {
    for( var strInputId in this.aFileXPath )
      if( formObject[strInputId].value.toString().length > 0 )
      {
        bHasFile = true
        break;
      }
  }

  if( formObject["userAction"] ) 
    formObject["userAction"].value = strAction;
  else // ## Compatibility with previous webApp/report
    formObject["action"].value = strAction;

  if ( strTransition != undefined )
    formObject["transition"].value = strTransition    

  if( bHasFile && (strAction == 'next' || strAction == 'submit') )
  {
    var dialog = new HtmlDialog(xtk_core.fileUploading(), "OK", this, true)
    dialog.buttonOk.style.display = "none"
    dialog.buttonCancel.style.display = "none"
    dialog.setSize("30em")
    dialog.show()

    formObject.enctype = "multipart/form-data"
    if( isIE() )
    {
      formObject.setAttribute("enctype", "multipart/form-data")
      formObject.setAttribute("encoding", "multipart/form-data")
    }
    formObject.action = "/nl/jsp/uploadFile.jsp" 
    formObject.target = "uploadFileTarget"
    this.formTarget = strTarget 
  }
  else
  {
    if( strTarget != undefined && strTarget.length > 0 )
      formObject.target = strTarget
    formObject["ctx"].value    = toXMLString(this.ctx)

    if( !bNoWait && (strTarget == "_self" || strTarget == "_parent") )
    {
      var divOverToDisable = document.getElementById("overToDisable")
      if( divOverToDisable != null )
        divOverToDisable.style.display = ''
      var divLoading = document.getElementById("divLoading")
      if( divLoading != null )
        divLoading.style.display = ''
    }
  } 
  
  formObject.submit()

  return true
}

UIController.prototype.getSessionToken = function()
{
  return getXPathValue(this.ctx, "/ctx/__sessiontoken")
}

// Register file in controller
UIController.prototype.addFileInput = function(strInputId, strXPath)
{
  this.aFileXPath[strInputId] = strXPath
}

// Called by uploadFile.jsp when all files are downloaded
// Update context with md5 and go to next activity
UIController.prototype.uploadFileCallBack = function(aFilesInfo)
{
  var formObject = document.getElementById("page-form");
  for( var strInputId in this.aFileXPath )
    formObject[strInputId].value = ""
    
  // add file information in context
  if( aFilesInfo ) 
    for( var iIndex = 0 ; iIndex < aFilesInfo.length ; iIndex++ )
    {
      var xpath =  this.aFileXPath[aFilesInfo[iIndex].paramName]
      this.setValue(xpath + "/@md5", aFilesInfo[iIndex].md5);      
      this.setValue(xpath + "/@originalName", aFilesInfo[iIndex].fileName);
      this.setValue(xpath + "/@id", 0);
    }

  formObject.action = ""
  formObject.target = ""
  formObject.enctype = ""
  if( isIE() )
  {
    formObject.setAttribute("enctype", "application/x-www-form-urlencoded")
    formObject.setAttribute("encoding", "application/x-www-form-urlencoded")
  }  
  this.submit(formObject["userAction"].value, this.formTarget, formObject["transition"].value, true, true)
}

// Update file input diplay
UIController.prototype.changeFileInputVisibiliy = function(strInputId, bIsEditing)
{
  if( bIsEditing )
  {
    document.getElementById(strInputId).style.display = "inline"
    document.getElementById(strInputId).disabled = false
    document.getElementById(strInputId+"-edit").style.display = "none"
    document.getElementById(strInputId+"-fileName").style.display = "inline"
  }
  else if( document.getElementById(strInputId).value.length == 0 )
  {
    document.getElementById(strInputId).style.display = "none"
    document.getElementById(strInputId).disabled = true
    document.getElementById(strInputId+"-edit").style.display = "inline"
    document.getElementById(strInputId+"-fileName").style.display = "none"
    if( document.getElementById(strInputId+"-downloadLink") )
      document.getElementById(strInputId+"-downloadLink").style.display = "inline"
  }  
}