var widget = {};

widget.colorPickers = {};

widget.ColorPicker = Class.create();

widget.ColorPicker.prototype = {
    /**
     * Constructor
     *
     * @param name - id of the color picker
     * @param initialColor - initial color hex string ("#000000")
     * @param alertMsg - localized error msg string ("Error: A valid color value must be specified")
     * @param previewIdStr - optional id string of the color preview DIV container user can specify
     */
    initialize: function(name, initialColor, alertMsg, previewIdStr, previewBackgroundIdStr)
    {
        this.theColorPickerName = name;
        this.theMenu = $(name);
        this.alertMsg = alertMsg;
        this.userPreviewContainer = $(previewIdStr); // may be undefined
        this.userPreviewBackgroundContainer = $(previewBackgroundIdStr); // may be undefined
        if (typeof this.userPreviewContainer == "undefined") {
          this.userPreviewContainer = null;
        }
        if (typeof this.userPreviewBackgroundContainer == "undefined") {
          this.userPreviewBackgroundContainer = null;
        }
        this.colorListParent = $(name + '_colorlist_parent');

        widget.ColorPicker.registerColorPicker( this );

        this.ie = document.all && navigator.userAgent.indexOf("Opera") == -1;
        this.color_hidden_input = $(name + "_title_color_input");
        this.color_hidden_input.value = initialColor;
        this.color_hidden_input._defaultValue = initialColor;
        this.anch_link = $(name + "_link");
        this.color_link = $(name + "_img");
        this.color_name_label = $(name + "_color_name_label");
        this.title_color_input = $(name + "_title_color");
        this.setDisplay(initialColor, this.getColorByValue(initialColor,false));
        this.color_name_label.innerHTML = this.getColorByValue(initialColor,true);
        this.color_link.setStyle({background:initialColor});
        this.updateUserPreviewContainerColor(initialColor);
        if ( this.anch_link )
        {
            Event.observe(this.anch_link, "click", this.onOpen.bindAsEventListener(this));
        }
        Event.observe(document.body, "click", this.closeColorPickers.bindAsEventListener(this));
        Event.observe( this.theMenu , 'keydown', this.onKeyPress.bindAsEventListener( this ) );
        Event.observe( this.theColorPickerName + '_previewColorLink', 'click', this.previewListener.bindAsEventListener(this));
        Event.observe( this.theColorPickerName + '_cancelButton', 'click', this.onClose.bindAsEventListener(this));
        Event.observe( this.theColorPickerName + '_submitButton', 'click', this.onApply.bindAsEventListener(this));

        this.skip_link = $(name + "_skip_link");
        if ( this.skip_link )
        {
          Event.observe(this.skip_link, "click", this.onSkipLinkClick.bindAsEventListener(this));
        }

        // relocate color picker popup div to the bottom of the doc b/c of css display issues
        Element.remove( this.theMenu );
        document.body.appendChild( this.theMenu );

        widget.colorPickers[ this.theColorPickerName ] = this;
    },

    onSkipLinkClick:function(event)
    {
     $(this.theColorPickerName + '_submitButton').focus();
    },

    onKeyPress: function( event )
    {
      var key = event.keyCode || event.which;
      if ( key == Event.KEY_ESC )
      {
        this.onClose(event);
      }
    },

    closeColorPickers:function(event)
    {
      var element = Event.element( event );
      widget.ColorPicker.colorPickers.each(function(cp)
       {
         if ( cp != this && !element.descendantOf( cp.theMenu ) )
         {
           cp.close();
         }
       }.bind(this));

    },
    setOnChangeHandler: function( handler )
    {
      this.onChangeHandler = handler;
    },
    setDefaultColor: function( colorValue )
    {
        this.color_hidden_input.value = colorValue;
        this.color_hidden_input._defaultValue = colorValue;
        this.color_link.setStyle({backgroundColor: colorValue});
        this.setDisplay(colorValue, this.getColorByValue(colorValue,false));
        this.color_name_label.innerHTML = this.getColorByValue(colorValue,true);
    },
    setColor:function(colorValue)
    {
    	document.body.appendChild( widget.ColorPicker.colorPalette );
        widget.ShowUnsavedChanges.changeHiddenValue( this.color_hidden_input, colorValue );
        this.color_link.setStyle({backgroundColor: colorValue});
        this.setDisplay(colorValue, this.getColorByValue(colorValue,false));
        this.color_name_label.innerHTML = this.getColorByValue(colorValue,true);
        this.updateUserPreviewContainerColor(colorValue);
	    if ( this.onChangeHandler )
	    {
	      this.onChangeHandler();
	    }
	    widget.ColorPicker.colorPalette.remove();
    },

    getColorByValue:function(colorValue,onlyColorName)
    {
        var newValue = 'color_' + colorValue.substring(1, colorValue.length);
        e = $(newValue);
        var text = "";
        if (e != null)
        {
            text = this.getDisplayColorInfo(e.innerHTML,onlyColorName);
        }
        return text;
    },


    onOpen:function(event)
    {
      this.closeColorPickers(event);      
      this.colorListParent.appendChild( widget.ColorPicker.colorPalette );
      Event.observe( widget.ColorPicker.colorPalette, 'click', this.setColorFromSwatch.bindAsEventListener(this));
      var offset = Position.cumulativeOffset( this.anch_link );
      this.theMenu.setStyle({display: "block", position: "absolute"});
      var width = this.theMenu.getWidth();
      var bodyWidth = $(document.body).getWidth();

      var menuDims = this.theMenu.getDimensionsEx();
      var anchorDims = this.anch_link.getDimensionsEx();
      var menuHeight = menuDims.height;
      var viewportDimensions = document.viewport.getDimensions();
      var scrollOffsets = document.viewport.getScrollOffsets();
      // Use viewport relative offsets to figure out placement within the view
      var offsetTop = offset[1] - scrollOffsets.top;
      var offsetLeft = offset[0] - scrollOffsets.left;

      var xpos = offsetLeft;
      if ( page.util.isRTL() )
      {
        xpos = xpos + this.anch_link.getWidth() - width;
      }
      if ( xpos + width > viewportDimensions.width )
      {
        // First try to position it on the left of the link instead of the right
        xpos = offsetLeft - width + anchorDims.width;
        if (xpos < 0)
        {
          // If we go off the page though, just try our best
          xpos = viewportDimensions.width - width;
        }
      }
      if (xpos < 0)
      {
        xpos = 0;
      }
      var ypos = offsetTop + anchorDims.height;
      if ((ypos + menuHeight) > viewportDimensions.height)
      {
        // Try to position above the color picker first
        ypos = offsetTop-menuHeight;
        if (ypos < 0)
        {
          // but if it goes off the top of the page just do a best-effort.
          ypos = ypos - (ypos + menuHeight - viewportDimensions.height);
          if (ypos < 0)
          {
            ypos = 0;
          }
        }
      }
      // The color swatch is positioned absolutely, so adjust offsets to include the scrollofset
      ypos = ypos + scrollOffsets.top;
      xpos = xpos + scrollOffsets.left;
      
      this.theMenu.setStyle({ left: xpos + "px", top: ypos + "px"});
      this.title_color_input.focus();
      (function() {
        if (!this.shim) this.shim = new page.popupShim( this.theMenu );
        this.shim.open();
      }).bind(this).defer();
      Event.stop(event);
    },

    /**
     * Hides the color picker.
     */
    onClose: function()
    {
      this.close();
      this.anch_link.focus();
    },

    close: function()
    {
      Event.stopObserving( widget.ColorPicker.colorPalette, 'click' );
      Element.remove(widget.ColorPicker.colorPalette);
      this.theMenu.setStyle({display: "none"});
      if ( this.shim ) this.shim.close();
    },

    /**
     * Sets the preview color and text to the color selected by the user in the palette
     */
    setColorFromSwatch:function(event)
    {
        var text = Event.element(event).childNodes[0].nodeValue;
        if (text)
        {
          this.setDisplay(text.substring(0, 7), this.getDisplayColorInfo(text,false));
        }
        Event.stop( event );
    },

    getDisplayColorInfo:function(nodeValue,onlyColorName)
    {
        var hex, hexname;
        if (nodeValue == null || nodeValue == "") return "";
        hex = nodeValue.substring(0, 7);
        hexname = nodeValue.substring(8);
        colorName = hexname + ' (' + hex + ')';
        if(onlyColorName)
        {
           colorName = hexname;
        }
        return colorName;
    },

    /**
     * Handles the Preview functionality on the swatch. If an inappropriate color is choosen, a msg is displayed to the user.
     * Else updates the color preview with the manually entered color.
     */
    previewListener:function(event)
    {
        if (this.isValidColor(this.title_color_input.value) == false)
        {
            alert(  this.alertMsg );
            return;
        }
        if (this.title_color_input.value.length != 6)
        {
            return;
        }
        var prevCol = '#' + this.title_color_input.value
        this.setDisplay(prevCol, this.getColorByValue(prevCol,false));
        Event.stop(event);
    },

    /**
     * Updates the color preview panel in the picker pop up
     */
    setDisplay:function(col, text)
    {
        $(this.theColorPickerName+'_swatchpreview').setStyle({backgroundColor:col});
        this.title_color_input.value = (col.startsWith('#')) ? col.substr(1) : col;
        $(this.theColorPickerName+'_title_color_name').innerHTML = text;
        $(this.theColorPickerName+'_color_text').setStyle({color:col});
    },

    /**
     * Checks for a valid color value.
     */
    isValidColor:function(color_value)
    {
        if (! /^[0-9A-Fa-f]{6}/.test(color_value))
        {
            return false;
        }
        else
        {
            return true;
        }
    },

    /**
     * Manages the functionality for the Apply button the swatch.
     */
    onApply:function(event)
    {
      var color = this.title_color_input.value;
      if (this.isValidColor(color) == false)
      {
        return;
      }
      color = '#' + color;
      this.color_link.setStyle({background:color});
      widget.ShowUnsavedChanges.changeHiddenValue( this.color_hidden_input, color );
      this.color_name_label.innerHTML = this.getColorByValue(color,true);
      this.updateUserPreviewContainerColor(color);
      this.onClose();
  if ( this.onChangeHandler )
  {
    this.onChangeHandler();
  }
      Event.stop(event);
    },

    /**
     * If the user specified preview container element exists,
     * update its color style to the color given.
     *
     * @param color - color hex string
     */
    updateUserPreviewContainerColor:function( color )
    {
      if( this.userPreviewContainer )
      {
        this.userPreviewContainer.style.color = color;
      }
      else if( this.userPreviewBackgroundContainer )
      {
        this.userPreviewBackgroundContainer.style.background = color;
      }
    }

}; //end widget.ColorPicker.prototype

widget.ColorPicker.colorPickers = [];
widget.ColorPicker.colorPickerMap = {}


widget.ColorPicker.registerColorPicker = function(cp)
{
    widget.ColorPicker.colorPickers.push(cp);
    widget.ColorPicker.colorPickerMap[cp.theColorPickerName] = cp;
    // grab the color definition and remove it from the DOM for the time being
    if ( !widget.ColorPicker.colorPalette )
    {
      var colorPalette = $('picker_colorlist');
      widget.ColorPicker.colorPalette = colorPalette;
      // deferring so that the name lookup for colors can succeed during init
      (function() { Element.remove( colorPalette ); }.defer());
    }
};

widget.ColorPicker.closeAllColorPickers = function()
{
    widget.ColorPicker.colorPickers.each(function(cp)
    {
      cp.close();
    });
};

widget.MultiSelect = Class.create()

widget.MultiSelect.multiselectBoxes = [];

// ---------------- "static" methods

widget.MultiSelect.registerMultiSelect = function(ms)
{
    widget.MultiSelect.multiselectBoxes.push(ms);
};

widget.MultiSelect.unRegisterMultiSelect = function(ms)
{
    widget.MultiSelect.multiselectBoxes.remove(ms);
};


