/*****************************************************************************
 * $Header$
 * $Author$
 * $Revision$
 * $Date$
 *
 * controls.js
 *
 * Copyright: Neolane 2001-2007
 *****************************************************************************/

//[of]:DebugConsole
/** DebugConsole
  *
  * Widget used to display the context when __debug=1 is avaiable on the URL. */
function DebugConsole(controller)
{
  this.controller = controller
  controller.registerObserver("/", this, this.onNodeChange, "DebugConsole", controller.OBSERVE_CHILDREN)
}

/** One node has changed in the context */
DebugConsole.prototype.onNodeChange = function()
{
  if ( this.htmlElement == null )
  {
    this.htmlElement = document.getElementById("DebugConsole")
    if ( this.htmlElement == null )
    {
      this.htmlElement = document.body.appendChild(document.createElement("pre"));
      this.htmlElement.id = "DebugConsole"
    }
  }

  var strXMLContext = toXMLString(this.controller.ctx)
  this.htmlElement.innerHTML = "" /* reset the content */
  this.htmlElement.appendChild(document.createTextNode(strXMLContext))
}
//[cf]
//[of]:RichTextArea
/** RichTextArea
  *
  * Widget used to add an Html Text Area*/
  
  
  /** Constructor
  *
  * @controller       Main controller
  * @id               id of the td i want to be in
  * @xpath            the xpath of the richtextarea
  * @pos              position of the tool bar */ 
function richTextArea(controller, id, xpath, pos)
{
  this.controller = controller;
  this.htmlParent = document.getElementById(id);
  this.id = id + "_richTextArea";
  this.xpath = xpath;
  this.toolbarPosition = pos;
  this.countTrigger = 0;
  this.countCurrent = 0;
  this.isTimer = false;
  this.tableIframe = 0;
  this.divTextArea = 0;
  this.iframe = 0;
  this.divToolBar = 0;
  this.arrayFct = new Array();
  this.init();
  controller.registerObserver(xpath, this, this.onNodeChange, this.id, controller.OBSERVE_XPATH)
}

  
  /** Fct called on notify
  *
  * @xpath              the xpath of the change 
  * @value              value to set in */ 
richTextArea.prototype.onNodeChange = function (xpath, value)
{
  this.setContent(value);
}

/** Fonction called when the form is submit
  *
  *                             */

richTextArea.prototype.onSubmit = function()
{
  this.controller.setValue(this.xpath, this.getContent(), this.id);
  return true;
}


/** Initialisation fonction
  *
  *                             */ 
richTextArea.prototype.init = function ()
{
  this.initFunction();
  richTextArea.prototype.getMyArea(this.id, this) // register this richTextArea
  this.initTextArea();

  if (document.designMode && !isOpera() && !isIOs()) 
  {
    this.divTextArea.style['display'] = "none";
    this.initBuildTable()
    this.iframe = document.getElementById(this.id);
    this.initAfterLoad();
  }
  else if( isOpera() || isIOs() )
    this.setContent(this.controller.getValue(this.xpath));
}


/** Add the function to build element to the arrayFCT
  *
  *                             */ 
richTextArea.prototype.initFunction = function ()
{
  this.arrayFct['bold'] = this.makeButton;
  this.arrayFct['italic'] = this.makeButton;
  this.arrayFct['underline'] = this.makeButton;
  this.arrayFct['justifyleft'] = this.makeButton;
  this.arrayFct['justifyfull'] = this.makeButton;
  this.arrayFct['justifycenter'] = this.makeButton;
  this.arrayFct['justifyright'] = this.makeButton;
  this.arrayFct['source'] = this.makeSource;
}

/** Make the textArea
  *
  *                             */ 
richTextArea.prototype.initTextArea = function ()
{
  this.divTextArea = document.createElement("div");
  this.htmlParent.appendChild(this.divTextArea);
  this.textArea = document.createElement("textarea");
  this.textArea.id = this.id + '_textarea';
  this.textArea.onblur = this.makeEvent(this.id, this.refresh);
  this.textArea.className = "textarea";
  this.divTextArea.appendChild(this.textArea);
}


/** Make the Table for the Iframe and the Iframe
  *
  *                             */ 
richTextArea.prototype.initBuildTable = function()
{
  this.tableIframe = document.createElement('table');
  this.tableIframe.appendChild(document.createElement('tbody'));
  this.tableIframe.lastChild.appendChild(document.createElement("tr"));
  if (this.toolbarPosition == 0)
  {
    this.divBar = this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td"));
    this.tableIframe.lastChild.appendChild(document.createElement("tr"));
    this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td")).appendChild(this.makeIframe("type"));
  }
  else if (this.toolbarPosition == 1)
  {
    this.divBar = this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td"));
    this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td")).appendChild(this.makeIframe("type"));
  }
  else if (this.toolbarPosition == 2)
  {
    this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td")).appendChild(this.makeIframe("type"));
    this.tableIframe.lastChild.appendChild(document.createElement("tr"));
    this.divBar = this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td"));
  }
  else if (this.toolbarPosition == 3)
  {
    this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td")).appendChild(this.makeIframe("type"));
    this.divBar = this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td"));
  }
  else
  {
    this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td")).appendChild(this.makeIframe("type"));
    this.divBar = this.tableIframe.lastChild.lastChild.appendChild(document.createElement("td"));
    this.divBar.style['display'] = 'none';
  }
  
  this.divBar.className = "richAreaToolbar"
  this.htmlParent.appendChild(this.tableIframe);
}

/** STATIC fct that let the browser build the iframe document
  * @id               id of the iframe
  *                             */ 
richTextArea.prototype.initAfterLoad = function ()
{
  var idoc;
  var w3c = true;
  if (this.iframe.contentDocument !== undefined)
    idoc = this.iframe.contentDocument;
  else if (this.iframe.contentWindow !== undefined)
  {
    idoc = this.iframe.contentWindow;
    w3c = false;
  }

  if (idoc == null || idoc === undefined)
  {
    window.setTimeout(this.makeEvent(this.id, this.initAfterLoad) , 250);
  }
  else 
  {
    if (w3c == false)
    {
      idoc = idoc.document;
      if (window[this.id].document.body == null || window[this.id].document.body == undefined)
        {
          window.setTimeout(this.makeEvent(this.id, this.initAfterLoad) , 500);
          return ;
        }
    }

    idoc.open();
    idoc.close();
    idoc.designMode = w3c ? "on" : "On";
    this.setContent(this.controller.getValue(this.xpath));
    this.addEvents();
    this.initIframeStyle(idoc);
  }
}


