/****************************************************************************
 * Interwoven Form Builder Toolbox
 *
 * Author: Rex Staples
 * Date:   26-May-2006
 *
 * Copyright 2006 Interwoven, Inc. All Rights Reserved.
 *
 * Interwoven trademarks, service marks, logos, and taglines are
 * exclusively owned by Interwoven, Inc.
 *
 *
 * OVERVIEW
 *
 * The IwFormBuilderToolbox is an object with a two public methods:
 *
 *   submit([form])
 *
 * and
 *
 *   ajaxSubmit()
 *
 * When called, these functions either clone the passed (optional) form
 * or clone the primary form builder toolbox form.
 *
 * They then scan the document for form fields matching the form builder
 * toolbox class name (see the moniker constant below) and append them
 * to the cloned form.
 *
 * If passed a submitButton, its label/value pair are appended to the form
 * clone, and the form is then either submitted or used in an ajax request.
 *
 ****************************************************************************/
var IwFormBuilderToolbox = new Object();


/**
 * Aggregates the form builder toolbox fields into a form and submits it.
 *
 * @param form (optional) the form to submit (instead of primary form)
 */
IwFormBuilderToolbox.submit = function(form)
{
  if (form || IwFormBuilderToolbox.doSubmitFormOnCRLF)
  {
    IwFormBuilderToolbox.getSubmissionForm(form).submit();
  }
  
  return false;
}

/**
 * Resets all the form builder toolbox forms.
 */
IwFormBuilderToolbox.reset = function()
{
  var forms = document.forms;

  for (var i=0; i < forms.length; i++)
  {
    if (forms[i].className.match(IwFormBuilderToolbox.classNameRegExp))
    {
      forms[i].reset();
    }
  }
  
  return false;
}

/**
 * Takes a form and performs an ajax submit on it.
 *
 * @param form (optional) the form to submit (instead of primary form)
 */
IwFormBuilderToolbox.ajaxSubmit = function(form)
{
  if (!form && !IwFormBuilderToolbox.doSubmitFormOnCRLF)
    return false;

  var form = IwFormBuilderToolbox.getSubmissionForm(form, submitButton);
  var callback = function(xml,txt){alert(txt);}; // TODO: do something!
  var req = new AjaxRequest(form.attributes.getNamedItem('action').value, callback)
  req.setParameters(form);
  req.submit();
}


/****************************************************************************
 ***** HELPER FUNCTIONS *****************************************************
 ****************************************************************************/


/**
 * Returns a clone of the form builder toolbox submission primary submission
 * form. The children of the form are assembled from input, select, and
 * textarea tags whose class names match the iw-form-builder-toolbox class.
 *
 * @param formPrototype (optional) form to clone (overrides the primary form)
 * @return a form builder toolbox form ready for submission
 */
IwFormBuilderToolbox.getSubmissionForm = function(formPrototype)
{
  var forms = document.forms;
  var pageform = (formPrototype) ? formPrototype : forms[IwFormBuilderToolbox.moniker];
  var form = pageform.cloneNode(true);
  form.style.display = 'none';

  for (var i=0; i < forms.length; i++)
  {
    if (forms[i].className.match(IwFormBuilderToolbox.classNameRegExp))
    {
      var fields = forms[i].elements;
      
      for (var j=0; j<fields.length; j++)
      {
        if (fields[j].disabled) continue;
        IwFormBuilderToolbox.appendFormField(form, fields[j]);
      }

    }
  }

  document.body.appendChild(form);
  return form;
}


/**
 * Appends to the form one or more hidden fields with name/value pairs from
 * the passed form field.
 *
 * @param form the form to which the form field is appended
 * @param formField the field whose value(s) are appeneded to the form
 */
IwFormBuilderToolbox.appendFormField = function(form, formField)
{
  switch(formField.type)
  {
    case AjaxRequest.FORM_FIELD_TEXT:
    case AjaxRequest.FORM_FIELD_PASSWORD:
    case AjaxRequest.FORM_FIELD_HIDDEN:
    case AjaxRequest.FORM_FIELD_TEXTAREA:
      IwFormBuilderToolbox.appendHiddenField(form, formField.name, formField.value);
      break;
    
    case AjaxRequest.FORM_FIELD_RADIO:
    case AjaxRequest.FORM_FIELD_CHECKBOX:
      if (formField.checked)
      {
        IwFormBuilderToolbox.appendHiddenField(form, formField.name, formField.value);
      }
      break;
    
    case AjaxRequest.FORM_FIELD_SELECT_ONE:
    case AjaxRequest.FORM_FIELD_SELECT_MULTIPLE:
      var options = formField.options;

      for (var j=0; j < options.length; j++)
      {
        if (options[j].selected)
        {
          IwFormBuilderToolbox.appendHiddenField(form, formField.name, options[j].value);
        }
      }
      break;
  }
}


/**
 * Appends to the form a hidden field with the passed name/value pair.
 *
 * @param form the form to which the hidden field is appended
 * @param name the name of the hidden form field
 * @param value the value of the hidden form field
 */