widget.MultiSelect.prototype =
{
    initialize: function(multiSelectDiv, formName)
    {
        this.multiSelectDiv = $(multiSelectDiv);
    this.formName = formName;

        if( this.multiSelectDiv != null )
        {
          this.leftClickListeners = new Array();
          this.rightClickListeners = new Array();
          var divs = this.multiSelectDiv.getElementsByTagName('div');
          var leftDiv = $(divs[0]);
          var buttonDiv = $(divs[1]);
          var rightDiv = $(divs[2]);

          var inputs = this.multiSelectDiv.getElementsByTagName('input')
          this.leftValues = $(inputs[0]);
          this.rightValues = $(inputs[1]);
          var leftSelects = leftDiv.getElementsByTagName('select');
          var rightSelects = rightDiv.getElementsByTagName('select');
          this.leftSelectBox = $(leftSelects[0]);
          this.rightSelectBox = $(rightSelects[0]);

          var buttons = buttonDiv.getElementsByTagName('button');
          var moveRightButton = $(buttons[0]);
          var moveLeftButton = $(buttons[1]);
          var leftInputs = leftDiv.getElementsByTagName('input');
          var leftInvertSelectionButton = $(leftInputs[0]);
          var leftSelectAllButton = $(leftInputs[1]);
          var rightInputs = rightDiv.getElementsByTagName('input');
          var rightInvertSelectionButton = $(rightInputs[0]);
          var rightSelectAllButton = $(rightInputs[1]);
          widget.MultiSelect.registerMultiSelect( this );
          Event.observe(moveLeftButton, "click", this.onMoveLeftClick.bindAsEventListener(this));
          Event.observe(moveRightButton, "click", this.onMoveRightClick.bindAsEventListener(this));
          Event.observe(leftInvertSelectionButton, "click", this.onInvertSelection.bindAsEventListener(this, true));
          Event.observe(rightInvertSelectionButton, "click", this.onInvertSelection.bindAsEventListener(this, false));
          Event.observe(leftSelectAllButton, "click", this.onSelectAllClick.bindAsEventListener(this, true));
          Event.observe(rightSelectAllButton, "click", this.onSelectAllClick.bindAsEventListener(this, false));


      // This for the MultiSelectAction Bean.
      //If actionBean list is less than 1, button is displayed and not the dropdown
      if ( leftSelects.length > 1 )
      {
            this.actionBeanLeftSelect = $(leftSelects[1]);
            this.actionBeanRightSelect = $(rightSelects[1]);
              var actionBeanLeftGoBox = $(leftInputs[2]);
            var actionBeanRightGoBox = $(leftInputs[2]);
            Event.observe( actionBeanLeftGoBox, "click", this.actionBeanFunc.bindAsEventListener(this, true));
            Event.observe( actionBeanRightGoBox, "click", this.actionBeanFunc.bindAsEventListener(this, false));
          }
        }
    },
    actionBeanFunc: function (event, leftSelect ) {
      var destination = this.actionBeanLeftSelect ;
      var selectedBox = this.leftSelectBox;
      if ( !leftSelect )
      {
        destination = this.actionBeanRightSelect ;
        selectedBox = this.rightSelectBox ;
      }
      var myindex=destination.selectedIndex;
      if( myindex == -1 || myindex > destination.length-1)
      {
        alert(msg);
        return false;
      }
      var sFunctionName = destination.options[myindex].value;
      var oFunc = window[sFunctionName];
      oFunc(selectedBox.name, this.formName);
      this.setHiddenValues();
    },
    /*
     *  listener registration for leftmove and rightmove clicks
     */
     addToLeftClickListener: function(listenerFunc) {
       if(this.leftClickListeners != null)
    this.leftClickListeners.push(listenerFunc);
  },

  addToRightClickListener: function(listenerFunc) {
    if(this.rightClickListeners != null)
    this.rightClickListeners.push(listenerFunc);
  },

   removeFromLeftClickListener: function(listenerFunc) {
     if(this.leftClickListeners != null)
    this.leftClickListeners.remove(listenerFunc);
  },

  removeFromRightClickListener: function(listenerFunc) {
    if(this.rightClickListeners != null)
    this.rightClickListeners.remove(listenerFunc);
  },

    removeAllLeftClickListeners: function() {
    this.leftClickListeners =[];
  },

  removeAllRightClickListeners: function() {
    this.rightClickListeners =[];
  },

     removeFromLeft: function(id){
       if(this.leftSelectBox != null){
       var leftItems = this.leftSelectBox.immediateDescendants();
       var toRemoveElement = leftItems.find(function(item){
         return !item.selected && item.value == id;
       });
       if(toRemoveElement != null)
       {
          Element.remove(toRemoveElement);
        this.setHiddenValues();
      }
       }
     },

   addToLeft: function(id, value){
   if(this.leftSelectBox != null){
      var leftItems = this.leftSelectBox.immediateDescendants();
      var returnItem = leftItems.find(function(item){
         return item.value == id;
       });
     exists = (returnItem != null) ? true : false;
     if(!exists)
     {
       this.rawAddToLeft( id, value );
       this.setHiddenValues();
     }
   }
   },

   rawAddToLeft: function(id, value) {
    var item = document.createElement("option");
      item.value = id;
      item.innerHTML = value;
      this.leftSelectBox.appendChild(item);
   },



     removeFromRight: function(id){
         if(this.rightSelectBox != null){
       var rightItems = this.rightSelectBox.immediateDescendants();
       var toRemoveElement = rightItems.find(function(item){
         return !item.selected && item.value== id;
       });
       if(toRemoveElement != null)
       {
         Element.remove(toRemoveElement);
         this.setHiddenValues();
      }
      }
     },

   addToRight: function(id, value){
     if(this.rightSelectBox != null){
      var rightItems = this.rightSelectBox.immediateDescendants();
      var returnItem  = rightItems.find(function(item){
         return item.value == id;
       });
      exists = (returnItem != null) ? true : false;
      if(!exists)
     {
         var item = document.createElement("option");
           item.value = id;
           item.innerHTML = value;
           this.rightSelectBox.appendChild(item);
           this.setHiddenValues();
    }
     }
    },

   resetRightBox: function( ){
     if(this.rightSelectBox != null){
       var rightItems = this.rightSelectBox.immediateDescendants();
       rightItems.invoke("remove");
       this.setHiddenValues();
     }
    },

    resetLeftBox: function( ){
      if(this.leftSelectBox != null){
       var leftItems = this.leftSelectBox.immediateDescendants();
       leftItems.invoke("remove");
       this.setHiddenValues();
      }
    },

    getAllLeftAvailableElements : function(){
      if(this.leftSelectBox != null)
      return this.leftSelectBox.immediateDescendants();
      else
      return [];
    },

    getAllRightElements : function(){
       if(this.rightSelectBox != null)
       return this.rightSelectBox.immediateDescendants();
       else
       return [];
    },

    onMoveLeftClick: function(event)
    {

        var rightItems = this.rightSelectBox.immediateDescendants();
        var selectedItems = rightItems.findAll(function(item)
        {
            return item.selected;
        });
        if ( selectedItems == null || selectedItems.length == 0 )
        {
          alert(page.bundle.getString("admin.manageuserlists.selectionwarning"));
          return;
         }
        selectedItems.invoke("remove");
        var lastItem = null;
        selectedItems.each(function (item)
        {
            this.leftSelectBox.insertBefore(item, lastItem);
            lastItem = item;
        }.bind(this));
        this.setHiddenValues();
        if( this.leftClickListeners)
      {
         this.leftClickListeners.each( function ( listenerFunc ) {
           listenerFunc.apply( this, selectedItems )
         }.bind(this) );

      }

    },

    onMoveRightClick: function(event)
    {

        var leftItems = this.leftSelectBox.immediateDescendants();
        var selectedItems = leftItems.findAll(function(item)
        {
            return item.selected;
        });
        if ( selectedItems == null || selectedItems.length == 0 )
        {
          alert(page.bundle.getString("admin.manageuserlists.selectionwarning"));
          return;
         }
        selectedItems.invoke("remove");
        var lastItem = null;
        selectedItems.each(function (item)
        {
            this.rightSelectBox.insertBefore(item, lastItem);
            lastItem = item;
        }.bind(this));
        this.setHiddenValues();
        if(this.rightClickListeners)
        {
         this.rightClickListeners.each( function ( listenerFunc ) {
           listenerFunc.apply( this, selectedItems )
         }.bind(this) );
        }
    },

    setHiddenValues: function()
    {

        var leftString;
        var rightString;
        var temp = "";

        //Populate select box values comma separated into leftString,rightString.
        if(this.leftSelectBox != null ){
          this.leftSelectBox.immediateDescendants().each(function(option)
          {
              leftString = option.value;
              temp += leftString + ",";
          })
          temp = temp.substring(0, temp.length - 1);
          temp = temp.replace(/^\s*|\s*$/g, "");
          this.leftValues.value = temp;
          temp = "";
        }
        if(this.rightSelectBox != null ){
          this.rightSelectBox.immediateDescendants().each(function(option)
          {
              rightString = option.value;
              temp += rightString + ",";
          })
          temp = temp.substring(0, temp.length - 1);
          temp = temp.replace(/^\s*|\s*$/g, "");
          this.rightValues.value = temp;
          temp = "";
        }
    },

    onSelectAllClick: function(event, isLeft)
    {
        if (isLeft)
        {
            this.leftSelectBox.immediateDescendants().each(function(option)
            {
                option.selected = true;
            });
        }
        else
        {
            this.rightSelectBox.immediateDescendants().each(function(option)
            {
                option.selected = true;
            });
        }
        Event.stop(event);
    },

    onInvertSelection: function(event, isLeft)
    {
        if (isLeft)
        {
            this.leftSelectBox.immediateDescendants().each(function(option)
            {
                option.selected = !option.selected;
            });
        }
        else
        {
            this.rightSelectBox.immediateDescendants().each(function(option)
            {
                option.selected = !option.selected;
            });
        }
    }
}

/**
 * A dynamic picker list
 */
widget.PickerList = Class.create();
widget.PickerList.prototype = {
    /**
     * Creates a new picker list
     * @param id id of the picker list table
     * @param cellGenerators an array of functions to be called to generate the HTML for the cells for
     *        a new row in the table.
     * @param columnAlignments an array of the alignments of the columns
     * @param columnStyles optional styles to applied to columns
     * @param reorderable whether the table is to be reorderable
     * @param reorderingUrl url that reordering changes will be persisted to (can be null)
     * @param contextParameters parameters that are passed along with the reordering action.
     */
    initialize: function( id, cellGenerators, columnAlignments, columnStyles, reorderable, reorderingUrl, contextParameters )
    {
        this.table = $(id);
        this.reorderable = reorderable;
        if ( this.table )
        {
            this.tableBody = $(this.table.getElementsByTagName('tbody')[0]);
            if ( reorderable )
            {
              this.dragDrop = new dragdrop.ListReordering( this.tableBody, this.table.id + '_reorderControls', false, 'tr', 'dndHandle', 'span', reorderingUrl, contextParameters, new Date().getTime(), null );
            }
        }
        this.cellGenerators = cellGenerators;
        this.columnAlignments = columnAlignments;
        this.columnStyles = columnStyles;
    },

    /**
     * This function takes any number of arguments.  They will be passed
     * directly to the generator functions.
     *
     * If this list is reorderable, the first two arguments must be:
     *  - id of the item added.
     *  - name of the item added.
     */
    addRow: function()
    {
        var generatorArgs = arguments;
        var row = $(document.createElement("tr"));
        if ( this.reorderable )
        {
           dragdrop.ListReordering.addDivs(); //Temporarily add the accessible controls to the dom
           var id = arguments[0];
           var name = arguments[1];
           //Set the row id so that the drag and drop code can identify the row
           row.id = this.table.id + '_row:' + id;
           //Add the reordering handle.
           var cell = document.createElement("td");
           cell.className = 'smallCell dndHandle';
           cell.valign = 'top';
           cell.innerHTML = '<span class="reorder"><img src="/images/ci/icons/generic_updown.gif" alt="" /></span>';
           row.appendChild( cell );
           //Add the item to the accessible controls.
           var accessibleSelect = $(this.table.id + "_reorderControlsSelect");
           accessibleSelect.options[accessibleSelect.length] = new Option( name, id );
           dragdrop.ListReordering.removeDivs(); //Remove the accessible controls from the dom again.
        }
        this.cellGenerators.each( function( generator, index )
        {
            var alignment  = this.columnAlignments[index];
            var cell = $( document.createElement( "td" ) );
            cell.setAttribute("align", alignment);
            var columnStyle = this.columnStyles[index];
            if ( '' != columnStyle )
              cell.addClassName( columnStyle );
            cell.innerHTML = generator.apply( window, generatorArgs );
            row.appendChild( cell );
        }.bind(this));
        this.tableBody.appendChild( row );
        if ( this.reorderable )
        {
            //Toggle drag and drop so that it picks up the new item.
            this.dragDrop.disableDragAndDrop();
            this.dragDrop.enableDragAndDrop();
            this.dragDrop.calculateItemOrder();
        }
        return row;
    },
    /**
     * Removes the row with the specified id or index
     * @param idOrIndex
     * - if the argument is a string - it is taken as the id of one of the rows in the table
     * - if the argument is a number - the row at the specified index will be removed.
     */
    removeRow: function( idOrIndex )
    {
      var rowToRemove = null;
      if ( Object.isString( idOrIndex ) )
      {
        rowToRemove = $(this.table.id + "_row:" + idOrIndex);
      }
      else
      {
        rowToRemove = this.tableBody.childElements()[idOrIndex];
      }
      if ( rowToRemove )
      {
        //Remove the row from the accessible repositioning controls if applicable
        if ( this.reorderable )
        {
          dragdrop.ListReordering.addDivs(); //Temporarily add the accessible controls to the dom
          var idToCheck = rowToRemove.id.split(':')[1];
          var accessibleSelect = $(this.table.id + "_reorderControlsSelect");
          var options = accessibleSelect.childElements();
          for ( var i = 0; i < options.length; i++ )
          {
            if ( options[i].value == idToCheck )
            {
              Element.remove( options[i] );
              break;
            }
          }
          dragdrop.ListReordering.removeDivs(); //Remove the accessible controls from the dom again.
        }
        Element.remove( rowToRemove );
        if ( this.reorderable )
        {
          this.dragDrop.calculateItemOrder();
        }
      }
    }
}
widget.PickerList.noopGenerator = function()
{
  return '&nbsp;';
}