/** Make Set the style of the Iframe Body and get it from the .richTextArea_Body in the document.styleSheets
  *
  *                             */ 
richTextArea.prototype.initIframeStyle = function (idoc)
{
  var rule;
  for (var i = 0; i < document.styleSheets.length; i++)
  {
    var rules;
    if (isIE())
      rules = document.styleSheets[i].rules;
    else
      rules = document.styleSheets[i].cssRules;
    for (var j = 0; j < rules.length; j++)
      if (rules[j].selectorText == ".richTextArea_Body")
        rule = rules[j];
  }
  var  style_node = idoc.createElement("style");
  style_node.setAttribute("type", "text/css");
  style_node.setAttribute("media", "screen");
  if (isSafari())
  {
    var head = idoc.createElement("head");
    idoc.firstChild.insertBefore(head, idoc.firstChild.firstChild);
  }
  if (idoc.getElementsByTagName("head").length > 0)
  {
    idoc.getElementsByTagName("head")[0].appendChild(style_node);
    if (isIE() && idoc.styleSheets && idoc.styleSheets.length > 0)
    {
      var last_style_node = idoc.styleSheets[idoc.styleSheets.length - 1];
      if (typeof(last_style_node.addRule) == "object")
      {
        last_style_node.addRule(".richTextArea_Body", rule.style.cssText);
        last_style_node.addRule("P", "margin: 0;");
      }
    }
    else
      style_node.appendChild(document.createTextNode(rule.cssText));
  }
  if (idoc.body)
  {
    idoc.body.className = "richTextArea_Body";
  }
}

/** Make a button
  * @type      type of the element you want to add
  *                             */
richTextArea.prototype.addElement = function (type)
{
  if (document.designMode /*&& !isOpera()*/) 
  {
    if (this.arrayFct[type] !== undefined)
    {
      this.divBar.appendChild(this.arrayFct[type].call(this, type));
      if (this.toolbarPosition & 1)
        this.divBar.appendChild(document.createElement("br"));
    }
  }
}

/** Change the width of the RichtextArea
  * @value                      value you want to set
  *                             */
richTextArea.prototype.setWidth = function(value)
{
  this.textArea.style["width"] = value;
  if (document.designMode && !isOpera() && !isIOs())
    this.iframe.style["width"] = value;
}

richTextArea.prototype.setRows = function(value)
{
  this.textArea.rows = value;
}

/** Change the height of the RichtextArea
  * @value                      value you want to set
  *                             */
richTextArea.prototype.setHeight = function(value)
{
  this.textArea.style["height"] = value;
  if (document.designMode && !isOpera() && !isIOs())
    this.iframe.style["height"] = value;
}

/** Change the align of the RichtextArea
  * @value                      value you want to set
  *                             */
richTextArea.prototype.setAlign = function(value)
{
  this.htmlParent.style["align"] = value;
  this.divTextArea.style['align'] = value;
  if (document.designMode && !isOpera() && !isIOs())
  {
    this.tableIframe.lastChild.firstChild.firstChild.style["align"] = value;
    this.tableIframe.lastChild.lastChild.firstChild.style["align"] = value;
    this.tableIframe.lastChild.lastChild.lastChild.style["align"] = value;
    this.tableIframe.lastChild.firstChild.lastChild.style["align"] = value;
  }
}

/** Change the valign of the RichtextArea
  * @value                      value you want to set
  *                             */
richTextArea.prototype.setVAlign = function(value)
{
  this.htmlParent.style["valign"] = value;
  this.divTextArea.style['valign'] = value;
  if (document.designMode && !isOpera() && !isIOs())
  {
    this.tableIframe.lastChild.firstChild.firstChild.style["valign"] = value;
    this.tableIframe.lastChild.lastChild.firstChild.style["valign"] = value;
    this.tableIframe.lastChild.lastChild.lastChild.style["valign"] = value;
    this.tableIframe.lastChild.firstChild.lastChild.style["valign"] = value;
  }
}

/** Make a button
  * @type      type of the button (!!!used to execCommand!!!)
  *                             */ 
richTextArea.prototype.makeButton = function (type) 
{
  var input = document.createElement("img");
  input.src = "/xtk/img/form/"+ type + ".png";
  input.className = type;
  input.id = this.id + "_" + type;
  input.onmousedown = this.makeEvent(this.id, this.buttonOnMouseDown);
  input.onclick = this.makeEvent(this.id, this.buttonOnClick);

  var link = document.createElement("div");

  link.className = "button"
  link.appendChild(input)
  return (link);
}

/** Make a button
  * @type      nearly unused
  *                             */ 
richTextArea.prototype.makeSource = function (type) 
{
  var input = document.createElement("img");
  input.className = type;
  input.src = "/xtk/img/form/"+ type + ".png";
  input.id = this.id + "_" + type;
  input.onmousedown = this.makeEvent(this.id, this.buttonOnMouseDown);
  input.onclick = this.makeEvent(this.id, this.SourceOnClick);
  var input2 = input.cloneNode(true);
  this.divTextArea.appendChild(input2); 
  input2.onmousedown = this.makeEvent(this.id, this.buttonOnMouseDown);
  input2.onclick = this.makeEvent(this.id, this.SourceOnClick);
  var link = document.createElement("div");
  link.className = "button"
  link.appendChild(input)  
  return (link);
}


/** Make the iframe
  * @type              UNUSED
  *                             */ 
richTextArea.prototype.makeIframe = function (type)
{
  this.iframe = document.createElement("iframe");
  this.iframe.id = this.id;
  this.iframe.className = "richTextArea_Frame";
  return (this.iframe);
}

/** Set the EventsHandler to the iframe
  *
  *                             */ 