IwFormBuilderToolbox.appendHiddenField = function(form, name, value)
{
  var input;

  if (IwFormBuilderToolbox.isIE) // MSIE bug
  {
    input = document.createElement('<input name="'+name+'">');
  }
  else
  {
    input = document.createElement('input');
    input.name = name;
  }

  input.type = 'hidden';
  input.value = value;
  form.appendChild(input);
}

/**
 * Autofits the labels/fields in the named form using the widest form
 * field.
 *
 * @param formId the id of the form to autofit
 */
IwFormBuilderToolbox.autoFitFormFields = function(formId)
{
  var form = document.forms[formId];

  var divs = form.getElementsByTagName('div');
  var size = 0;
  var RPAD = 10; // right padding for the label

  for (var i=0; i<divs.length; i++)
  {
    if (divs[i].className.match(/\bform-label\b/))
    {
      size = Math.max(size, IwFormBuilderToolbox.getOffsetWidth(divs[i]));
    }
  }

  document.write(IwFormBuilderToolbox.autoFitCss
    .replace(/%ID%/g, formId).replace(/%WIDTH%/g, RPAD+size));

  if (IwFormBuilderToolbox.isIE)
  {
    // MSIE loses the scrollbars upon typing in a textarea
    // whose width is 100% (defined in the css stylesheet),
    // so set the width explicitly from the offset width
    // while focused and remove the width when blurred.
    var tags = form.getElementsByTagName('textarea');
    for (var i=0; i<tags.length; i++)
    {
      tags[i].onfocus = IwFormBuilderToolbox.setTextareaWidthMSIE;
      tags[i].onblur = IwFormBuilderToolbox.setTextareaWidthMSIE;
    }
    
    // MSIE will not auto-fit if inside an iframe
    if (top != self)
    {
      IwFormBuilderToolbox.repaintMSIE(formId);
    }
  }
}


/**
 * Gets the offset width of the passed element. MSIE reports an offset
 * width of zero if nested within a table (e.g. tabled layout). To get
 * around this the passed element's content is inserted into a node
 * outside of the nested table and calculated.
 *
 * @param obj the element to whose width is measured
 */
IwFormBuilderToolbox.getOffsetWidth = function(obj)
{
  if ('' == obj.innerHTML) return 0;
  
  var width = obj.offsetWidth;
  
  if (!width)
  {
    var offsetNode = IwFormBuilderToolbox.getOffsetCalculationNode();
    offsetNode.innerHTML = obj.innerHTML;
    width = offsetNode.offsetWidth;
  }
  
  return width;
}


/**
 * Gets an invisible node outside any table structure used to calculate
 * an offset width.
 */
IwFormBuilderToolbox.getOffsetCalculationNode = function()
{
  var id = 'iw-offset-calculation-node';
  var node = document.getElementById(id);
  
  if (!node)
  {
    node = document.createElement('div');
    node.style.visibility = 'hidden';
    node.style.position = 'absolute';
    node.id = id;

    if (document.body.firstChild)
    {
      document.body.insertBefore(node, document.body.firstChild);
    }
    else
    {
      document.body.appendChild(node);
    }
  }
  
  return node;
}


/**
 * MSIE will size a textarea such that the right border and scrollbars
 * disappear once a user starts typing if the textarea width is 100%.
 */
IwFormBuilderToolbox.setTextareaWidthMSIE = function()
{
  this.style.width = ('blur' == window.event.type) ? '' : this.offsetWidth;
}


/**
 * MSIE has a painting bug where auto-fit form fields will not adjust if
 * rendered within an iframe.  This function jiggles the form padding to
 * cause the iframe to repaint correctly.
 *
 * @param formId the id of the form to jiggle
 * @param doRestore whether to restore the form to its original state
 */
IwFormBuilderToolbox.repaintMSIE = function (formId, doRestore)
{
  var form = document.forms[formId];
  
  if (doRestore)
  {
    form.style.paddingRight = '';
  }
  else
  {
    form.style.paddingRight = '1px';
    setTimeout('IwFormBuilderToolbox.repaintMSIE("'+formId+'",true);',1);
  }
}

/****************************************************************************
 ***** CONSTANTS ************************************************************
 ****************************************************************************/

/**
 * unique moniker for the form builder toolbox
 */
IwFormBuilderToolbox.moniker = 'iw-form-builder-toolbox';

/**
 * regular expression that identifies a form builder toolbox field
 */
IwFormBuilderToolbox.classNameRegExp = new RegExp('\\b' + IwFormBuilderToolbox.moniker + '\\b');

/**
 * true if this is a MSIE browser
 */
IwFormBuilderToolbox.isIE = (navigator.userAgent.indexOf('MSIE') != -1);

/**
 * Auto-fit css template (replace form id and width tokens)
 */
IwFormBuilderToolbox.autoFitCss = '<style type="text/css">'
  + 'form#%ID% .form-field div.form-label {width:%WIDTH%;}'
  + 'form#%ID% .form-field div.form-input {margin-left:-%WIDTH%;}'
  + 'form#%ID% .form-field div.form-input div {margin-left:%WIDTH%;}'
  + '</style>';

/**
 * whether pressing CRLF in a text input should submit to the primary form
 */
IwFormBuilderToolbox.doSubmitFormOnCRLF = false;