/**
 *  File picker
 */
widget.FilePicker = Class.create();
widget.FilePicker.prototype = {
    /**
     * Creates a new file picker
     * @param pickerList the javascript object representing the picker list (of currently attached files)
     * @param baseElementName the base name for the file picker elements
     * @param required whether a file is required to be chosen
     * @param overrideLocalBehavior whether the CS options to override local behavior should be shown
     * @param csPickerUrl url to the CS file picker
     * @param showAddMetadata whether the "submit and add metadata" content system functionality should be enabled.
     * @param onAttachFile Optional callback function triggered when a new file is attached
     */
    initialize: function( pickerList, baseElementName, required, overrideLocalBehavior, csPickerUrl, showAddMetadata, showSpecialAction, allowMultipleFiles, onAttachFile )
    {
        this.pickerList = pickerList;
        this.baseElementName = baseElementName;
        this.required = required;
        this.overrideLocalBehavior = overrideLocalBehavior;
        this.csPickerUrl = csPickerUrl;
        this.showAddMetadata = showAddMetadata;
        this.showSpecialAction = showSpecialAction;
        this.allowMultipleFiles = allowMultipleFiles;
        this.onAttachFile = onAttachFile;

        this.pickCSButton = $( this.baseElementName + '_csBrowse' );
        this.pickLocalButton = $( this.baseElementName + '_localBrowse');
        this.localFilePicker = $( this.baseElementName + '_chooseLocalFile');
        
        this.pickURLButton = $( this.baseElementName + '_urlBrowse');
        
        this.selectedCSFile = $( this.baseElementName + '_selectedCSFile' );
        this.selectedCSFileName = $( this.baseElementName + '_selectedCSFileName' );
        this.selectedCSFilePath = $( this.baseElementName + '_selectedCSFilePath' );
        this.selectedCSFileSize = $( this.baseElementName + '_selectedCSFileSize' );
        if ( this.showAddMetadata )
        {
          this.selectedCSFileMetadata = $( this.baseElementName + '_selectedCSFileMetadata' );
          this.selectedCSFileMetadataSync = $( this.baseElementName + '_selectedCSFileMetadataSync');
          this.selectedCSFileMetadataFormat = $( this.baseElementName + '_selectedCSFileMetadataFormat');
        }
        
        // single file selected area
        this.selectedFileActionsArea = $( this.baseElementName + '_selectedFileActions');
        this.selectedFileName = $( this.baseElementName + '_selectedFileName');
        this.selectedFileSource = $( this.baseElementName + '_selectedFileSource');
        this.selectedFileLinkTitle = $( this.baseElementName + '_selectedFileLinkTitle');
        this.selectedFileSpecialAction = $( this.baseElementName + '_selectedFileSpecialAction');
        this.attachFileButton = $( this.baseElementName + '_attachFileButton' );
        this.cancelFileButton = $( this.baseElementName + '_cancelFileButton' );

        this.allowCS = (this.pickCSButton != null);
        this.allowLocal = (this.pickLocalButton != null);
        this.allowURL = (this.pickURLButton != null);
        
        // Wire up events
        if( this.overrideLocalBehavior )
        {
            this.pickTargetButton = $( this.baseElementName + '_CSTargetButton' );
            this.targetCSLocation = $( this.baseElementName + '_CSTarget' );
            Event.observe( this.pickTargetButton, "click", this.onPickTargetClick.bindAsEventListener( this ) );
        }

        this.pickMDButton = $( this.baseElementName + '_CSTargetMetaButton' );
        if( this.pickMDButton )
        {
            CSMetadataPickerCallBack = this.afterPickMD.bind( this );
            Event.observe( this.pickMDButton, "click", this.onPickMDClick.bindAsEventListener( this ) );
        }
        if( $(this.baseElementName + '_permissionPickerButton0') )
        {
          Event.observe( $(this.baseElementName + '_permissionPickerButton0'), "click", 
                         widget.FilePicker.openPermPicker.bindAsEventListener(this, this.csPickerUrl, this.baseElementName + "_permissions0_manual", "", this.selectedCSFilePath ));
        }
        if( this.allowCS )
        {
            Event.observe( this.pickCSButton, "click", this.onCSBrowse.bindAsEventListener( this ) );
            Event.observe( this.selectedCSFile, "change", this.onCSPick.bindAsEventListener( this ) );
        }
        if( this.allowLocal )
        {
            this.localFileContainer = this.localFilePicker.up();
            Event.observe( this.localFilePicker, "change", this.onLocalPick.bindAsEventListener( this ) );
        }
        if (this.allowURL)
        {
          this.linkId = $(this.baseElementName + "_urlBrowse");
          this.formDivId = $(this.baseElementName + "_addUrlForm");
          this.formContainerId = $(this.baseElementName + "_urlContainerDiv");
            var flyout = new flyoutform.FlyoutForm({    
                linkId:this.linkId,
                formDivId:this.formDivId,
                inlineFormContainerId:this.formContainerId,
                customCallbackObject:this,
                customOnSubmitHandler: this.onURLPick });
            this.flyout = flyout;
        }
        
        if ( this.cancelFileButton )
        {
          Event.observe( this.cancelFileButton, "click", this.onCancelClick.bindAsEventListener( this ) );
        }
        
        Event.observe( this.allowLocal ? this.pickLocalButton.form : this.pickCSButton.form, "submit", this.onSubmit.bindAsEventListener( this ) );
        
        this.listHtmlDiv = $( this.baseElementName + "_listHtmlDiv");
        
        if ( this.listHtmlDiv && this.pickerList && this.pickerList.tableBody &&
             ( this.pickerList.tableBody.getElementsBySelector('input[type="hidden"]').length > 0 ||
               this.pickerList.tableBody.getElementsBySelector('input[type="file"]').length > 0 ) )
        {
          this.listHtmlDiv.show();
        }
        
        if ( !this.allowMultipleFiles && this.selectedFileSource.value != '' )
        {
          // A file is already attached, so show the selected files area and
          // turn off the file choosing button (and metadata button if any).
          this.selectedFileActionsArea.show();
          this.togglePickerButtons( false );
          if ( this.pickMDButton )
          {
            this.pickMDButton.hide();
          }
        }

        widget.FilePicker.registerFilePicker( this );
    },

    /**
     * For the override local behavior: show the picker to pick the location
     */
    onPickTargetClick: function( event )
    {
        var remote = window.open('/webapps/cmsmain/folderpicker/', 'picker_browse', 'width=800,height=500,resizable=yes,scrollbars=yes,status=yes,top=20,left='+(screen.width-800));
        if ( remote != null )
        {
            remote.focus();
            if ( remote.opener == null ) remote.opener = self;
            remote.opener.inputEntryURLToSet = this.targetCSLocation;
            remote.opener.returnFullURL = false;
            remote.methodCall = this.onPickMDClick.bind( this );
        }
    },

    /**
     * For the override local behavior: show the picker to pick the metadata
     */
    onPickMDClick: function( event )
    {
        var location = this.targetCSLocation;
        var file = this.localFilePicker;
        if ( file.value == '')
        {
            return;
        }
        var url = '/webapps/cmsmain/execute/metadatapicker/manage?action=pick&file='+file.value;
        if ( location && location.value != '' )
        {
          url += 'location='+location.value;
        }
        var remote = window.open( url, 'picker_browse', 'width=800,height=500,resizable=yes,scrollbars=yes,status=yes,top=20,left='+(screen.width-800));
        if ( remote != null )
        {
          remote.focus();
          if ( remote.opener == null ) remote.opener = self;
        }
    },
    /**
     * For the override local behavior: callback from the metadata picker
     */
    afterPickMD: function( metadata, selection, synchronised, format )
    {
        $(this.baseElementName + '_CSTargetmetadata').value = metadata;
        $(this.baseElementName + '_CSTargetselection').value = selection;
        $(this.baseElementName + '_CSTargetSynchronised').value = synchronised;
        $(this.baseElementName + '_CSTargetDisplayFormat').value = format;
    },

    /**
     * Show the CS file chooser
     */
    onCSBrowse: function( event )
    {
        this.csBrowseWindow = null;
        var windowId = new Date().getTime();
        this.csBrowseWindow = window.open( this.csPickerUrl, windowId, 'width=800,height=500,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no,directories=no,top=20,left='+ (screen.width - 800) );
        if ( this.csBrowseWindow != null )
        {
            this.csBrowseWindow.focus();
            if ( this.csBrowseWindow.opener == null ) this.csBrowseWindow.opener = self;
            this.csBrowseWindow.opener.returnFullURL = false;

            this.csBrowseWindow.opener.inputEntryURLToSet = this.selectedCSFile;
            this.csBrowseWindow.opener.inputFileSizeToSet = this.selectedCSFileSize;
            this.csBrowseWindow.opener.linkName = this.selectedCSFileName;
            if ( this.selectedCSFilePath )
            {
              this.csBrowseWindow.opener.filePath = this.selectedCSFilePath;
            }
            this.csBrowseWindow.opener.customHandler = this.onCSPick.bind(this);
            if ( this.showAddMetadata )
            {
              this.csBrowseWindow.opener.displayMetadataPicker = true;
              CSMetadataCallBack = this.afterMD.bind( this );
            }
        }
        Event.stop( event );
    },

    /**
     * Called after file has been chosen from CS file picker with metadata added.
     */
    afterMD: function( metadata, synced, format, fileId )
    {
      var metadataString = metadata.join("#");

      if( fileId )
      {
        if( $(this.baseElementName+'_CSMetadata_'+fileId) ) $(this.baseElementName+'_CSMetadata_'+fileId).value = metadataString;
        if( $(this.baseElementName+'_CSMetadataSync_'+fileId) ) $(this.baseElementName+'_CSMetadataSync_'+fileId).value = synced;
        if( $(this.baseElementName+'_CSMetadataFormat_'+fileId) ) $(this.baseElementName+'_CSMetadataFormat_'+fileId).value = format;
      }
      else
      {
        if( this.selectedCSFileMetadata ) this.selectedCSFileMetadata.value = metadataString;
        if( this.selectedCSFileMetadataSync ) this.selectedCSFileMetadataSync.value = synced;
        if( this.selectedCSFileMetadataFormat) this.selectedCSFileMetadataFormat.value = format;
      }
    },

    /**
     * Callback called after a file has been chosen from the CS file picker in single file mode - updating selected files section
     */
    onCSPick: function( fileList )
    {
      if (this.listHtmlDiv) 
      {
        this.listHtmlDiv.show();
      }
      
      if ( this.allowMultipleFiles )
      {
        // picker was opened in multiple mode but one of the legacy page was hit
        // see how legacy pages with radio buttons submit its selection - pickmultiple.jspf
        // create object cell generator is expecting using hidden input
        if ( typeof(fileList) == 'string' )
        {
          var filePath = this.selectedCSFilePath ? this.selectedCSFilePath.value : "";
          var csFile = { fileName: this.selectedCSFile.value,
                         fullUrlToFile: this.selectedCSFile.value,
                         linkTitle: this.selectedCSFileName.value,
                         filePath: filePath,
                         size: this.selectedCSFileSize.value };
          this.onCSPickAllowMultiple( new Array( csFile ) );
        }
        else
        {
          this.onCSPickAllowMultiple( fileList );
        }
      }
      else
      {
        this.csBrowseWindow = null;
        this.selectedFileSource.value = 'C';
        this.selectedFileName.innerHTML = this.selectedCSFileName.value;
        if ( this.selectedFileLinkTitle != null )
        {
           this.selectedFileLinkTitle.value = this.selectedCSFileName.value;
        }
        this.selectedFileActionsArea.show();
        this.togglePickerButtons( false );
        if ( this.pickMDButton )
        {
          this.pickMDButton.hide();
        }
        if ( $( this.baseElementName + '_csPermissionsLi') )
        {
          var fileName = this.selectedCSFileName.value;
          var ext = fileName.match(/.*\.(.*)/);
          ext = ext ? ext[1] :'';
          
          // Determine the type of the attached file.
          if( (/html|htm/i).test( ext ) )
          {
            $( this.baseElementName + "_permissionOptionsIndex" ).value = 0;
            $(this.baseElementName + '_csPermissionsLi').show();
          }
        }
        if ( null != this.onAttachFile )
        {
          this.onAttachFile( this, false );
        }
      }
    },

    /**
     * Called after a file is chosen from the local file picker in single file mode - updating selected files section
     */
    onLocalPick: function( event )
    {
      if ( this.localFilePicker.value != '' )
      {
        if ( this.listHtmlDiv )
        {
          this.listHtmlDiv.show();
        }
        
        if ( this.allowMultipleFiles )
        {
          this.onLocalPickAllowMultiple( event );
        }
        else
        {
          this.selectedFileName.innerHTML = this.getFileName(this.localFilePicker.value);
          this.selectedFileSource.value = 'L';
          this.selectedFileActionsArea.show();
          this.togglePickerButtons( false );
          if ( this.pickMDButton )
          {
            this.pickMDButton.show();
          }
          if ( null != this.onAttachFile )
            this.onAttachFile( this, true );
        }
      }
    },
    
    /**
     * Callback called after files have been chosen from the CS file picker in multiple files mode - updating table
     */
    onCSPickAllowMultiple: function( fileList )
    {
      this.csBrowseWindow = null;
      
      fileList.each( function ( selectedFile )
      {
        selectedFile.baseElementName = this.baseElementName;
        selectedFile.showAddMetadata = this.showAddMetadata;
        selectedFile.csPickerUrl = this.csPickerUrl;
        this.pickerList.addRow( selectedFile, false );
        
        // Wire a listener that will show the embed options when the embed action is chosen (if configured to)
        this.registerEmbedOptionsListener( widget.FilePicker.cellGenerators.fileActionEmbedOptions_index - 1 );
        
        if ( null != this.onAttachFile )
        {
          this.onAttachFile( this, false );
        }
      }.bind(this));
    },

    /**
     * Called after a file is chosen from the local file picker in multiple files mode - updating table
     */
    onLocalPickAllowMultiple: function( event )
    {
      var row = this.pickerList.addRow( this, true );

      // Wire a listener that will show the embed options when the embed action is chosen (if configured to)
      this.registerEmbedOptionsListener( widget.FilePicker.cellGenerators.fileActionEmbedOptions_index - 1 );
      
      var cell = $(row.getElementsByTagName('td')[0]);          
      var title = this.localFilePicker.title; 
      
      Element.remove( this.localFilePicker );
      this.localFilePicker.removeClassName("hiddenInput");
      this.localFilePicker.setStyle({position: "absolute", top: '-10000px'});
      this.localFilePicker.disabled = false;
      cell.appendChild( this.localFilePicker );
      
      //Set up the names of the local file fields
      this.pickerList.tableBody.getElementsBySelector('input[type="file"]').each( function( item, index )
      {
          item.name = this.baseElementName + '_LocalFile' + index;
      }.bind(this));
      
      //Create and insert a new local file picker for more local file attaching
      this.localFilePicker = new Element('input', { title: title, type: 'file' });
      this.localFilePicker.addClassName('hiddenInput');
      this.localFileContainer.insertBefore( this.localFilePicker, this.localFileContainer.firstChild );
      Event.observe( this.localFilePicker, "change", this.onLocalPick.bindAsEventListener( this ) );

      if ( null != this.onAttachFile )
      {
        this.onAttachFile( this, true );
      }
    },
    
    /**
     * If a) Special Actions are enabled, and b) the embed options are configure to show up:
     *  - Wires up an on change even on the special action select box that shows the 
     *    embed options when the embed action is chosen.
     */
    registerEmbedOptionsListener: function( index )
    {
      if ( this.showSpecialAction )
      {
        var specialAction = $( this.baseElementName + '_specialAction' + index );
        var embedOptions = $( this.baseElementName + '_embedOptions' + index );
        if ( embedOptions )
        {
          specialAction.observe('change', function( event )
          {
            if ( "EMBED" == specialAction.options[specialAction.selectedIndex].value )
            {
              embedOptions.show();
            }
            else
            {
              embedOptions.hide();
            }
          });            
        }
      }      
    },

    /**
     * Called after a url is chosen from the local file picker in multiple files mode - updating table
     */
    onURLPick: function(widget)
    {
        if (widget.listHtmlDiv)
          widget.listHtmlDiv.show();
        
        if ( widget.allowMultipleFiles )
        {
            var selectedURL = document.getElementById(widget.baseElementName + '_newUrlName');
            var urlFile = { fileName: selectedURL.value,
                    fullUrlToFile: selectedURL.value,
                    linkTitle: selectedURL.value };
            
            widget.onCSPickAllowMultiple( new Array( urlFile ) );
        }
        else
        {
          widget.selectedFileSource.value = 'C';
          var selectedURL = document.getElementById(widget.baseElementName + '_newUrlName');
          widget.selectedFileName.innerHTML = selectedURL.value;
          if ( widget.selectedFileLinkTitle != null )
          {
            widget.selectedFileLinkTitle.value = selectedURL.value;
          }
          widget.selectedFileActionsArea.show();
          widget.togglePickerButtons( false );
          if ( widget.pickMDButton )
          {
            widget.pickMDButton.hide();
          }
          if ( null != widget.onAttachFile )
            widget.onAttachFile( widget, false );
        }
        
        //Purge entry
        widget.flyout.close();
        selectedURL.value='';
        return true;
    },
    
    /**
     * Called when the "Do not attach file" button is clicked
     */
    onCancelClick: function( event )
    {
        this.selectedFileActionsArea.hide();
        this.clearSelectedFileInfo();
        this.togglePickerButtons( true );
        if ( event )
        {
          Event.stop( event );
        }
    },

    /**
     * Toggles the enabled state of the Local/CS picker buttons to the specified state
     */
    togglePickerButtons: function( enabled )
    {
        if ( this.allowLocal )
        {
            this.pickLocalButton.disabled = !enabled;
            if (enabled) 
            {
              this.pickLocalButton.className='browse visibleInput';
                this.localFilePicker.position = 'relative';
                this.localFilePicker.style.top = '';
            } 
            else 
            {
              this.pickLocalButton.className='disabled';
                this.localFilePicker.position = 'absolute';
                this.localFilePicker.style.top = '-10000px';
            }
            if ( this.allowMultiple )
            {
                this.localFilePicker.disabled = !enabled;
            }
        }
        if ( this.allowCS )
        {
            this.pickCSButton.disabled = !enabled;
            if (enabled) 
            this.pickCSButton.className='browse visibleInput';
            else
              this.pickCSButton.className='disabled';
        }
        if ( this.allowURL )
        {
            this.pickURLButton.disabled = !enabled;
            if (enabled) 
            this.pickURLButton.className='browse visibleInput';
            else
              this.pickURLButton.className='disabled';
        }
    },

    /**
     * Clears the info associated to the current attached file (if the user cancelled, or chose
     * to attach another file )
     */
    clearSelectedFileInfo: function()
    {
        var isLocal = this.selectedFileSource.value == 'L';
        this.selectedFileName.innerHTML = "";
        this.selectedFileSource.value = "";
        if ( this.selectedFileLinkTitle != null ) this.selectedFileLinkTitle.value= "";
        if ( this.pickMDButton )
        {
          $(this.baseElementName + '_CSTargetmetadata').value = "";
          $(this.baseElementName + '_CSTargetselection').value = "";
          $(this.baseElementName + '_CSTargetSynchronised').value = "";
          $(this.baseElementName + '_CSTargetDisplayFormat').value = "";
        }
        if ( $( this.baseElementName + "_permissionOptionsIndex" ) )
        {
          $( this.baseElementName + '_permissionOptionsIndex' ).value = -1;
          $( this.baseElementName + '_permissions0_all' ).checked = true;
        }        
        if ( this.selectedFileSpecialAction != null ) this.selectedFileSpecialAction.selectedIndex = 0;
        if ( isLocal )
        {
            var title = "";
            if ( !this.allowMultipleFiles )
            {
              title = this.localFilePicker.title;
              Element.remove( this.localFilePicker );
            }
            this.localFilePicker = $(document.createElement("input"));
            if ( !this.allowMultipleFiles )
            {
                this.localFilePicker.name = this.baseElementName + '_LocalFile0';
            }
            this.localFilePicker.title = title;
            this.localFilePicker.type = "file";
            this.localFilePicker.addClassName("hiddenInput");
            this.localFileContainer.insertBefore( this.localFilePicker, this.localFileContainer.firstChild );
            Event.observe( this.localFilePicker, "change", this.onLocalPick.bindAsEventListener( this ) );
        }
        else
        {
            this.selectedCSFile.value = "";
            this.selectedCSFileName.value = "";
            if( this.selectedFilePath )
            {
              this.selectedFilePath = "";
            }
            this.selectedCSFileSize.value = "";
            if ( this.showAddMetadata )
            {
              this.selectedCSFileMetadata.value = "";
              this.selectedCSFileMetadataSync.value = "";
              this.selectedCSFileMetadataFormat.value = "";
            }
        }
    },

    /**
     * Gets the file name based on the full file path.
     */
    getFileName: function( fullPath )
    {
        var result = fullPath;
        var lastIndexOfBackslash = fullPath.lastIndexOf('\\');
        var lastIndexOfSlash = fullPath.lastIndexOf('/');
        if ( lastIndexOfBackslash > lastIndexOfSlash )
        {
            result = fullPath.substring( lastIndexOfBackslash + 1, fullPath.length );
        }
        else if ( lastIndexOfSlash > lastIndexOfBackslash )
        {
            result = fullPath.substring( lastIndexOfSlash + 1, fullPath.length );
        }
        return result;
    },

    setRequired: function( required )
    {
      this.required = required;
    },

    /**
     * Validates the form when it is submitted.
     */
    onSubmit: function( event )
    {
        //Validate the form if necessary
        if ( this.required )
        {
          if ( this.allowMultipleFiles )
          {
              if ( this.pickerList.tableBody.immediateDescendants().length == 0 )
              {
                  alert( page.bundle.getString("filePicker.validate.atLeastOne") );
                  if ( event )
                  {
                    Event.stop( event );
                  }
                  return false;
              }
          }
          else
          {
              if ( (this.selectedCSFile != null && this.selectedCSFile.value == '') &&
                   (this.localFilePicker != null && this.localFilePicker.value == '') )
              {
                  alert( page.bundle.getString("filePicker.validate.one") );
                  if ( event )
                  {
                    Event.stop( event );
                  }
                  return false;
              }
          }
        }
        return true;        
    }
}