richTextArea.prototype.addEvents = function ()
{
  if (this.iframe.contentDocument && this.iframe.contentDocument.addEventListener)
  {
    this.iframe.contentDocument.addEventListener("keyup", this.makeEvent(this.id, this.askRefresh), false);
    this.iframe.contentDocument.addEventListener("mouseup", this.makeEvent(this.id, this.askRefresh), false);
  }
  else if(this.iframe.contentWindow && this.iframe.contentWindow.document &&
           this.iframe.contentWindow.document.attachEvent)
  {
    this.iframe.contentWindow.document.attachEvent("onkeyup", this.makeEvent(this.id, this.askRefresh))
    this.iframe.contentWindow.document.attachEvent("onmouseup", this.makeEvent(this.id, this.askRefresh))
  }
}

/** Handler of mousedown event on button. Do nothing but avoid some default problem with IE
  *
  *                             */ 
richTextArea.prototype.buttonOnMouseDown = function(e)
{
  var evt = e ? e : window.event; 
  if (evt.returnValue)
    evt.returnValue = false;
  else if (evt.preventDefault)
    evt.preventDefault( );
  return false;
}

/** Handler of click event on Style buttons.
  *
  *                             */ 
richTextArea.prototype.buttonOnClick = function (e) 
{
  var target;
  var event = e || window.event;
  if(event.target)
    target = event.target;
  else
    target = event.srcElement;

  if (window[this.id])
    window[this.id].document.execCommand(target.className, false, null);
  else
    document.getElementById(this.id).contentWindow.document.execCommand(target.className, false, null); 
  this.askRefresh(e);
  return false;
}

/** Handler of click event on Source button.
  *
  *                             */ 
richTextArea.prototype.SourceOnClick = function (e) 
{
  var target;
  var event = e || window.event;
  if(event.target)
    target = event.target;
  else
    target = event.srcElement;

  
  var str = this.getContent();
  var tmp = this.tableIframe.style['display'];
  this.tableIframe.style['display'] = this.divTextArea.style['display'];
  this.divTextArea.style['display'] = tmp;
  this.setContent(str);
  this.askRefresh(e);
  return false;
}

/** return the content of the RichTextArea.
  *
  *                             */ 
richTextArea.prototype.getContent = function() 
{
  if (document.designMode && !isOpera() && !isIOs() && this.divTextArea.style['display'] == 'none') 
  {
    var str;
    if (window[this.id])
      str = window[this.id].document.body.innerHTML;
    else
      str = document.getElementById(this.id).contentWindow.document.body.innerHTML;
    if (isIE() && (str == '<P>&nbsp;</P>' || str == '<DIV>&nbsp;</DIV>'))
      str = "";
    else if (isGecko() && str == '<br>')
      str = "";
    else if (isSafari() && (str == '<div><br></div>' || str == '<br>'))
      str = "";
    return (str);
  } 
  else
  {
    return document.getElementById(this.id + "_textarea").value;
  }
}

/** Set the value of the RTA.
  *
  *                             */
richTextArea.prototype.setContent = function(value) 
{
  if (document.designMode && !isOpera() && !isIOs() && this.divTextArea.style['display'] == 'none') 
  {
    if (this.iframe.contentDocument && this.iframe.contentDocument.body)
      this.iframe.contentDocument.body.innerHTML = value;
    else if(this.iframe.contentWindow && this.iframe.contentWindow.document
            && this.iframe.contentWindow.document.body)
      this.iframe.contentWindow.document.body.innerHTML = value;
    else
      this.initAfterLoad(this.id);
  }
  else
    document.getElementById(this.id + "_textarea").value = value;
  return (true);
}

/** STATIC FONCTION, retrieve or set a richtextarea associated to an id
  * @id              The RTA id that we want to retrieve
  * @val             if undefined the fct will return the RTA associated to the param id.
  *                  else it will store val and associated it to val.id
  *                             */
richTextArea.prototype.getMyArea = function (id, val)
{
  if (this.hashRichTextArea === undefined)
    this.hashRichTextArea = new Array();
  if (val !== undefined)
  {
    this.hashRichTextArea[val.id] = val;
  }
  else
    return this.hashRichTextArea[id];
  return null;
}

/** Fct to notifiate the controller from changes
  *
  *                             */
richTextArea.prototype.refresh = function(e)
{
  if (this.countCurrent != this.countTrigger)
  {
    this.isTimer = true;
    this.countTrigger = this.countCurrent;
    window.setTimeout(this.makeEvent(this.id, this.refresh), 500);
  }
  else
  {
    this.isTimer = false;
    this.controller.setValue(this.xpath, this.getContent(), this.id);
  }
}

/** Fct to temporate the notification
  *
  *                             */
richTextArea.prototype.askRefresh = function(e)
{
  this.countCurrent++;

  if (this.isTimer == false)
  {
    this.isTimer = true;
    this.countTrigger = this.countCurrent;
    window.setTimeout(this.makeEvent(this.id, this.refresh), 500);
  }
  return false;
}

/** STATIC FONCTION.  it return a function that you should use for eventhandling setting.
  *                   It helps calling richTextArea methode with the RTA associated to the @id
  * @id               The RTA id that will instanciate the fct param
  * @fct              the fonction you want to be called with the rta with the param id
  *                             */
richTextArea.prototype.makeEvent = function(id, fct)
{
  return function(e)
  {
    fct.call(richTextArea.prototype.getMyArea(id), e);
    return false;
  };
}
//[cf]

/** Functions to set the width of a table's tds to the size of the biggest one
  *
  *                                 */
function AutoAdjustMatrixColumns(id)
{
  var table = document.getElementById(id);
  var ths = table.getElementsByTagName ('TH');
  var max = 0;
  for (var i = 1; i < ths.length; i++)
  {
    if (max < ths[i].offsetWidth)
      max = ths[i].offsetWidth;
  }
  if( max > 0 ) // if displayed
  {
    for (var i = 1; i < ths.length; i++)
      ths[i].width = max + "px";
  }
}

// ---------------------------------------------------------------
// Calls a function when the enter key is pressed
// ---------------------------------------------------------------
function onEnterPressed(e, func)
{
  var evt = (e) ? e:(window.event) ? window.event : null;
  if( evt )
  {
    var cKey = (evt.charCode) ? evt.charCode : 
                ((evt.keyCode) ? evt.keyCode : 
                  ((evt.which) ? evt.which : 0));
    if( cKey == "13" )
      return func();
  }
  return true;
}


// ---------------------------------------------------------------
// Performs a click
// @option URL or object ID list or javascript code
// ---------------------------------------------------------------
function PerformClickAction(ctx, action, target, transition, option, param, event)
{
  // Use a form to pass URL parameters.
  // If the form already exists, reuse it.
  if( action == "url" )
  {
    /** Add a value as an hidden input in a form */
    function addInputValue(form, strName, strValue)
    {
      input = document.createElement("input")
      input.type = "hidden"
      input.name = strName
      input.value = strValue
      form.appendChild(input)
    }

    if( document.getElementById("URLForm") != null )
    {
      var ndForm = document.getElementById("URLForm")
      // Delete previous form inputs 
      var ndFormChild = ndForm.firstChild
      while( ndFormChild != null )
      {
        if( ndFormChild.tagName == "INPUT" )
          ndFormChild = ndForm.removeChild(ndFormChild)
        ndFormChild = ndFormChild.nextSibling
      }
    }
    
    var ndURLForm = document.createElement("form")
    ndURLForm.setAttribute("id", "URLForm")
    
    ndURLForm.method = "GET"
    ndURLForm.action = option
    ndURLForm.target = target
    
    for (var i=0; i < param.length; i++)
      addInputValue(ndURLForm, param[i].name, param[i].value)
    
    document.body.appendChild(ndURLForm)
    ndURLForm.submit()
  }
  else
  {
    for (var i=0; i < ctx.length; i++)
      document.controller.setValue(ctx[i].xpath, ctx[i].value)
    
    if( action == "refreshData" )
    {
      var jsString = ""
      for ( var i in option )
        jsString += "document.lstData"+ option[i] +".load(true);"
      return eval(jsString)
    }
    else if( action == "javascript" )
      return eval(option)
    else
      return document.controller.submit(action, target, transition)
  }
}

// ===========================================================================
// HTML  dialog
// ===========================================================================
// ---------------------------------------------------------------------------
// constructor
// [in] strTitle : dialog title
// [in] strOkLabel : overrides default ok label.
// [in] dialogsCallBacks : object which implement some functions
// ---------------------------------------------------------------------------
function HtmlDialog(strTitle, strOkLabel, dialogsCallBacks, bCenter)
{
  this.htmlElement = document.createElement("div")
  
  // disable text selection
  if( isIE() ) 
    this.htmlElement.onselectstart = function () { return false }
  else
    this.htmlElement.onmousedown = function (event) 
    { 
      if (typeof event.preventDefault != 'undefined') 
        event.preventDefault();
    }
  this.htmlElement.className = "dialog"
  this.htmlElement.style.display = "none"
  this.divOverToDisable = document.getElementById("overToDisable")
  this.dialogsCallBacks = dialogsCallBacks
  this.bCenter = bCenter
  
  this.titleTable = document.createElement("table")
  this.titleTable.cellPadding = 0 
  this.titleTable.cellSpacing = 4
  this.titleTable.width = "100%" 
  this.titleTable.className = "dialogHeader"
  this.titleTable.style.fontSize = "9pt"
  var tbodyTable =  this.titleTable.appendChild(document.createElement("tbody"))
  var trTable = tbodyTable.appendChild(document.createElement("tr"))
  var tdTable = trTable.appendChild(document.createElement("td"))
  
  this.title = tdTable.appendChild(document.createElement("table"))
  this.title.cellSpacing = 0
  this.title.cellPadding = 0
  this.title.className = "title"
  var tbody = this.title.appendChild(document.createElement("tbody"))
  var tr = tbody.appendChild(document.createElement("tr"))
  
  var td = tr.appendChild(document.createElement("td"))
  td.className="top-left-corner"
  td = tr.appendChild(document.createElement("td"))
  td.className="top"
  td = tr.appendChild(document.createElement("td"))
  td.className="top-right-corner"
  
  var trTitle = tbody.appendChild(document.createElement("tr"))
  td = trTitle.appendChild(document.createElement("td"))
  td.className="left"
  
  td = trTitle.appendChild(document.createElement("td"))
  var tableTitle = td.appendChild(document.createElement("table"))
  tableTitle.cellSpacing = 0
  tableTitle.cellPadding = 0
  tableTitle.style.width = "100%"
  tr = tableTitle.appendChild(document.createElement("tbody")).appendChild(document.createElement("tr"))

  // left image ref
  this.tdLeftImg = tr.appendChild(document.createElement("td"))

  this.tdTitle = tr.appendChild(document.createElement("td"))
  this.tdTitle.style.width="100%"
  this.tdTitle.align="left"
  this.tdTitle.appendChild(document.createTextNode(strTitle))
  this.tdTitle.className = "title"
  
  // close button
  this.tdClose = tr.appendChild(document.createElement("td"))
  this.tdClose.className = "close"
  var closeBtn = this.tdClose.appendChild(document.createElement("img"))
  closeBtn.src = "/xtk/img/close.png"
  closeBtn.className = "close"
  closeBtn.dialog = this
  
  td = trTitle.appendChild(document.createElement("td"))
  td.className="right"
  
  tr = tbody.appendChild(document.createElement("tr"))
  td = tr.appendChild(document.createElement("td"))
  td.className="bottom-left-corner"
  td = tr.appendChild(document.createElement("td"))
  td.className="bottom"
  td = tr.appendChild(document.createElement("td"))
  td.className="bottom-right-corner"
  
  this.htmlElement.appendChild(this.titleTable)
  
  this.divContent = document.createElement("div")
  this.divContent.className = "content"
  this.divContent.style.display = "none"
  this.htmlElement.appendChild(this.divContent)
  
  this.divLoading = document.createElement("div")
  this.divLoading.className = "loading"
  this.divLoading.innerHTML = 
        "<div><object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='https://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,65,0' width='48' height='48' id='preload48x48' align='middle'>"+
        "  <param name='movie' value='/xtk/img/form/classic/preload48x48.swf'/>"+
        "  <param name='quality' value='high' /><param name='bgcolor' value='#ffffff' />"+
        "  <embed src='/xtk/img/form/classic/preload48x48.swf' quality='high' bgcolor='#ffffff' width='48' height='48' name='preload48x48' align='middle' type='application/x-shockwave-flash' pluginspage='https://www.macromedia.com/go/getflashplayer'/>"+
        "</object></div>"
  this.htmlElement.appendChild(this.divLoading)
  
  var divCommand = document.createElement("div")
  divCommand.className = "command"
  this.htmlElement.appendChild(divCommand)
  
  this.buttonOk = document.createElement("input")
  this.buttonOk.type = "button"
  if( strOkLabel == "" || strOkLabel == null )
    strOkLabel = xtk_formbase.dialog_defaultOk()
  this.buttonOk.value = strOkLabel
  this.buttonOk.dialog = this  
  divCommand.appendChild(this.buttonOk)
  
  this.buttonCancel = document.createElement("input")
  this.buttonCancel.type = "button"
  this.buttonCancel.value = xtk_formbase.dialog_cancel()
  this.buttonCancel.dialog = this
  divCommand.appendChild(this.buttonCancel)        
  document.getElementById("dialogsLib").appendChild(this.htmlElement)
  
  this.buttonCancel.onclick = function ()
  {
    this.dialog.show(false)
  }
  this.buttonOk.onclick = function ()
  {
    var bHide = true 
    if( this.dialog.dialogsCallBacks && typeof(this.dialog.dialogsCallBacks.onValidDialog) == "function" )
      bHide = this.dialog.dialogsCallBacks.onValidDialog()
    if( bHide )
      this.dialog.show(false)
  }
  closeBtn.onclick = function()
  {
    this.dialog.htmlElement.style.display  = 'none'
    var divOverToDisable = document.getElementById("overToDisable")
    if( divOverToDisable != null )
      divOverToDisable.style.display = 'none'
  }
}