/**
 * Toggles the "mark for removal" status of an item in the currently attached
 * files table.  Should be called from an onclick attribute on the toggle link
 * in the table.
 */
widget.FilePicker.toggleForRemove = function( event, removeLink )
{
  var e = event || window.event;
  removeLink = $( removeLink );
  var tableRow = removeLink.up("tr");
  var hiddenField = removeLink.up("td").down('input[type="hidden"]');

  tableRow.toggleClassName("removeCell");
  if ( hiddenField.disabled )
  {
    hiddenField.disabled = false;
    removeLink.innerHTML = page.bundle.getString("filePicker.unmarkForRemove");
  }
  else
  {
    hiddenField.disabled = true;
    removeLink.innerHTML = page.bundle.getString("filePicker.markForRemove");
  }
  Event.stop( e );
};

widget.FilePicker.togglePickerButton = function( pickerButton, enabled )
{
  if (enabled)
  {
    $(pickerButton).setAttribute("href", "#");
    $(pickerButton).removeClassName("disabled");
  }
  else
  {
    $(pickerButton).removeAttribute("href");
    $(pickerButton).addClassName("disabled");
  }
};

/**
 * Removes the specified pending attachment.  Should be called from an
 * onclick handler on a link in the currently attached files table
 */
widget.FilePicker.removePendingAttachment = function( event, removeLink, baseElementName )
{
  var e = event || window.event;
  if ( confirm( page.bundle.getString("filePicker.doNotAttach.confirm") ) )
  {
    removeLink = $( removeLink );
    var row = removeLink.up("tr");
    var tbody = row.up("tbody");

    // Remove the pending row
    Element.remove( row );

    // Re-index the local file inputs.
    tbody.getElementsBySelector('input[type="file"]').each( function( item, index )
    {
      item.name = baseElementName + '_LocalFile' + index;
    });
    
    // Hide the whole attached files table if there are no more attached files.
    var rowTotal = tbody.getElementsByTagName('tr').length;
    if ( rowTotal == 0 )
    {
      var listHtmlDiv = $( baseElementName + "_listHtmlDiv");
      if ( listHtmlDiv )
      {
        listHtmlDiv.hide();
      }
    }
  }
  Event.stop( e );
};