HtmlDialog.prototype.setTitle = function(strTitle)
{
  this.tdTitle.innerHTML = strTitle
  this.tdTitle.style.width="100%"
  this.tdTitle.className = "title"
}

HtmlDialog.prototype.setLeftImg = function(strImg)
{
  this.tdLeftImg.innerHTML = "<img style='margin-right: 4px;margin-left: 4px' src='"+strImg+"'/>"
}

HtmlDialog.prototype.setSize = function(width, height)
{
  if( typeof(width) == "string" )
    this.htmlElement.style.width = width
  else if( width )
    this.htmlElement.style.width = width + "px"
  if( typeof(height) == "string" )
    this.htmlElement.style.height = height
  else if( height )
    this.htmlElement.style.height = height + "px"
}

HtmlDialog.prototype.show = function(bVisible)
{
  function getWindowWidth()
  {
    var iWindowWidth
    if( window.innerWidth ) // all except IE
      iWindowWidth  = window.innerWidth;
    else if( document.documentElement && document.documentElement.clientWidth ) // IE 6
      iWindowWidth = document.documentElement.clientWidth
    else if( document.body ) // IE 7
      iWindowWidth = document.body.clientWidth;
    return iWindowWidth
  }   
  
  if( bVisible != false )
  {
    this.htmlElement.style.display = "block"
    if( this.bCenter )
    { 
      var iScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || 0
      var iScrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0
      
      // Center the dialog horizontally
      this.htmlElement.style.left = iScrollLeft+Math.floor((getWindowWidth() - this.htmlElement.offsetWidth)/2)+"px"
      this.htmlElement.style.top = 50 + iScrollTop + "px"
    }
  }
  else
    this.htmlElement.style.display = "none"

  if( this.divOverToDisable )
  {
    this.divOverToDisable.style.display = this.htmlElement.style.display
    this.divOverToDisable.style.height  = document.body.clientHeight + "px"
    this.divOverToDisable.style.width  = document.body.clientWidth + "px"
  }
  
  if( bVisible != false && this.dialogsCallBacks
   && typeof(this.dialogsCallBacks.onShowDialog) == "function" )
    this.dialogsCallBacks.onShowDialog()
} 

HtmlDialog.prototype.addContent = function(html)
{
  this.divContent.appendChild(html)
} 

HtmlDialog.prototype.setLoading = function(bLoading)
{
  if( bLoading )
  {
    this.divContent.style.display = "none"
    this.divLoading.style.display = "block"
  }
  else
  {
    this.divContent.style.display = "block"
    this.divLoading.style.display = "none"
  }
} 

HtmlDialog.prototype.deleteContent = function()
{
  if (this.divContent != undefined)
  {
    this.divContent.innerHtml = ""
    delete this.divContent
  }
} 

// ---------------------------------------------------------------------------
// Recurse depth-first over all the nodes within the given XML tree, calling
// the user specified callback upon each node.
// NB : if the callback returns with true, the recursion will be stopped.
// ---------------------------------------------------------------------------
var traverseXMLTree = function(ndNode, fnProcessNode)
{
  // Dead leaf
  if( !ndNode )
    return false
  
  var bStopRecursion
  // Get references to the first child and next sibling right away in order to
  // be robust to node deletions within the callback.
  var ndFirstChild = ndNode.firstChild
  var ndNextSibling = ndNode.nextSibling
  
  // Apply the user specified callback on the current, valid element
  if( ndNode.nodeType == 1 )
    bStopRecursion = fnProcessNode(ndNode)
  
  // And recurse on every child unless told otherwise (depth first traversal)
  if( !bStopRecursion )
    bStopRecursion = traverseXMLTree(ndFirstChild, fnProcessNode)
  if( !bStopRecursion )
    bStopRecursion = traverseXMLTree(ndNextSibling, fnProcessNode)
  
  return bStopRecursion
}