widget.FilePicker.getPermPickerUrl = function( csPickerUrl, path, pathElement )
{
  // reuse current file picker's url as fall back plan 
  var baseUrl = csPickerUrl.split("?")[0];
  var queryParams = csPickerUrl.toQueryParams();
  if ( !queryParams['multipicker'] )
  {
    queryParams['multipicker'] = 'true';
  }
  if ( !path && pathElement )
  {
    path = pathElement.value;
  }
  // if we have path, construct proper full url from scratch using the provided path & extracted principalId
  if ( path )
  {
    var principalId = baseUrl.match(/\/webapps\/cmsmain\/.*picker\/([^\/]*)\/?/);
    baseUrl = "/webapps/cmsmain/picker";
    if ( principalId )
    {
      baseUrl += "/" + principalId[1];
    }
    baseUrl += path;
  }
  
  return baseUrl + "?" + $H( queryParams ).toQueryString();
};

widget.FilePicker.openPermPicker = function( event, csPickerUrl, pid, path, pathElement )
{
  if( !( pid && $(pid) && $(pid).type == 'radio' && $(pid).checked) )
  {
    return false;
  }
  
  var permPickerUrl = widget.FilePicker.getPermPickerUrl( csPickerUrl, path, pathElement )
  
  var windowId = new Date().getTime();
  var remote = window.open( permPickerUrl, windowId, 'width=800,height=500,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no,directories=no,top=20,left='+ (screen.width - 800) );
  if ( remote != null )
  {
      remote.focus();
      if ( remote.opener == null ) remote.opener = self;

      remote.opener.customHandler = null;
  }
  
  var e = event || window.event;
  if( e )
  {
    var eventElement = Event.element( e );
    Event.stop( e );
  }
  return false;
};

/**
 * A registry of defined file pickers and methods to access this registry
 */
widget.FilePicker.filePickers = {};
widget.FilePicker.registerFilePicker = function( filePicker )
{
 widget.FilePicker.filePickers[filePicker.baseElementName] = filePicker;
};
widget.FilePicker.unRegisterFilePicker = function( filePicker )
{
 delete widget.FilePicker.filePickers[filePicker.baseElementName];
};
widget.FilePicker.getFilePicker = function( baseElementName )
{
 return widget.FilePicker.filePickers[baseElementName];
};

/**
 * Cell generators for the current attached files table
 */
widget.FilePicker.cellGenerators = {
    fileName: function( filePicker, isLocal )
    {
        var result = '<input type="hidden" name="'+filePicker.baseElementName+'_attachmentType" value="'+(isLocal?'L':'C')+'">';
        result += '<input type="hidden" name="'+filePicker.baseElementName+'_fileId" value="new">';
        if ( isLocal )
        {
            result += '<span class="fileName"><img src="/images/ci/ng/cal_year_event.gif" alt="'+page.bundle.getString('common.file')+'"> '+filePicker.getFileName(filePicker.localFilePicker.value)+'</span>';
        }
        else
        {
            result += '<span class="fileName"><img src="/images/ci/ng/cal_year_event.gif" alt="'+page.bundle.getString('common.file')+'"> '+filePicker.linkTitle+'</span>';            
            result += '<input type="hidden" name="'+filePicker.baseElementName+'_CSFile" value="'+filePicker.fileName+'">';
            result += '<input type="hidden" name="'+filePicker.baseElementName+'_CSFileUrl" value="' + filePicker.fullUrlToFile + '">';
            if ( filePicker.showAddMetadata )
            {
              result += '<input type="hidden" name="'+filePicker.baseElementName+'_CSMetadata" id="'+filePicker.baseElementName+'_CSMetadata_'+filePicker.xythosId+'" value="">';
              result += '<input type="hidden" name="'+filePicker.baseElementName+'_CSMetadataSync" id="'+filePicker.baseElementName+'_CSMetadataSync_'+filePicker.xythosId+'" value="">';
              result += '<input type="hidden" name="'+filePicker.baseElementName+'_CSMetadataFormat" id="'+filePicker.baseElementName+'_CSMetadataFormat_'+filePicker.xythosId+'" value="">';
            }
        }
        return result;
    },
    fileType: function( filePicker, isLocal )
    {
        var result = null;
        if ( isLocal )
        {
            result = page.bundle.getString("filePicker.fileType.attachment");
        }
        else
        {
            result = page.bundle.getString("filePicker.fileType.content");
        }
        return result;
    },
    linkTitle: function( filePicker, isLocal )
    {
        var linkTitle = "";
        if ( isLocal )
        {
          linkTitle = filePicker.getFileName(filePicker.localFilePicker.value);
        }
        else
        {
          if ( filePicker.linkTitle != null )
          {
              linkTitle = filePicker.linkTitle;
          }
        }
        return '<input type="text" name="'+filePicker.baseElementName+'_linkTitle" value="'+linkTitle
               +'" title="' + page.bundle.getString("filePicker.nameOfLink") + " " + linkTitle + '">';
    },
    size: function( filePicker, isLocal )
    {
        if ( !isLocal && filePicker.size != null )
        {
            return '<input type="hidden" name="'+filePicker.baseElementName+'_size" value="'+filePicker.size+'"> ' + filePicker.size;
        }
        else
        {
            return "";
        }
    },
    fileAction: function( filePicker, isLocal )
    {
      return widget.FilePicker.cellGenerators.fileActionEmbedOptions( filePicker, isLocal, true )
    },
    fileActionEmbedOptions_index: 0, // used to uniquely identify embed options form elements on the page.
    fileActionEmbedOptions: function( filePicker, isLocal, suppressOptions )
    {
      var index = widget.FilePicker.cellGenerators.fileActionEmbedOptions_index++;
      var baseName = filePicker.baseElementName;
      
      var fileName = isLocal ? filePicker.getFileName(filePicker.localFilePicker.value) : filePicker.linkTitle;
      var ext = fileName.match(/.*\.(.*)/);
      ext = ext ? ext[1] :'';
      
      // Determine the type of the attached file.
      var isType = {
        image: (/gif|jpeg|png|tif|bmp|jpg/i).test( ext ),
        videoQt:  (/qt|mov|moov|movie/i).test( ext ),
        videoOther: (/avi|mpg|mpeg|wmv|asf|wma|mpe/i).test( ext ),
        real: (/ra|ram|rm/i).test( ext ),
        flash: (/swa|swf/i).test( ext ),
        audio: (/aif|aiff|au|mp3|wav/i).test( ext ),
        html: (/html|htm/i).test( ext )
      }
      
      var isMedia = isType.image || isType.videoQt || isType.videoOther || isType.flash || isType.audio;      
      
      var result = '';
      if ( isMedia )
      {
        result += '<select id="' + baseName + '_specialAction' + index + '" name="' + baseName + '_specialAction"';
        result += ' title="' + page.bundle.getString("filePicker.specialAction.for") + " " + fileName + '">';
        result += '<option value="LINK">' + page.bundle.getString("filePicker.specialAction.link") + '</option>';
        result += '<option value="EMBED">' + page.bundle.getString("filePicker.specialAction.embed") + '</option>';
        result += '</select>';
        
        // Render the embed options (if not suppressed)
        if ( !suppressOptions )
        {
           result += '<ul style="display: none;" class="nestedList smallControls liveArea liveArea-slim" id="' + baseName + '_embedOptions' + index + '">'; 
          
           // Alignment
           fid = baseName + 'align' + index;
           result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.alignment' ) +
             '</div><div class="field" style="white-space:nowrap;">' +
             '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.alignment' ) + '</span></legend>' +
             '<input type="radio" id="' + fid + '_l" name="' + fid + '" value="Left" checked> <label for="' +
             fid + '_l">' + page.bundle.getString( 'wysiwyg.insert_picture.alignment.left' ) + '</label>' +
             '<input type="radio" id="' + fid + '_c" name="' +
             fid + '" value="Center"> <label for="' + fid + '_c">' + 
             page.bundle.getString( 'wysiwyg.insert_picture.alignment.center' ) + '</label>' +
             '<input type="radio" id="' + fid + '_r" name="' +
             fid + '" value="Right"> <label for="' + fid + '_r">' + 
             page.bundle.getString( 'wysiwyg.insert_picture.alignment.right' ) + '</label></fieldset></div></li>';   
           
           // Placement
           fid = baseName + 'placement' + index;
           result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.placement' ) +
             '</div><div class="field" style="white-space:nowrap;">' +
             '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.placement' ) + '</span></legend>' +
             '<input type="radio" id="' + fid + '_a" name="' + fid + '" value="Above"> <label for="' +
             fid + '_a">' + page.bundle.getString( 'wysiwyg.insert_picture.placement.above' ) + '</label>' +
             '<input type="radio" id="' + fid + '_b" name="' +
             fid + '" value="Below" checked> <label for="' + fid + '_b">' + 
             page.bundle.getString( 'wysiwyg.insert_picture.placement.below' ) + '</label></fieldset></div></li>';              
           
           // Dimensions
           if ( !isType.audio )
           {
             fid = baseName + 'width' + index;
             result += '<li><div class="label"><label for="' + fid + '">' +
               page.bundle.getString( 'wysiwyg.insert_picture.width' ) +
               '</label></div><div class="field"><input class="width" type="text" name="' +
               fid + '" id="' + fid + '" size="4" value=""></div></li>';
             
             fid = baseName + 'height' + index;
             result += '<li><div class="label"><label for="' + fid + '">' + 
               page.bundle.getString( 'wysiwyg.insert_picture.height' ) +
               '</label></div><div class="field"><input class="height" type="text" name="' +
               fid + '" id="' + fid + '" size="4" value=""></div></li>';
           }
             
           // Autoplay
           if ( !isType.image )
           {
             fid = baseName + 'autostart' + index;
             result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.autostart' ) +
               '</div><div class="field" style="white-space:nowrap;">' +
               '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.autostart' ) + '</span></legend>' +
               '<input type="radio" id="' + fid + '_y" name="' + fid + '" value="true"> <label for="' +
               fid + '_y">' + page.bundle.getString( 'wysiwyg.insert_picture.yes' ) + '</label>' +
               '<input type="radio" id="' + fid + '_n" name="' +
               fid + '" value="false" checked> <label for="' + fid + '_n">' + 
               page.bundle.getString( 'wysiwyg.insert_picture.no' ) + '</label></fieldset></div></li>';   
           }

           // Loop
           if ( !isType.image && !isType.real )
           {
             fid = baseName + 'loop' + index;
             result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.loop' ) +
               '</div><div class="field" style="white-space:nowrap;">' +
               '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.loop' ) + '</span></legend>' +
               '<input type="radio" id="' + fid + '_y" name="' + fid + '" value="true"> <label for="' +
               fid + '_y">' + page.bundle.getString( 'wysiwyg.insert_picture.yes' ) + '</label>' +
               '<input type="radio" id="' + baseName + 'loop' + index + '_n" name="' +
               fid + '" value="false" checked> <label for="' + fid + '_n">' + 
               page.bundle.getString( 'wysiwyg.insert_picture.no' ) + '</label></fieldset></div></li>';             
           }
           
           // Control (radio)
           if ( isType.videoQt || isType.real )
           {
             fid = baseName + 'controls' + index;
             result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.controls' ) +
               '</div><div class="field" style="white-space:nowrap;">' +
               '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.controls' ) + '</span></legend>' +
               '<input type="radio" id="' + fid + '_y" name="' + fid + '" value="Full" checked> <label for="' +
               fid + '_y">' + page.bundle.getString( 'wysiwyg.insert_picture.yes' ) + '</label>' +
               '<input type="radio" id="' + fid + '_n" name="' +
               fid + '" value="None"> <label for="' + fid + '_n">' + 
               page.bundle.getString( 'wysiwyg.insert_picture.no' ) + '</label></fieldset></div></li>';                
           }
             
           // Controls (drop down)
           if ( isType.videoOther || isType.audio )
           {
             fid = baseName + 'controls' + index;
             result += '<li><div class="label"><label for="'+ fid + '">' +
              page.bundle.getString( 'wysiwyg.insert_picture.controls' ) +
              '</label></div><div class="field"><select id="' + fid + '" name="' + fid + '" class="controls">' +
              '<option value="None">' + page.bundle.getString( 'wysiwyg.insert_picture.controls_none' ) + '</option>' +
              '<option value="Mini">' + page.bundle.getString( 'wysiwyg.insert_picture.controls_mini' ) + '</option>' +
              '<option value="Full" selected>' + page.bundle.getString( 'wysiwyg.insert_picture.controls_full' ) + '</option>' +
              '</select></div></li>';
           }
             
           // Quality
           if ( isType.flash )
           {
             fid = baseName + 'quality' + index;
             result += '<li><div class="label"><label for="'+ fid +'">' +
               page.bundle.getString( 'wysiwyg.insert_picture.set_quality' ) +
               '</label></div><div class="field"><select id="' + fid + '" name="' + fid + '" class="quality">' +
               '<option value="Low">' + page.bundle.getString( 'wysiwyg.insert_picture.quality_low' ) + '</option>' +
               '<option value="Medium">' + page.bundle.getString( 'wysiwyg.insert_picture.quality_med' ) + '</option>' +
               '<option value="High">' + page.bundle.getString( 'wysiwyg.insert_picture.quality_high' ) + '</option>' +
               '<option value="Best" selected>' + page.bundle.getString( 'wysiwyg.insert_picture.quality_best' ) + '</option>' +
               '</select></div></li>';
           }
             
           if ( isType.image )
        {
             // URL      
             fid = baseName + 'url' + index;
             result += '<li><div class="label"><label for="' + fid + '">' +
               page.bundle.getString( 'wysiwyg.insert_picture.url' ) +
               '</label></div><div class="field"><input type="text" name="' + fid + '" id="' + fid +'" size="25">'+
               '</div></li>';
             
             // New Window
             fid = baseName + 'newwin' + index;
             result += '<li><div class="label">' + page.bundle.getString( 'wysiwyg.insert_picture.new_window' ) +
               '</div><div class="field" style="white-space:nowrap;">' +
               '<fieldset role="radiogroup"><legend><span class="hideoff">' + page.bundle.getString( 'wysiwyg.insert_picture.new_window' ) + '</span></legend>' +
               '<input type="radio" id="' + fid + '_y" name="' + fid + '" value="true" checked> <label for="' +
               fid + '_y">' + page.bundle.getString( 'wysiwyg.insert_picture.yes' ) + '</label>' +
               '<input type="radio" id="' + fid + '_n" name="' + fid + '" value="false"> <label for="' + 
               fid + '_n">' + page.bundle.getString( 'wysiwyg.insert_picture.no' ) + '</label>' +
               '</fieldset></div></li>';
             
             // Border
             fid = baseName + 'border' + index;
             result += '<li><div class="label"><label for="'+ fid +'">' +
               page.bundle.getString( 'wysiwyg.insert_picture.border' ) +
               '</label></div><div class="field"><select id="' + fid + '" name="' + fid + '" class="border">' +
               '<option value="0" selected>' + page.bundle.getString( 'wysiwyg.insert_picture.none' ) + '</option>' +
               '<option value="1>1</option><option value="2">2</option><option value="3">3</option>' +
               '<option value="4">4</option></select></div></li>';
           }           
          
           // Alt text
           var fid = baseName + 'alttext' + index;
           result += '<li><div class="label"><label for="' + fid + '">' +
             page.bundle.getString( 'wysiwyg.insert_picture.alt_text' ) +
             '</label></div><div class="field"><input type="text" name="' +
             fid + '" id="' + fid + '" size="25" value=""></div><span class="fieldHelp">' + 
             page.bundle.getString( 'wysiwyg.insert_picture.alt_text.instructions' ) + '</span></li>';
           
           result += '</ul>';
        }
      }
      else
      {
        result += '<input type="hidden" name="' + filePicker.baseElementName + '_specialAction" value="LINK">';
        result += page.bundle.getString("filePicker.specialAction.link");
      }
      // The index is used to find the correct request parameters on the backend.
      result += '<input type="hidden" name="' + filePicker.baseElementName + '_specialActionIndex" value="'+ ( isMedia && !suppressOptions ? index : -1 ) +'">';

      if ( filePicker.shouldManagePermission == true && !isLocal )
      { 
        // permission management options
        var permissionCsPickerUrl = filePicker.csPickerUrl;
        if ( permissionCsPickerUrl.indexOf( 'multipicker=true' ) != -1 )
        {  
          var xythosId = new String( filePicker.fileName ).sub( '/xid-', '' ).strip();
          if ( xythosId )
          {  
            permissionCsPickerUrl = permissionCsPickerUrl + '&preselectedItemId=' + xythosId; 
          }
        }
        
        result += '<ul id="' + filePicker.baseElementName + '_imgSpecOptions" class="nestedList smallControls liveArea liveArea-slim">';
        result += '<li>';
        var pid = baseName + "_permissions" + index;
        var pickerButtonId = baseName + "_permissionPickerButton" + index;
        result += '<input type="radio" name="' + pid + '" value="A" id="' + pid + '_all" checked="checked" onChange="widget.FilePicker.togglePickerButton(\'' + pickerButtonId + '\', false)" />' + page.bundle.getString("fm.permission.grant.all") + '<br/>';
        result += '<input type="radio" name="' + pid + '" value="O" id="' + pid + '_this" onChange="widget.FilePicker.togglePickerButton(\'' + pickerButtonId + '\', false)" />' + page.bundle.getString("fm.permission.grant.this") + '<br/>';
        result += '<input type="radio" name="' + pid + '" value="S" id="' + pid + '_manual" onChange="widget.FilePicker.togglePickerButton(\'' + pickerButtonId + '\', true)" />' + page.bundle.getString("fm.permission.grant.manual");
        result += '<a id="' + pickerButtonId + '" class="button-4 disabled" style="margin:0 3px" onclick="widget.FilePicker.openPermPicker( event, \''+ permissionCsPickerUrl + '\', \'' + pid + '_manual\',\'' + filePicker.filePath + '\');">' + page.bundle.getString("filePicker.browse") + '</a>';
        result += '</li>';
        result += '</ul>';
      }
      result += '<input type="hidden" name="' + filePicker.baseElementName + '_permissionOptionsIndex" value="'+ ( isType.html&& !isLocal ? index : -1 ) +'">';

      return result;      
    },
    remove: function( filePicker, isLocal )
    {
      var result = '<a href="#" onclick="widget.FilePicker.removePendingAttachment(event, this, ';
      result += "'" + filePicker.baseElementName + "'";
      result += ');">' + page.bundle.getString("filePicker.doNotAttach") + '</a>';
      
      return result;
    }
};

widget.InlineSingleCSFilePicker = Class.create();
widget.InlineSingleCSFilePicker.prototype =
{
    initialize: function( baseElementName, csPickerUrl, showAddMetadata, showSpecialAction )
    {
    this.baseElementName = baseElementName;
    this.csPickerUrl = csPickerUrl;
    this.showAddMetadata = showAddMetadata;
    this.showSpecialAction = showSpecialAction
    this.browseButton = $(baseElementName + '_csBrowse' );
    this.fileEntry = $(baseElementName + '_CSFile' );
    this.fileId = $(baseElementName + '_fileId' );
    if ( this.showAddMetadata )
    {
      this.selectedCSFileMetadata = $( this.baseElementName + '_CSMetadata' );
      this.selectedCSFileMetadataSync = $( this.baseElementName + '_CSMetadataSync');
      this.selectedCSFileMetadataFormat = $( this.baseElementName + '_CSFileMetadataFormat');
    }

    Event.observe( this.browseButton, 'click', this.onCSBrowse.bindAsEventListener( this ) );
    },

    /**
     * Show the CS file chooser
     */
    onCSBrowse: function( event )
    {
        this.csBrowseWindow = null;
        var windowId = new Date().getTime();
        this.csBrowseWindow = window.open( this.csPickerUrl, windowId, 'width=800,height=500,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no,directories=no,top=20,left='+ (screen.width - 800) );
        if ( this.csBrowseWindow != null )
        {
            this.csBrowseWindow.focus();
            if ( this.csBrowseWindow.opener == null ) this.csBrowseWindow.opener = self;
            this.csBrowseWindow.opener.inputEntryURLToSet = this.fileEntry;
            this.csBrowseWindow.opener.returnFullURL = false;
            this.csBrowseWindow.opener.linkName = this.fileId;
            if ( this.showAddMetadata )
            {
              this.csBrowseWindow.opener.displayMetadataPicker = true;
              CSMetadataCallBack = this.afterMD.bind( this );
            }
        }
        Event.stop( event );
    },
    afterMD: function( metadata, synced, format )
    {
      var metadataString = metadata.join("#");
      this.selectedCSFileMetadata.value = metadataString;
      this.selectedCSFileMetadataSync.value = synced;
      this.selectedCSFileMetadataFormat.value = format;
    }
};

/**
 * Inline single local file picker
 *   - $baseElementName_attachmentType - 'AL' or 'L' or ''
 *   - $baseElementName_localBrowse
 *   - $baseElementName_fileId
 *
 */