// ===========================================================================
// DHTML version of linkEdit
// ===========================================================================
// ---------------------------------------------------------------------------
// constructor
// [in] controller 
// [in] id : javascript id of this linkEdit
// [in] xpath : xpath of an element to store id and compute string
// [in] strFolderModel : xpath of an element to store id and compute string
// [in] strFolderLinkId : xpath folder link id
// [in] strFolderLink : xpath folder link
// [in] bIsFolder : this link edit link to a folder
// [in] bWriteFolderAccess : force to filter on right folder access
// [in] strEnumImagePath : enum image path
// [in] strInputTableWidth : custom width for the input's parent table
// [in] inlineStyles : object containing inline style informations
// ---------------------------------------------------------------------------
function XtkLinkEdit(controller, id, xpath, strSchemaId, strFolderModel, 
                     strFolderLinkId, strFolderLink, bIsFolder, bWriteFolderAccess,
                     strEnumImagePath, strInputTableWidth, inlineStyles)
{
  this.controller = controller
  this.id = id
  this.xpath = xpath
  this.isFolder = false
  this.iSelectedFolderId = 0
  this.strSchemaId = strSchemaId
  this.isFolder = bIsFolder
  this.strFolderModel = strFolderModel
  this.strFolderLinkId = strFolderLinkId
  this.strFolderLink = strFolderLink
  if( this.strFolderModel != "" || this.isFolder )
  {
    this.xtkNavigationTree = new XtkNavigationTree(this.controller, this.strFolderModel,
                                                   "/ctx/" + this.id + "/folder", this)
    if (bWriteFolderAccess != undefined && (bWriteFolderAccess == "true" || bWriteFolderAccess == 1))
      this.xtkNavigationTree.isWriteAccess = true
  }
  this.ndSysFilter = null
  this.aJoinPath = new Array()
  this.aSelectionData = null
  this.bNavtreeLoaded = false
  // Enum image path used in the query with analyse == true
  this.strEnumImagePath = (typeof strEnumImagePath == "string") ? strEnumImagePath : ""
  this.strInputTableWidth = strInputTableWidth || ""
  // We only use the inlineStyleValue style as of now
  this.inlineStyleValue = inlineStyles ? inlineStyles["inlineStyleValue"] : null
}

XtkLinkEdit.prototype.setSysFilter = function(ndSysFilter)
{
  if( this.isFolder && this.xtkNavigationTree )
    this.xtkNavigationTree.setSysFilter(ndSysFilter)
  else
  {
    this.ndSysFilter = ndSysFilter
    
    // Check the conditions to see whether we should show an input to
    // allow filtering or not. We don't use the findNodes function as we need
    // to recurse on children (condition nodes within a condition node).
    var testFilterInputExistence = function(ndCondition)
    {
      // Returns true (which will stop the recursion by the way) if 
      // $([@_filterInput]) is used in an expression.
      var expr = ndCondition.getAttribute("expr")
      return expr && expr.indexOf("$([@_filterInput])") != -1
    }
    this.bUseFiltering = traverseXMLTree(this.ndSysFilter.documentElement, 
                                         testFilterInputExistence)
    
    if( this.bUseFiltering )
    {
      // Set up the input filter div
      // filter input value
      this.strFilterValue = ""
      
      this.filterDiv = document.createElement("div")
      this.filterDiv.className = "filter"
      var filterLabel = this.filterDiv.appendChild(document.createElement("div"))
      filterLabel.appendChild(document.createTextNode(xtk_core.linkEditFilter()))
      var filterInput = this.filterDiv.appendChild(document.createElement("input"))
      this.filterDiv.input = filterInput
      
      // Force the focus on click as it doesn't work whatever the reason
      filterInput.onclick = function()
      {
        this.focus()
      }
      
      // Update the querydef and reload the list on enter
      var thisRef = this
      var filterAndReload = function()
      {
        // Until we have a requests cache, only check against the last value
        if( thisRef.strFilterValue != filterInput.value )
        {
          thisRef.strFilterValue = filterInput.value
          return thisRef.loadList()
        }
      }
      filterInput.onkeypress = function(e)
      {
        return onEnterPressed(e, filterAndReload)
      }
    }
  }
}

XtkLinkEdit.prototype.addJoinPath = function(xtrXPath)
{
  this.aJoinPath.push(xtrXPath)
}

XtkLinkEdit.prototype.initNavtree = function()
{
  if( !this.bNavtreeLoaded )
    this.xtkNavigationTree.init(this.iSelectedFolderId, this.strSelectedFolderFullName)
  this.bNavtreeLoaded = true
}

// ---------------------------------------------------------------------------
// onShowDialog callback 
// get first authorized folder or load folder's documents 
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.onShowDialog = function()
{
  this.aSelection = new Array()
  for( var index in this.aJoinPath )
  {
    var xpath = this.aJoinPath[index]
    var value = this.controller.getValue(this.xpath + "/" + xpath)
    // Don't take into account values equal to 0
    if( value && value != 0 )
      this.aSelection.push({"xpath":xpath, "value":value})
  }
  
  if( this.aSelection.length > 0 && this.strFolderModel != ""  )
  {
    // we have a selection : get parent folder
    // TODO : use ajax list with bread crumb
    var query = new QueryDef(this.strSchemaId, "getIfExists")
    query.action = "getEntityFolder"
    query.addSelectExpr("[" + this.strFolderLinkId + "]")
    query.addSelectExpr("[" + this.strFolderLink + "/@fullName]")
    for(var index in this.aSelection )
      query.addWhereConditionExpr("[" + this.aSelection[index].xpath + "] = " 
                                      + escapeXtkString(this.aSelection[index].value))
    query.execute(this.controller.soapRouterUrl, this.controller.getSessionToken(), this)  
  }
  else if( this.aSelection.length > 0 && this.isFolder )
  {
    // Folder selection only (no list)
    this.iSelectedFolderId = this.aSelection[0].value
    this.initNavtree()   
    // hide loading
    this.dialog.setLoading(false)
  }
  else 
  {
    if( !this.bNavtreeLoaded && (this.strFolderModel != "" || this.isFolder ))
    {
      // Load navtree with the first authorized folder selectionned by default
      // TODO : use ajax list with bread crumb
      var soapCall = new SoapMethodCall("xtk:folder", "FindFirstAuthorized",
                                        SoapMethodCall.SOAP_ENCODING_NATIVE, 
                                        this.controller.getSessionToken())
      soapCall.writeString("folderFilter", "@model = "+ escapeXtkString(this.strFolderModel))
      soapCall.writeBoolean("writeAccess", false)
      soapCall.writeInt("orgUnit", 0)
      soapCall.finalizeDocument()
      soapCall.execute(this.controller.soapRouterUrl, this)      
    }
    else if( !this.isFolder )
    {
      // Load the list without any folder selection
      this.loadList()
    }
  }
}