widget.InlineSingleLocalFilePicker = Class.create();
widget.InlineSingleLocalFilePicker.prototype =
{
    initialize: function( baseElementName, currentAttachedFile, required, showSpecialAction )
    {
      this.baseElementName = baseElementName;
      this.fileInput = $(baseElementName + '_chooseLocalFile');
      this.fileInputWrapper = $(this.fileInput.parentNode);
      this.attachmentType = $(baseElementName + '_attachmentType');
      this.fullFileName = $(baseElementName + '_fileId');
      this.attachmentNameSpan = $(baseElementName + '_attachmentName');
      this.removeLink = $(baseElementName + '_removeLink');
      this.inputArea = $(baseElementName + '_inputArea');
      this.attachedFileArea = $(baseElementName + '_attachedFileArea');
      this.showSpecialAction = showSpecialAction;
      //Wire up events
      Event.observe( this.fileInput, "change", this.onAfterChooseFile.bindAsEventListener( this ) );
      Event.observe( this.removeLink, "click", this.onRemoveClick.bindAsEventListener( this ) );
      Event.observe( this.fileInput.form, "submit", this.onSubmit.bindAsEventListener( this ) );
      //Set up initial values
      if ( currentAttachedFile != null && currentAttachedFile != '' )
      {
        this.setDefaultCurrentAttachedFile( currentAttachedFile );
      }
      widget.InlineSingleLocalFilePicker.pickerMap[baseElementName] = this;
    },
    setCurrentAttachedFile: function( file )
    {
      this.clearFileInput();
      if ( file != null && file != '' )
      {
        this.attachmentType.value = 'AL';
        this.attachmentNameSpan.innerHTML = '<a href="'+file+'" target="_new">' + this.getFileName( file ) + '</a>';
        if ( this.removeHiddenField )
        {
          Element.remove( this.removeHiddenField );
          this.removeHiddenField = null;
        }
        widget.ShowUnsavedChanges.changeHiddenValue( this.fullFileName, file );
        this.inputArea.setStyle({position: "absolute", top: '-10000px'});
        this.attachedFileArea.setStyle({position: "static", top: ''});
      }
      else
      {
        this.attachmentNameSpan.innerHTML = '';
        this.attachmentType.value = '';
        widget.ShowUnsavedChanges.changeHiddenValue( this.fullFileName, '');
        this.inputArea.setStyle({position: "static", top: ''});
        this.attachedFileArea.setStyle({position: "absolute", top: '-10000px'});
      }
    },
    setDefaultCurrentAttachedFile: function( file )
    {
      this.clearFileInput();
      if ( file != null  && file != '' )
      {
        this.attachmentType.value = 'AL';
        this.attachmentType._defaultValue = 'AL';
        this.attachmentNameSpan.innerHTML = '<a href="'+file+'" target="_new">' + this.getFileName( file ) + '</a>';
        if ( this.removeHiddenField )
        {
          Element.remove( this.removeHiddenField );
          this.removeHiddenField = null;
        }
        this.fullFileName.value = file;
        this.fullFileName._defaultValue = file;
        this.inputArea.setStyle({position: "absolute", top: '-10000px'});
        this.attachedFileArea.setStyle({position: "static", top: ''});
      }
      else
      {
        this.attachmentNameSpan.innerHTML = '';
        this.attachmentType.value = '';
        this.fullFileName.value = '';
        this.inputArea.setStyle({position: "static", top: ''});
        this.attachedFileArea.setStyle({position: "absolute", top: '-10000px'});
      }
    },
    onAfterChooseFile: function( event )
    {
      this.attachmentNameSpan.innerHTML = this.getFileName( this.fileInput.value );
      this.attachmentType.value = 'L';
      if ( this.removeHiddenField )
      {
        Element.remove( this.removeHiddenField );
        this.removeHiddenField = null;
      }
      widget.ShowUnsavedChanges.changeHiddenValue( this.fullFileName, this.fileInput.value );
      this.inputArea.setStyle({position: "absolute", top: '-10000px'});
      this.attachedFileArea.setStyle({position: "static", top: ''});
      Event.stop( event );
    },
    onRemoveClick: function( event )
    {
      this.clearFileInput();
      if ( this.attachmentType.value == 'AL' )
      {
        this.removeHiddenField = document.createElement('input');
        this.removeHiddenField.type = 'hidden';
        this.removeHiddenField.name = this.baseElementName + '_remove';
        this.removeHiddenField.id = this.baseElementName + '_remove';
        this.removeHiddenField.value = this.fullFileName.value;
        this.attachedFileArea.insertBefore( this.removeHiddenField, this.attachedFileArea.firstChild );
        widget.ShowUnsavedChanges.changeHiddenValue(this.fullFileName, this.fullFileName.value);
      }
      else
      {
        this.attachmentType.value = '';
        widget.ShowUnsavedChanges.changeHiddenValue(this.fullFileName, '');
      }
      this.attachmentNameSpan.innerHTML = '';

      this.inputArea.setStyle({position: "static", top: ''});
      this.attachedFileArea.setStyle({position: "absolute", top: '-10000px'});
      Event.stop( event );
    },
    clearFileInput: function()
    {
      var title = this.fileInput.title;
      Element.remove( this.fileInput );
      this.fileInput = $(document.createElement("input"));
      this.fileInput.title = title;
      this.fileInput.type = "file";
      this.fileInput.addClassName("hiddenInput");
      this.fileInput.name = this.baseElementName + '_LocalFile0';
      this.fileInput._defaultValue = '';
      this.fileInputWrapper.insertBefore( this.fileInput, this.fileInputWrapper.firstChild );
      Event.observe( this.fileInput, "change", this.onAfterChooseFile.bindAsEventListener( this ) );
    },
    /**
     * Gets the file name based on the full file path.
     */
    getFileName: function( fullPath )
    {
        var result = fullPath;
        var lastIndexOfBackslash = fullPath.lastIndexOf('\\');
        var lastIndexOfSlash = fullPath.lastIndexOf('/');
        if ( lastIndexOfBackslash > lastIndexOfSlash )
        {
            result = fullPath.substring( lastIndexOfBackslash + 1, fullPath.length );
        }
        else if ( lastIndexOfSlash > lastIndexOfBackslash )
        {
            result = fullPath.substring( lastIndexOfSlash + 1, fullPath.length );
        }
        return result;
    },
    /**
     * Validates the form when it is submitted.
     */
    onSubmit: function( event )
    {
        //Validate the form if necessary
        if ( this.required )
        {
            if ( this.fullFileName.value == '' )
            {
                alert( page.bundle.getString("filePicker.validate.one") );
                Event.stop( event );
                return;
            }
        }
    }
};
widget.InlineSingleLocalFilePicker.pickerMap = {};

widget.UserRoleSelect= Class.create();
widget.UserRoleSelect.prototype = {
  initialize: function( userSelectRoleDivStr, moveMsgJS, noRoleMsgJs )
    {
       this.sourceValueName = "_left_values";
       this.secondaryValueName = "_right_values";
       this.primaryValueName = "_primary_value";

    this.userSelectRoleDiv = $(userSelectRoleDivStr);
    if ( this.userSelectRoleDiv != null ) {
        var selects = this.userSelectRoleDiv.getElementsByTagName('select');
        var inputs = this.userSelectRoleDiv.getElementsByTagName('input');
        var links = this.userSelectRoleDiv.getElementsByTagName('a');
      this.source = $(selects[0]);
      this.primary = $(inputs[3]);
      this.secondary = $(selects[1]);

      this.leftValues =  $(inputs[0]);
      this.rightValues = $(inputs[1]);
      this.primaryValue = $(inputs[2]);

      this.toPrimaryMove = $(links[0]);
      this.toSecondaryMove = $(links[1]);
      this.toSourceMove = $(links[2]);
      Event.observe(this.toSourceMove, "click", this.switchUserRoleColumns.bindAsEventListener(this, false,moveMsgJS,noRoleMsgJs));
      Event.observe(this.toPrimaryMove, "click", this.switchPrimary.bindAsEventListener(this, moveMsgJS));
      Event.observe(this.toSecondaryMove, "click", this.switchUserRoleColumns.bindAsEventListener(this, true,moveMsgJS,noRoleMsgJs));
    }
    },

  switchPrimary : function ( event, msg ) {
    var indx;
    indx=0;

    var sourceArray = this.source;

    if (sourceArray.selectedIndex == -1 || sourceArray.selectedIndex > sourceArray.length-1)
    {
      alert(msg);
      return false;
    }
    if (sourceArray.selectedIndex >= 0)
    {
      indx =sourceArray.selectedIndex;
      if( indx == -1 || indx > sourceArray.length-1)
          {
             alert(msg);
             return false;
          }
        tmpOpt = this.getFromPrimary();

        this.primaryValue.value=sourceArray.options[indx].value;
        this.primary.value = sourceArray.options[indx].text;

        sourceArray.options[indx].selected=false;
        sourceArray.options[indx]=null;
        sourceArray.options[sourceArray.length] = tmpOpt;
    }
    this.writeToSelectBoxValues( this.source ,this.leftValues );
    return false;
  },
  getFromPrimary: function () {
    var primaryRoleField = this.primaryValue;
    var primaryField = this.primary;
    return new Option( primaryField.value, primaryRoleField.value);
  },
  writeToSelectBoxValues: function( selectBox, hiddenName )
  {
    var i = 0;
    var values = "";
    while( i < selectBox.length )
    {
      if( i  > 0)
      {
        values += ",";
      }
      values += selectBox.options[i].value;
      i++;
    }
    hiddenName.value  = values;
    return true;
  },

  switchUserRoleColumns: function (event, isSourceDest, msg, noRoleMsg)
  {
    var indx;
    indx=0;

    sourceArray = this.source;
    targetArray = this.secondary;

    if ( !isSourceDest  ) // If we are moving from Dest to Source
    {
      var temp = sourceArray;
      sourceArray = targetArray;
      targetArray = temp;
    }

    if (sourceArray.length <=0) {
       alert(noRoleMsg);
       return false;

    }
    if (sourceArray.selectedIndex == -1 || sourceArray.selectedIndex > sourceArray.length-1 )
    {
      alert(msg);
      return false;
    }
    var rightIndex=0;
    var targetLength = targetArray.length;
    while (sourceArray.selectedIndex >= 0)
    {
      indx =sourceArray.selectedIndex;
      if( indx == -1 || indx > sourceArray.length-1)
          {
             alert(msg);
             return false;
          }

      tmpOpt = this.cloneMultipleRows(sourceArray.options[indx]);
      if (targetLength>0) {
          var i=targetLength;

          while(i >0){
              tmpOpt1 = this.cloneMultipleRows(targetArray.options[i+rightIndex-1]);
              targetArray.options[i+rightIndex] = tmpOpt1;
              i--;
          }
      }
      sourceArray.options[indx].selected=false;
      targetArray.options[rightIndex] = tmpOpt;
      //targetArray.options[rigthIndex].selected=true;
      rightIndex = rightIndex+1;
      sourceArray.options[indx] = null;
    }  //end of while
    this.writeToSelectBoxValues( this.source, this.leftValues );
    this.writeToSelectBoxValues( this.secondary, this.rightValues );

    return false;
  },
  cloneMultipleRows: function (opt)
  {
      return new Option( opt.text, opt.value, opt.defaultSelected, opt.selected);
  }
};

widget.StepGroup = {};
widget.StepGroup.toggleStepGroup = function( e, activeTabStr )
{
  var event = e || window.event;
  var target = Event.findElement( event, 'li' );
  var tabs = $A($('dataCollectionContainer').getElementsByTagName('ul')[0].getElementsByTagName('li'));
  tabs.each( function( tab )
  {
    tab = $(tab);
    
    if ( tab.id == target.id )
    {
      tab.addClassName("active");
      $(tab.getAttribute("bb:groupId")).show();
      $('activeTabSpanId_'+tab.id).innerHTML = activeTabStr;      
    }
    else
    {
      tab.removeClassName("active");
      $(tab.getAttribute("bb:groupId")).hide();
      $('activeTabSpanId_'+tab.id).innerHTML = "";
    }
  });
  Event.stop( event );
};
/**
 * Show a specific step group
 * @param number - the number of the group in order of the tabs at the top
 * (e.g. 1 is the leftmost tab, then 2, 3, etc.)
 */
widget.StepGroup.showStepGroup = function( number )
{
  var target = $('stepGroupTab_' + number);
  var tabs = $A($('dataCollectionContainer').getElementsByTagName('ul')[0].getElementsByTagName('li'));
  tabs.each( function( tab )
  {
    tab = $(tab);
    if ( tab.id == target.id )
    {
      tab.addClassName("active");
      $(tab.getAttribute("bb:groupId")).show();
    }
    else
    {
      tab.removeClassName("active");
      $(tab.getAttribute("bb:groupId")).hide();
    }
  });
}
widget.StepGroup.showAllStepGroupsForSubmit = function()
{
  var tabs = $A($('dataCollectionContainer').getElementsByTagName('ul')[0].getElementsByTagName('li'));
  tabs.each( function( tab )
  {
    $(tab.getAttribute("bb:groupId")).show();
  });
};
widget.StepGroup.revertAllStepGroupsAfterSubmit = function()
{
  var tabs = $A($('dataCollectionContainer').getElementsByTagName('ul')[0].getElementsByTagName('li'));
  tabs.each( function( tab )
  {
    if ( page.util.hasClassName(tab, 'active') )
    {
      $(tab.getAttribute("bb:groupId")).show();
    }
    else
    {
      $(tab.getAttribute("bb:groupId")).hide();
    }
  });
};
widget.ShowUnsavedChanges = Class.create();
widget.ShowUnsavedChanges.prototype =
{
  initialize: function( dataCollectionContainer, skipTextArea )
  {
    widget.ShowUnsavedChanges.enabled = true;
    widget.ShowUnsavedChanges.registerOnValueChangeCallback( widget.ShowUnsavedChanges.showIconsForUnsavedChanges );
    var container = $(dataCollectionContainer);
    var inputs = $A(container.getElementsByTagName('input'));
    var selects = $A(container.getElementsByTagName('select'));
    var textareas = $A(container.getElementsByTagName('textarea'));
    inputs.each( function( input )
    {
      input = $(input);
      var type = input.type.toLowerCase();
      if ( type == 'checkbox' || type == 'radio' )
      {
        input._defaultValue = input.checked;
      } else if ( type == 'hidden' || type == 'text' )
      {
        input._defaultValue = input.value;
      } else
      {
        input._defaultValue = '';
      }
      if ( type != 'hidden' && type != 'button' && type != 'submit' )
      {
        input.observe( 'change', widget.ShowUnsavedChanges.onValueChange.bindAsEventListener() );
      }
    }.bind(this));
    selects.each( function( input ) //TODO: multiselect?
    {
      input = $(input);
      input._defaultValue = input.selectedIndex;
      input.observe( 'change', widget.ShowUnsavedChanges.onValueChange.bindAsEventListener() );
    }.bind(this));
    
    if ( skipTextArea == null || !skipTextArea )
    {
      textareas.each( function( input )
      {
        input = $(input);
        input._defaultValue = input.value;
        input.observe( 'change', widget.ShowUnsavedChanges.onValueChange.bindAsEventListener() );
      }.bind(this));
    }
  }
};

widget.ShowUnsavedChanges.onValueChangeCallback = function () {}; // do nothing
widget.ShowUnsavedChanges.registerOnValueChangeCallback = function( callbackFunction )
{
  widget.ShowUnsavedChanges.onValueChangeCallback = callbackFunction;
};

widget.ShowUnsavedChanges.dataElementsByStepGroup = {};
widget.ShowUnsavedChanges.onValueChange = function( event, elem )
{
    if ( widget.ShowUnsavedChanges.enabled )
    {
      var dataElement = null;
      if ( event )
      {
        dataElement = Event.findElement( event, 'li' );
      } else
      {
        dataElement = elem.parentNode;
        while ( dataElement != null && dataElement.tagName.toLowerCase() != 'li' )
        {
          dataElement = dataElement.parentNode;
          if ( dataElement.tagName.toLowerCase() == 'body' )
            {
              dataElement = null;
            }
        }
        if ( dataElement != null )
        {
          dataElement = $(dataElement);
        }
      }
      if ( !dataElement )
      {
        return;
      }

      var isDefault = true;
      var inputs = $A(dataElement.getElementsByTagName('input'));
      var selects = $A(dataElement.getElementsByTagName('select'));
      var textareas = $A(dataElement.getElementsByTagName('textarea'));
      inputs.each( function( input )
      {
        var type = input.type.toLowerCase();
        if ( type == 'checkbox' || type == 'radio' )
        {
          isDefault &= (input.checked == input._defaultValue );
        } else if ( type == 'hidden' || type == 'text' )
        {
          isDefault &= (input.value == input._defaultValue );
        }
      });
      selects.each( function( input ) //TODO: multiselect?
      {
        isDefault &= (input.selectedIndex == input._defaultValue );
      });
      textareas.each( function( input )
      {
        isDefault &= (input.value == input._defaultValue );
      });

      //trigger call back function
      widget.ShowUnsavedChanges.onValueChangeCallback( isDefault, dataElement );

    }
};

widget.ShowUnsavedChanges.showIconsForUnsavedChanges = function ( isDefault, dataElement )
{
  var imgs = dataElement.getElementsByTagName('img');
  var unsavedImg = null;
  for ( var i = 0; i < imgs.length; i++ )
  {
     if ( imgs[i].className.indexOf('unsavedChangeImg') >= 0 )
     {
       unsavedImg = $(imgs[i]);
       break;
     }
  }
  if ( unsavedImg != null )
  {
    if ( isDefault )
    {
      dataElement.removeClassName('dirty');
      unsavedImg.hide();
    }
    else
    {
      dataElement.addClassName('dirty');
      unsavedImg.show();
    }
    widget.ShowUnsavedChanges.updateStepGroupIndicator(dataElement, !isDefault);
  }

};

widget.ShowUnsavedChanges.updateStepGroupIndicator = function( dataElement, isDirty )
{
  if ( widget.ShowUnsavedChanges.enabled )
  {
    var tabs = $('dataCollectionContainer').getElementsByTagName('ul')[0].getElementsByTagName('li');
    if ( tabs.length > 0 && tabs[0].parentNode.parentNode.id == 'dataCollectionContainer' )
    {
      for( var t = 0; t < tabs.length; t++ )
      {
        var tab = tabs[t];
        var stepGroup = $(tab.getAttribute("bb:groupId"))
        var img = $(tab.getElementsByTagName('img')[0]);
        var link = $(tab.getElementsByTagName('a')[0]);
        var dataElements = widget.ShowUnsavedChanges.dataElementsByStepGroup[stepGroup.id];
        if ( dataElements == null )
        {
          dataElements = stepGroup.getElementsByTagName('li');
          widget.ShowUnsavedChanges.dataElementsByStepGroup[stepGroup.id] = dataElements;
        }

        var dirtyCount = 0;
        for ( var i = 0; i < dataElements.length; i++ )
        {
          if ( dataElements[i].className.indexOf('dirty') >= 0 )
          {
            dirtyCount++;
          }
        }
        if ( dirtyCount > 0 && !tab.isDirty)
        {
          img.show();
          link.setStyle({'paddingLeft': '20px'});
          tab.isDirty = true;
        }
        else if ( dirtyCount == 0 && tab.isDirty )
        {
          img.hide();
          link.setStyle({'paddingLeft': ''});
          tab.isDirty = false;
        }
      }
    }
  }
};
// Call this when the value of a hidden data element changes that you want to
// show an indicator for.  Otherwise changing the value of the hidden element directly will
// not cause an indicator to show.
widget.ShowUnsavedChanges.changeHiddenValue = function( hiddenElem, value )
{
  hiddenElem.value = value;
  if ( widget.ShowUnsavedChanges.enabled )
  {
    widget.ShowUnsavedChanges.onValueChange( null, hiddenElem );
  }
};
widget.ShowUnsavedChanges.enabled = false;

/**
 * Supports a counter widget attached to a textbox -- it keeps track of the number of
 * characters in the text box, and updates a counter whenever that number changes.
 * The counter turns red, and begins counting backwards, when the max number of charters
 * is exceeded.
 */
widget.TextBoxCounter = Class.create();
widget.TextBoxCounter.prototype =
{

  /**
   * Contructor
   *
   * @param textBoxId  The id of the textbox we're counting from
   * @param counterId  The id of the element that countains the count
   * @param maxCharCount  The max number of characters supported by this textbox
   * @param charactersRemainingLabel  The label for the character count element
   * @param charactersOverLimitLabel  The alternate label for the character element,
   *                                  for when the max # of characters has been
   *                                  exceeded.
   */
  initialize: function ( textBoxId, counterId, maxCharCount, charactersRemainingLabel, charactersOverLimitLabel )
  {

    this.textBoxElement    = $(textBoxId);
    this.counterElement    = $(counterId);
    this.maxCharCount      = maxCharCount;
    this.countLabelElement = this.counterElement.up().childNodes[0];

    this.charactersRemainingLabel = charactersRemainingLabel;
    this.charactersOverLimitLabel = charactersOverLimitLabel;

    // watch all keystrokes in the text area, and update the counter accordingly
    this.textBoxElement.observe
      ('keyup', this.updateCount.bindAsEventListener(this));

    this.registerCounter(textBoxId);

  },

  /**
   * Registers the current counter with the document, so that it can be accessed
   * from anywhere on the page.
   *
   * @param id The id with which this counter can be accessed
   */
  registerCounter: function(id)
  {
    if (!widget.TextBoxCounter.counters) widget.TextBoxCounter.counters = new Array();

    widget.TextBoxCounter.counters[id] = this;
  },


  /**
   * Update the counter.
   *
   * @param event The event that prompted this update, if any.
   */
  updateCount: function (event)
  {

    var chars = this.textBoxElement.value.length;

    // max character count exceeded
    if ( chars > this.maxCharCount )
    {
      this.counterElement.update(chars - this.maxCharCount).addClassName('bad');
      this.countLabelElement.nodeValue = this.charactersOverLimitLabel;
    }

    // max character count not exceeded
    else  if ( chars <= this.maxCharCount )
    {
      this.counterElement.update(this.maxCharCount - chars ).removeClassName('bad');
      this.countLabelElement.nodeValue = this.charactersRemainingLabel;
    }

  }
};

widget.ColorPalettePicker = Class.create();
widget.ColorPalettePicker.prototype =
{
    initialize: function( onChange )
  {
      widget.ColorPalettePicker.picker = this;

      this.onChange = onChange;
      var palettes = [];
      $A($('paletteLibraryPanel').getElementsByTagName('li')).each( function( li )
      {
        if ( page.util.hasClassName( li, 'palette' ) )
        {
          palettes.push( $(li) );
        }
      });
    this.palettes = palettes;
    this.libraryOpen = false;

    this.paletteLibraryPanel = $('paletteLibraryPanel');
    this.paletteLibraryButton = $('paletteLibraryButton');

        var updateHandler = this.updateCurrentSelection.bindAsEventListener( this );
       // this.paletteLibraryPanel.toggleLibrary();
        if( this.palettes )
        {
          this.palettes.each( function ( palette )
            {
              Event.observe( palette, "click", updateHandler );
            });
        }
        if( this.paletteLibraryButton )
        {
      Event.observe( 'paletteLibraryButtonLink', "click", this.toggleLibrary.bindAsEventListener( this ) );
    }
   },

  toggleLibrary: function( event )
  {
     var img = this.paletteLibraryButton.getElementsByTagName('img')[0]
    if ( this.libraryOpen )
    {
      // Collapse the library.
      img.src = "/images/db/p.gif"; 
      img.alt = page.bundle.getString("dynamictree.expand");
      this.paletteLibraryPanel.hide();
      this.libraryOpen = false;
    }
    else
    {
      // Expand the library.
      img.src = "/images/db/m.gif";
      img.alt = page.bundle.getString("dynamictree.collapse");
      this.paletteLibraryPanel.show();
      this.libraryOpen = true;
    }
    Event.stop( event );
   },


   selectColorPaletteByExtRef: function( extRef )
   {
     var eventElement = $(extRef);
     this.selectColorPaletteByElement( eventElement );
   },

   selectColorPaletteByElement: function( eventElement )
   {
      this.palettes.each( function ( p )
      {
      var a = $(p.getElementsByTagName('a')[0]);
      if (a.hasClassName("selected")) a.removeClassName('selected');
      });

    var selPal = eventElement;
    while ( selPal != null && selPal.tagName != 'LI' )
    {
      selPal = selPal.parentNode;
      if ( selPal.tagName == 'BODY' )
      {
        selPal = null;
      }
    }
    $(selPal.getElementsByTagName('a')[0]).addClassName('selected');
    widget.ShowUnsavedChanges.changeHiddenValue( $('currentPaletteExtRef'), selPal.id == 'currentsystemthemecolorpalette'? '':selPal.id);
    $('currentPaletteLabel').update( $(selPal.id + '_label').innerHTML );
    this.copyStyles( selPal.down('.schemePreview'), $('currentPalettePreview') );
    this.copyStyles( selPal.down('.schemePreviewHead'), $('currentPalettePreviewHead') );
    this.copyStyles( selPal.down('.schemePreviewBody'), $('currentPalettePreviewBody') );
    this.copyStyles( selPal.down('.modulePreview'), $('currentModulePalettePreview') );
    this.copyStyles( selPal.down('.modulePreviewHead'), $('currentModulePalettePreviewHead') );
    this.copyStyles( selPal.down('.modulePreviewBody'), $('currentModulePalettePreviewBody') );
    $('selectedPalette').show();

   },
  updateCurrentSelection: function( event )
  {
    var eventElement = Event.element( event );
    this.selectColorPaletteByElement( eventElement );
    eval( this.onChange );
    Event.stop( event );
  },

  copyStyles: function( s, d)
  {
     for (var i in s.style){
        try{
           d.style[i] = s.style[i];
        }
        catch (e){
            // do nothing with errors
        }
     }
   }

};
widget.ColorPalettePicker.picker = null;