// ---------------------------------------------------------------------------
// onValidDialog callback 
// set values (compute string and key) via controller
// return a boolean to hide the dialog
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.onValidDialog = function()
{
  if( this.isFolder )
  {
    this.controller.setValue(this.xpath + "/@_cs", getXPathValue(this.selectedItem, "@label"))
    this.controller.setValue(this.xpath + "/@id", getXPathValue(this.selectedItem, "@id"))
  }
  else
  {
    this.controller.setValue(this.xpath + "/@_cs", getXPathValue(this.selectedItem, "@_cs"))
    if( this.aJoinPath.length > 0 )
    {
      for( var index in this.aJoinPath )
      {
        var xpath = this.aJoinPath[index]
        this.controller.setValue(this.xpath + "/" + xpath, getXPathValue(this.selectedItem, xpath))
      }
    }
    else
      this.controller.setValue(this.xpath + "/@id", getXPathValue(this.selectedItem, "@id"))
  }
  return true
}

// ---------------------------------------------------------------------------
// onXtkQueryCompleted callback 
// render list result in the dialog
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.onXtkQueryCompleted = function(queryDef, xmlResult, ex)
{
  if( ex == null )
  {
    if( queryDef.action == "loadList" )
    {
      var node = firstChildElement(xmlResult)
      
      this.list.innerHTML = "" // clean old list
      
      while( node )
      {
        var item = this.list.appendChild(document.createElement("a"))
        item.href = "javascript:;"
        item.className = "linkEditListItem"
        item.itemKeys = node
        // test if this item is selected
        if( this.aSelection.length > 0 )
        {
          var bIsSelected = true
          for( var index in this.aSelection )
            bIsSelected = bIsSelected && this.aSelection[index].value == getXPathValue(node, this.aSelection[index].xpath)        
          if( bIsSelected )
          {
            this.selection = item
            item.className = "linkEditListItemSelected"          
          }
        }
        item.linkEdit = this
        item.onclick = function()
        {
          if( this.linkEdit.selection )
            this.linkEdit.selection.className = "linkEditListItem"
          this.className = "linkEditListItemSelected"
          this.linkEdit.selectedItem = this.itemKeys
          this.linkEdit.selection = this
          this.linkEdit.dialog.buttonOk.disabled = false
        }
        
        // Append the associated image before the compute string
        if( this.strEnumImagePath != "" )
        {
          var strImagePath = getXPathValue(node, this.strEnumImagePath + "Img")
          if( strImagePath != "" )
          {
            var img = item.appendChild(document.createElement("img"))
            img.className = "raw"
            img.src = ParseXtkImg(strImagePath)
          }
        }
        
        item.appendChild(document.createTextNode(getXPathValue(node, "@_cs")))
        node = nextSiblingElement(node)
      }
    }
    else if( queryDef.action == "getEntityFolder" )
    {
      var folderLinkId = getXPathValue(xmlResult, this.strFolderLinkId)
      if (!Number(folderLinkId))  
        folderLinkId = 0
      this.iSelectedFolderId = folderLinkId
      this.strSelectedFolderFullName = getXPathValue(xmlResult, this.strFolderLink + "/@fullName")
      this.initNavtree()
      this.loadList()
    }
    
    // hide loading    
    this.dialog.setLoading(false)
  }
  else
    printException(ex)
}

// ---------------------------------------------------------------------------
// onSoapRequestCompleted callback 
// for FindFirstAuthorized
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.onSoapRequestCompleted = function(soapCall, ex)
{
  if( ex == null )
  {
    if( soapCall.strMethodName == "FindFirstAuthorized" )
    { //
      // Get next id of report counter to build a new unique internal name
      //
      var ndResult = soapCall.getNextElement();
      soapCall.checkNoMoreArgs();
      if( ndResult != null )
      { //
        // Get folder data and load content 
        this.strSelectedFolderFullName = ndResult.getAttribute("fullName")
        this.iSelectedFolderId = parseInt(ndResult.getAttribute("id"))
        this.initNavtree()
        this.loadList()
      }
      else if( this.isFolder )
        this.initNavtree()
      
      this.dialog.setLoading(false)
    } 
  }
  else
    printException(ex)
}

// ---------------------------------------------------------------------------
// Call back function called when a folder is selected in full dhtml mode
// [in] xtkNavtree : navtree widget
// [in] xtkEntity : selected folder node
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.onFolderChange = function(xtkNavtree, xtkEntity)
{
  this.iSelectedFolderId = parseInt(getXPathValue(xtkEntity, "@id"))
  if( this.isFolder )
  {
    this.selectedItem = xtkEntity
    this.dialog.buttonOk.disabled = false
  }
  else
    this.loadList()
}

// ---------------------------------------------------------------------------
// render : draw the widget
// [in] htmlParent : where the widget will be added
// [in] strDocumentLabel : type of looked document (displayed in dialog title)
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.render = function(htmlParent, strDocumentLabel)
{
  // Build the input
  var inputTable = document.createElement("table")
  if( this.strInputTableWidth != "" )
    inputTable.style.width = this.strInputTableWidth
  else
    inputTable.style.width = "125px" // default
  inputTable.style.borderCollapse = "collapse"
  var tbody = inputTable.appendChild(document.createElement("tbody"))
  var tr = tbody.appendChild(document.createElement("tr"))
  
  this.input = document.createElement("input")
  this.input.id = this.id
  this.input.name = this.id
  this.input.className = "linkEdit"
  this.input.readOnly = "true"
  this.input.style.width = "100%"
  HTMLHelper.setStyle(this.input, this.inlineStyleValue)
  
  var tdInput = document.createElement("td")
  tdInput.style.width = "100%"
  tdInput.appendChild(this.input)
  tr.appendChild(tdInput)
  
  var tdButton = document.createElement("td")
  tdButton.linkEdit = this
  tdButton.className = "button"
  htmlParent.appendChild(tdButton)
  tdButton.onclick = function()
  {
    this.linkEdit.dialog.show()
  }
  var img = document.createElement("img")
  img.src = "/xtk/img/form/xawchoice.png"
  tdButton.appendChild(img)
  tr.appendChild(tdButton)
  
  htmlParent.appendChild(inputTable)
  
  // Build the dialog
  this.dialog = new HtmlDialog(strDocumentLabel, "OK", this, true)
  this.dialog.buttonOk.disabled = true
  this.dialog.setSize("40em", "auto")
  var contentTable = document.createElement("table")
  contentTable.style.width = "100%"
  var tr = contentTable.appendChild(document.createElement("tbody")).appendChild(document.createElement("tr"))

  // display navtree if needed 
  if( this.strFolderModel != "" || this.isFolder )
  {
    var folderTreeTd = document.createElement("td")
    folderTreeTd.className = "linkEditFolderTree"
    if( this.isFolder )
      folderTreeTd.style.width = "100%"
    tr.appendChild(folderTreeTd)
    var folderTreeDiv = document.createElement("div")
    folderTreeTd.appendChild(folderTreeDiv)
    folderTreeDiv.className = "linkEditFolderTree"
    this.xtkNavigationTree.render(folderTreeDiv)
  }
  
  // display the list to select entity
  if( !this.isFolder )
  {
    var tdFolder = tr.appendChild(document.createElement("td"))
    tdFolder.className = "linkEditList"
    
    this.list = tdFolder.appendChild(document.createElement("div"))
    this.list.className = "linkEditList"
    var loadList = this.list.appendChild(document.createElement("div"))
    loadList.className = "loading"
    loadList.innerHTML = "<div><object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='https://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,65,0' width='48' height='48' id='preload48x48' align='middle'>"+
                         "  <param name='movie' value='/xtk/img/form/classic/preload48x48.swf'/>"+
                         "  <param name='quality' value='high' /><param name='bgcolor' value='#ffffff' />"+
                         "  <embed src='/xtk/img/form/classic/preload48x48.swf' quality='high' bgcolor='#ffffff' width='48' height='48' name='preload48x48' align='middle' type='application/x-shockwave-flash' pluginspage='https://www.macromedia.com/go/getflashplayer'/>"+
                         "</object></div>"
    
    // Add a filter input
    if( this.bUseFiltering )
    {
      this.dialog.addContent(this.filterDiv)
      // Try to give the focus to this input
      try {
        this.filterDiv.input.focus()
      }
      catch(ex) {}
    }
  }
  else
    this.dialog.divContent.style.padding = "0"

  this.dialog.addContent(contentTable)
  this.dialog.titleTable.style.fontSize = "0.8em"
}

// ---------------------------------------------------------------------------
// Start the query to load
// ---------------------------------------------------------------------------
XtkLinkEdit.prototype.loadList = function()
{
  var query = new QueryDef(this.strSchemaId, "select")
  if( this.aJoinPath.length > 0 )
  {
    for(var index in this.aJoinPath )
    {
      var xpath = this.aJoinPath[index]
      query.addSelectExpr("[" + xpath + "]")
    }
  }
  else
    query.addSelectExpr("@id")
  // Retrieve the compute string and order the results against it.
  query.addSelectExpr("[/]")
  query.addOrderByExpr("[/]")
  
  if( this.strEnumImagePath != "" )
  {
    // If images are available, then we add their path with analyze == true
    query.addSelectExpr(this.strEnumImagePath, null, true)
  }
  
  // TODO : bug ID2133
  query.setLineCount(100)
  
  if( this.iSelectedFolderId != 0 && this.strFolderLinkId != "" && this.strFolderModel != "")
    query.addWhereConditionExpr("[" + this.strFolderLinkId + "] = " + this.iSelectedFolderId)
  
  // The sysFilter can contain expressions of the form "$([somePath])" that 
  // need to be evaluated before executing the query as the query WILL fail
  // otherwise.
  if( this.ndSysFilter != null )
  {
    var ndFilter = importNode(this.ndSysFilter, this.ndSysFilter.documentElement, true)
    
    // Local reference for closure
    var that = this
    
    var analyzeExpression = function(ndCondition, strAttribute)
    {
      var expr = ndCondition.getAttribute(strAttribute)
      if( expr )
      {
        var aMatch = expr.match(/\$\(\[(.+)\]\)/)
        
        if( aMatch )
        {
          // An expression containing "$([somePath])" must be updated with the
          // value corresponding to "somePath" in the context, unless
          // "somePath" is equal to "@_filterInput" as this mean we must use
          // what the user typed in the filter input.
          var strReplacement = (aMatch[1] == "@_filterInput") ? that.strFilterValue
                                              : that.controller.getValue(aMatch[1])
          if( typeof strReplacement != "string" || strReplacement == "" )
          {
            if( !ndCondition.getAttribute("enabledIf") )
              ndCondition.setAttribute("enabledIf", "false")
          }
          else
          {
            strReplacement = strReplacement.replace(/\\/g, "\\\\").replace(/'/g, "\\'")
            ndCondition.setAttribute(strAttribute, expr.replace(aMatch[0], strReplacement))
          }
        }
      }
    }
    
    // Callback called on each and every condition of the sysfilter
    var modifyInput = function(ndCondition)
    {
      analyzeExpression(ndCondition, "enabledIf")
      analyzeExpression(ndCondition, "expr")
      
      // Don't stop the recursion to update all the conditions
      return false
    }
    traverseXMLTree(ndFilter, modifyInput)
    
    query.addWhereCondition(ndFilter, "", true)
    query.setExpandParam(true)
  }
  query.action = "loadList"
  query.execute(this.controller.soapRouterUrl, this.controller.getSessionToken(), this)
}

