YAHOO.namespace("com.lgan");

(function(){
    YAHOO.com.lgan.ListDialog = function(el, userConfig){
        YAHOO.com.lgan.ListDialog.superclass.constructor.call(this, el, userConfig);
    };
    
    var
      Dom = YAHOO.util.Dom,
      Dialog = YAHOO.widget.Dialog,
      ListDialog = YAHOO.com.lgan.ListDialog,
      Event = YAHOO.util.Event,
      CustomEvent = YAHOO.util.CustomEvent,
      Button = YAHOO.widget.Button,
      Logger = YAHOO.com.lgan.Logger || YAHOO
    ;
    
    var EVENT_TYPES = {
      "LIST_REFRESHED": "ListRefreshed",
      "INOREX_REFRESHED": "InorExRefreshed",
      "SELECTION_REFRESHED": "SelectionRefreshed",
      "LIST_CLEARED": "ListCleared"
    };
    
    /**
      CSS class applied to the innerElement of all ListDialogs.
    */
    ListDialog.CSS_LISTDIALOG = "ListDialog"
    
    ListDialog.CSS_POOL = "base-list";
    
    ListDialog.CSS_OUTLIST = "out-list";
    
    ListDialog.INPUTNAME_OUTLIST = "filterdialoglist";
    
    ListDialog.INPUTNAME_VARID = "varid";
    
    ListDialog.INOREX_RADIONAME = "In_or_Ex";
    
    YAHOO.extend(ListDialog, Dialog, {
      /**
        State descriptor.
        @property isCurrentlyShortList
        @type Boolean
      */
      isCurrentlyShortList: null,
      
      /**
        State descriptor.  Mainly present to let handlers interact with elements on the body that don't exist until it has been set at least once.
        @property isCurrentlyShortList
        @type Boolean
      */
      hasBodyBeenSet: false,
      
      init: function(el, userConfig){
          Logger.log("Called.", "trace", "ListDialog.init");
          ListDialog.superclass.init.call(this, el);
          
          this.beforeInitEvent.fire(ListDialog);
          
          Dom.addClass(this.innerElement, ListDialog.CSS_LISTDIALOG);
          
          if(userConfig){
              this.cfg.applyConfig(userConfig, true);
          }
          
          //Define event handlers
          this.subscribe("changeBody", this.RefreshVarID, this, true);
          this.subscribe("changeBody", this.RefreshInorEx, this, true);
          this.subscribe("changeBody", this.MaybeSubscribeButtonHandlers, this, true);
          
          this.subscribe("beforeSubmit", this.SelectLongListValues, this, true);
          
          this.subscribe("beforeShow", this.RefreshList, this, true);
          
          this.initEvent.fire(ListDialog);
          Logger.log("Finished.", "trace", "ListDialog.init");
      },
      
      /**
        This executes just before submission of a long list, to make sure the values in the out-list are selected and recognized by the form handlers as selected.
      */
      SelectLongListValues: function(){
          if(!this.isCurrentlyShortList){
              var elSelector = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
              for(var i=0, imax=elSelector.options.length; i<imax; i++){
                  elSelector.options[i].selected = true;
              }
          }
          return true;
      },
      
      /**
        Defines new custom events.
      */
      initEvents: function(){
          ListDialog.superclass.initEvents.call(this);
          
          this.ListRefreshedEvent = this.createEvent(EVENT_TYPES.LIST_REFRESHED);
          this.ListRefreshedEvent.signature = CustomEvent.LIST;
          
          this.InorExRefreshedEvent = this.createEvent(EVENT_TYPES.INOREX_REFRESHED);
          this.InorExRefreshedEvent.signature = CustomEvent.LIST;
          
          this.SelectionRefreshedEvent = this.createEvent(EVENT_TYPES.SELECTION_REFRESHED);
          this.SelectionRefreshedEvent.signature = CustomEvent.LIST;
          
          this.ListClearedEvent = this.createEvent(EVENT_TYPES.LIST_CLEARED)
          this.ListClearedEvent.signature = CustomEvent.LIST;
      },
      
      /**
        Loads everything from the DEFAULT_CONFIG object.  Appends these configurations to the ones defined in the superclass.
      */
      initDefaultConfig: function(){
          ListDialog.superclass.initDefaultConfig.call(this);
          
          /**
            This should be set after the 'pool' is set.  The pool being set destroys this configuration list.
          */
          this.cfg.addProperty("preselected",{
            value:[],
            handler:this.RefreshSelection,
            validator: YAHOO.lang.isArray
          });
          
          this.cfg.addProperty("pool",{
            value:[],
            suppressEvent: true,
            validator: YAHOO.lang.isArray
          });
          
          this.cfg.addProperty("varid",{
            value:"",
            suppressEvent: true,
            handler: this.RefreshVarID,
            validator: YAHOO.lang.isString
          });
          
          this.cfg.addProperty("translator",{
            value:null,
            suppressEvent: true,
            validator: YAHOO.lang.isFunction
          });
          
          this.cfg.addProperty("enforcedistinct",{
            value:true,
            suppressEvent: true,
            validator: YAHOO.lang.isBoolean
          });
          
          /**
            Possible values:  "IN", "EX"
          */
          this.cfg.addProperty("inorex",{
            value:"IN",
            suppressEvent: true,
            handler: this.RefreshInorEx,
            validator: YAHOO.lang.isString
          });
          
          /**
            If the list of data values is greater than or equal to this number, a different selection mechanism will be used.
          */
          this.cfg.addProperty("intLongListLength",{
            value:10,
            suppressEvent: true,
            validator: YAHOO.lang.isNumber
          });
          
          this.cfg.addProperty("htmlBody_ShortList",{
            value:[
              '<form>',
              '  <input type="hidden" name="$varid" id="$dialogid_varid" />',
              '  <p>',
              '    <label><input type="radio" name="In_or_Ex" value="IN" id="In_or_Ex_IN" /> Include</label> &nbsp; or &nbsp; ',
              '    <label><input type="radio" name="In_or_Ex" value="EX" id="In_or_Ex_EX" /> Exclude</label> the following values:',
              '  </p>',
              '  <p style="text-align:center;"><select name="$outlistname" class="$poolcss $outlistcss" multiple="multiple" size="15"></select></p>',
              '</form>'
            ].join("\n"),
            validator: YAHOO.lang.isString
          });
          
          this.cfg.addProperty("htmlBody_LongList",{
            value:[
              '<form>',
              '  <input type="hidden" name="$varid" id="$dialogid_varid" />',
              '  <p>',
              '    <label><input type="radio" name="In_or_Ex" value="IN" id="In_or_Ex_IN" /> Include</label> &nbsp; or &nbsp;',
              '    <label><input type="radio" name="In_or_Ex" value="EX" id="In_or_Ex_EX" /> Exclude</label> the following values:',
              '  </p>',
              '  <p>Total number of items:  <dfn class="tally" id="$dialogid_tally"></dfn></p>',
              '  <p><label>Search: &nbsp; <input type="text" id="$dialogid_search" /></label> &nbsp; &nbsp; <button type="push" id="$dialogid_pare">Search</button></p>',
              '  <p><dfn class="tally" id="$dialogid_paretally">&nbsp;</dfn></p>',
              '  <table summary="Layout table, used for vertical alignment of buttons">',
              '    <tr>',
              '      <td><select class="$poolcss" multiple="multiple" size="15"></select></td>',
              '      <td id="$dialogid_movebuttons_container" class="buttoncol">',
              '        <p id="$dialogid_deselect_all_container"     ><button id="$dialogid_deselect_all"     >&lt;&lt;</button></p>',
              '        <p id="$dialogid_deselect_selected_container"><button id="$dialogid_deselect_selected">&lt;</button></p>',
              '        <p id="$dialogid_select_selected_container"  ><button id="$dialogid_select_selected"  >&gt;</button></p>',
              '        <p id="$dialogid_select_all_container"       ><button id="$dialogid_select_all"       >&gt;&gt;</button></p>',
              '      </td>',
              '      <td><select name="$outlistname" class="$outlistcss" multiple="multiple" size="15"></select></td>',
              '    </tr>',
              '  </table>',
              '</form>'
            ].join("\n"),
            validator: YAHOO.lang.isString
          });
      },
      
      /**
        Unsure whether clearing the list means submitting an empty list or aborting the procedure.  This is thus left as a non-terminal action for the dialog, merely deselecting all values.
      */
      ClearList: function(){
          var elListToEmpty = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
          if(this.isCurrentlyShortList === true){
              //Deselection procedure:  Set selected OPTIONS to not-selected
              for(var i=0; i<elListToEmpty.options.length; i++){
                  elListToEmpty.options[i].selected = false;
              }
          } else {
              //Deselection procedure:  Move options to pool element
              Logger.log("Clearing long list.", "trace", "ListDialog.ClearList");
              this._moveOptions("deselect", "all");
          }
          
          //Notify that the list is cleared.
          this.ListClearedEvent.fire(Dom.get(this.id + "_varid").value);
      },
      
      MaybeSubscribeButtonHandlers: function(){
          Logger.log("Called.", "trace", "ListDialog.MaybeSubscribeButtonHandlers");
          if(this.isCurrentlyShortList === null){
              Logger.log("Nop - uninitialized.", "trace", "ListDialog.MaybeSubscribeButtonHandlers");
          } else if(this.isCurrentlyShortList === true){
              Logger.log("Nop - no extra buttons in short-list dialog.", "trace", "ListDialog.MaybeSubscribeButtonHandlers");
          } else {
              var blnSuccess = true;
              
              blnSuccess = Event.addListener(this.id + "_deselect_all",      "click", function(p_e){this._moveOptions("deselect", "all");      Event.preventDefault(p_e);}, this, true) && blnSuccess;
              blnSuccess = Event.addListener(this.id + "_deselect_selected", "click", function(p_e){this._moveOptions("deselect", "selected"); Event.preventDefault(p_e);}, this, true) && blnSuccess;
              blnSuccess = Event.addListener(this.id + "_select_selected",   "click", function(p_e){this._moveOptions("select", "selected");   Event.preventDefault(p_e);}, this, true) && blnSuccess;
              blnSuccess = Event.addListener(this.id + "_select_all",        "click", function(p_e){this._moveOptions("select", "all");        Event.preventDefault(p_e);}, this, true) && blnSuccess;
              blnSuccess = Event.addListener(this.id + "_pare", "click", this.Pare, this, true) && blnSuccess;
              
              Logger.log(blnSuccess ? "Added all five event handlers." : "Failed to add at least one of the five event handlers.", "debug", "ListDialog.MaybeSubscribeButtonHandlers");
          }
          Logger.log("Finished.", "trace", "ListDialog.MaybeSubscribeButtonHandlers");
      },
      
      /**
        Semi-private utility method to move options between lists in the long list dialog form.
        @private
        @param {String} strAction Either "select" or "deselect"; order of the list elements is not preserved.
        @param {String} strCriterion Either "all" or "selected".
      */
      _moveOptions: function(strAction, strCriterion){
          Logger.log("Called.", "trace", "ListDialog._moveOptions");
          if(this.isCurrentlyShortList){
              Logger.log("Called, though the dialog is currently in short-list mode.", "error", "ListDialog._moveOptions");
          } else {
              var elSelFrom, elSelTo;
              switch(strAction){
                  case "select":
                      elSelFrom = Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0];
                      elSelTo = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
                  break;
                  case "deselect":
                      elSelFrom = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
                      elSelTo = Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0];
                  break;
                  default:
                      Logger.log("Unexpected action defined for transferring Option elements.", "error", "ListDialog._moveOptions");
                  break;
              }
              if(!(elSelFrom && elSelTo)){
                  Logger.log("Select element not found.", "error", "ListDialog._moveOptions");
              } else {
                  switch(strCriterion){
                      case "all":
                          //Quick 'n' easy - dump the lists' contents and re-populate the destination.
                          elSelFrom.options.length = 0;
                          elSelTo.options.length = 0;
                          this._PopulateSelectElement(elSelTo, this.cfg.getProperty("pool"));
                      break;
                      case "selected":
                          var elTmp;
                          for(var i=0; i<elSelFrom.options.length; ){
                              if(elSelFrom.options[i].selected){
                                  elSelTo.options[elSelTo.options.length] = new Option(elSelFrom.options[i].text, elSelFrom.options[i].value);
                                  
                                  //Delete option ('delete' keyword does not work with Option lists, at least in IE)
                                  elSelFrom.options[i] = null;
                              } else {
                                  i++;
                              }
                          }
                      break;
                      default:
                          Logger.log("Unexpected selection criterion for transferring Option elements.", "error", "ListDialog._moveOptions");
                      break;
                  }
              }
          }
          Logger.log("Finished.", "trace", "ListDialog._moveOptions");
      },
      
      Pare: function(p_e){
          Logger.log("Called.", "trace", "ListDialog.Pare");
          
          Event.preventDefault(p_e);
          
          var elText = Dom.get(this.id + "_search");
          if(!elText){
              Logger.log("Unable to find text input for list paring.", "error", "ListDialog.Pare");
          } else {
              var elReporter = Dom.get(this.id + "_paretally");
              if(!elReporter){
                  Logger.log("Unable to find reporting element for paring meta-output.", "error", "ListDialog.Pare");
              } else {
                  var elPool = Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0];
                  if(!elPool){
                      Logger.log("Unable to find pool select element.", "error", "ListDialog.Pare");
                  } else {
                      var elActive = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
                      if(!elActive){
                          Logger.log("Unable to find output list element.", "error", "ListDialog.Pare");
                      } else {
                          var funcTranslate = this.cfg.getProperty("translator");
                          if(!funcTranslate){
                              Logger.log("Unable to find translation function.  Nop.", "error", "ListDialog.Pare");
                          } else {
                              var rxParer = null;
                              try {
                                  rxParer = new RegExp(elText.value, "gi");
                              } catch(e) {
                                  Logger.log(e, "debug", "ListDialog.Pare");
                              }
                              
                              if(!rxParer){
                                  elReporter.innerHTML = "Unable to search based on the entered critera.";
                              } else {
                                  var aryPool = this.cfg.getProperty("pool");
                                  var aryParedOpts = [];
                                  
                                  var objActive = {};
                                  for(var i=0; i<elActive.options.length; i++){
                                      objActive[elActive.options[i].text] = true;
                                  }
                                  
                                  //Collect Options to generate our own list (don't use _PopulateSelectElement, that will re-pre-select Options).
                                  for(var i=0, tmpOpt; i<aryPool.length; i++){
                                      tmpOpt = funcTranslate(aryPool[i]);
                                      if(rxParer.test(tmpOpt.text)){
                                          if(!objActive[tmpOpt.text]){
                                              aryParedOpts.push(tmpOpt);
                                          }
                                      }
                                  }
                                  
                                  //Destructively output to list if results found
                                  if(aryParedOpts.length == 0){
                                      elReporter.innerHTML = "No results found for entered criteria.";
                                  } else {
                                      elPool.options.length = 0;
                                      for(var i=0; i<aryParedOpts.length; i++){
                                          elPool.options[elPool.options.length] = aryParedOpts[i];
                                      }
                                      
                                      //Output length of results
                                      elReporter.innerHTML = "Found " + aryParedOpts.length + " results.";
                                  }
                              }
                          }
                      }
                  }
              }
          }
          Logger.log("Finished.", "trace", "ListDialog.Pare");
          return true;
      },
      
      /**
        Utility method to completely, destructively populate a Select element with the <code>pool</code> configuration.
        Not meant to be called external to this class.
        @param {HTMLSelectElement} p_elList
        @param {Array} p_aryData Array of data elements the translator function will convert into HTML Options for <code>p_elList</code>.
      */
      _PopulateSelectElement: function(p_elList, p_aryData){
          var funcTranslate = this.cfg.getProperty("translator");
          if(!funcTranslate){
              Logger.log("No translation function supplied.  Nop.", "warning", "ListDialog.RefreshList");
          } else {
              if(!p_elList || p_elList.nodeName != "SELECT"){
                  Logger.log("Unable to find SELECT element.", "error","ListDialog._PopulateSelectElement");
              } else {
                  var aryData = p_aryData;
                  if(!aryData){
                      Logger.log("No data array available.  Nop.", "error", "ListDialog._PopulateSelectElement");
                  } else {
                      Logger.log("Passed guards.", "trace", "ListDialog._PopulateSelectElement");
                      //Build quick-lookup object of the values to preselect
                      var objPreselected = {}, aryPres = this.cfg.getProperty("preselected");
                      for(var i=0; i<aryPres.length; i++){
                          objPreselected[ aryPres[i] ] = true;
                      }
                      
                      Logger.log("objPreselected:\n" + YAHOO.lang.JSON.stringify(objPreselected), "debug", "ListDialog._PopulateSelectElement");
                      
                      //Begin destructive update
                      Logger.log("Beginning destructive update.", "trace", "ListDialog._PopulateSelectElement");
                      p_elList.options.length = 0;
                      
                      //Depending on whether we're enforcing distinct values (not labels), we have two ways of populating the list.
                      if(this.cfg.getProperty("enforcedistinct") == true){
                          Logger.log("Building distinct-valued list.", "trace", "ListDialog._PopulateSelectElement");
                          //Cache all Options in a hash table, overwriting as we go along (assuming there are less duplicates than distinct values)
                          var objOptions = {};
                          
                          for(var elOpt, i=0, imax=aryData.length; i<imax; i++){
                              elOpt = funcTranslate(aryData[i]);
                              if(objPreselected[ elOpt.value ]) elOpt.selected = true;
                              objOptions[elOpt.value] = elOpt;
                          }
                          
                          for(var val in objOptions){
                              p_elList.options[p_elList.options.length] = objOptions[val];
                          }
                      } else {
                          Logger.log("Building list with possible value duplicity.", "trace", "ListDialog._PopulateSelectElement");
                          //No distinct enforcement necessary - build the list in a simple loop.
                          for(var i=0, imax=aryData.length; i<imax; i++){
                              p_elList.options[i] = funcTranslate(aryData[i]);
                              if(objPreselected[ p_elList.options[i].value ]) p_elList.options[i].selected = true;
                          }
                      }
                      Logger.log("List built.", "trace", "ListDialog._PopulateSelectElement");
                  }
              }
          }
      },
      
      /**
        Assumes a namescheme for the radio buttons named ListDialog.INOREX_RADIONAME:  <var>id</var> = <code><var>name</var>_<var>value</var>.toUpperCase()</code>
      */
      RefreshInorEx: function(){
          if(!this.hasBodyBeenSet){
              Logger.log("Body hasn't been initialized yet; nop.", "debug", "ListDialog.RefreshInorEx");
          } else {
              var strSelect = this.cfg.getProperty("inorex");
              
              var idExpected = ListDialog.INOREX_RADIONAME + "_" + strSelect.toUpperCase();
              var elCheck = Dom.get(idExpected);
              
              if(!elCheck){
                  Logger.log("Radio element not found for refreshing:  '" + idExpected + "'", "error", "ListDialog.RefreshInorEx");
              } else {
                  elCheck.checked = true;
                  this.InorExRefreshedEvent.fire();
              }
          }
      },
      
      /**
        This is expected to be called in two locations:
        1.)  this.init
        2.)  this.RefreshList, conditionally
        @param {String} strConfigBody The key in the config object of the body to swap in.
      */
      RefreshBody: function(strConfigBody){
          Logger.log("Called.", "trace", "ListDialog.RefreshBody");
          var htmlNewBody = this.cfg.getProperty(strConfigBody).replace(
            /\$outlistname/g, ListDialog.INPUTNAME_OUTLIST
          ).replace(
            /\$varid/g, ListDialog.INPUTNAME_VARID
          ).replace(
            /\$poolcss/g, ListDialog.CSS_POOL
          ).replace(
            /\$outlistcss/g, ListDialog.CSS_OUTLIST
          ).replace(
            /\$dialogid/g, this.id
          );
          
          Logger.log("NEW LISTDIALOG BODY:\n" + htmlNewBody, "debug", "ListDialog.RefreshBody");
          this.hasBodyBeenSet = true;  //Set this before calling setBody, because the body-updated event fires right at the end of setBody.
          this.setBody(htmlNewBody);
          Logger.log("Finished.", "trace", "ListDialog.RefreshBody");
      },
      
      /**
        Populates the dialog body depending on the length of the list in the configuration.
      */
      RefreshList: function(){
          Logger.log("Called.", "trace", "ListDialog.RefreshList");
          Logger.log("Called in " + this.id, "debug", "ListDialog.RefreshList");
          var aryData = this.cfg.getProperty("pool");
          if(!aryData){
              Logger.log("No data array available.  Nop.", "warning", "ListDialog.RefreshList");
          } else {
              var intLongListLength = this.cfg.getProperty("intLongListLength");
              if(!this.cfg.checkNumber(intLongListLength)){
                  Logger.log("Unsure when to call a list long; configuration needs to be set.  Nop.", "warning", "ListDialog.RefreshList");
              } else {
                  //Maybe refresh body
                  var strNewBodyChoice = "";
                  if(this.isCurrentlyShortList === null){
                      //Initializing list
                      this.isCurrentlyShortList = (aryData.length < intLongListLength);
                      strNewBodyChoice = this.isCurrentlyShortList ? "htmlBody_ShortList" : "htmlBody_LongList";
                      Logger.log("Body will be initialized to '" + strNewBodyChoice + "'.", "debug", "ListDialog.RefreshList");
                  } else {
                      if(aryData.length >= intLongListLength){
                          //Body should be long-list input.
                          if(this.isCurrentlyShortList === true){
                              strNewBodyChoice = "htmlBody_LongList";
                              this.isCurrentlyShortList = false;
                          } else {
                              Logger.log("Body is currently long, and list is long.  No change in body.", "debug", "ListDialog.RefreshList");
                          }
                      } else {
                          //Body should be short-list input.
                          if(this.isCurrentlyShortList === false){
                              strNewBodyChoice = "htmlBody_ShortList";
                              this.isCurrentlyShortList = true;
                          } else {
                              Logger.log("Body is currently short, and list is short.  No change in body.", "debug", "ListDialog.RefreshList");
                          }
                      }
                  }
                  if(strNewBodyChoice == ""){
                      Logger.log("Not refreshing the dialog body.", "trace", "ListDialog.RefreshList");
                  } else {
                      this.RefreshBody(strNewBodyChoice);
                  }
                  
                  Logger.log("Body reset, now configuring list (" + aryData.length + " items).", "debug", "ListDialog.RefreshList");
                  
                  //Make sure the last filter's options are cleared from the long-list interface's out list
                  //Initialize the list's (or lists') contents.
                  if(this.isCurrentlyShortList){
                      Logger.log("Initializing contents to the entire option pool.", "trace", "ListDialog.RefreshList");
                      this._PopulateSelectElement(Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0], this.cfg.getProperty("pool"));
                  } else {
                      Logger.log("Initializing initial-display lists' support data.", "trace", "ListDialog.RefreshList");
                      
                      //Long list - need to make two Option arrays
                      var
                        aryCfgPreselected = this.cfg.getProperty("preselected"),
                        aryToPool = [],
                        aryToOut = []
                      ;
                      if(aryCfgPreselected.length == 0){
                          //No preselection - all values go to pool (this happens following the other branch anyway, but saves a run over the data by using this branch to skip steps)
                          aryToPool = this.cfg.getProperty("pool");
                      } else {
                          var funcTranslate = this.cfg.getProperty("translator");
                          if(!funcTranslate){
                              Logger.log("No translation function found.  List initialization aborted.", "error", "ListDialog.RefreshList");
                          } else {
                              Logger.log("Building preselected associative array.", "trace", "ListDialog.RefreshList");
                              var objPreselectedValues = {};
                              for(var elOpt, i=0, iMax=aryCfgPreselected.length; i<iMax; i++){
                                  objPreselectedValues[ aryCfgPreselected[i] ] = true;
                              }
                              
                              Logger.log("Splitting Option data pool with associate array '" + YAHOO.lang.JSON.stringify(objPreselectedValues) + "'.", "trace", "ListDialog.RefreshList");
                              for(var aryData = this.cfg.getProperty("pool"), j=0, jMax = aryData.length; j<jMax; j++){
                                  if(objPreselectedValues[ funcTranslate(aryData[j]).value ]){
                                      aryToOut.push(aryData[j]);
                                  } else {
                                      aryToPool.push(aryData[j]);
                                  }
                              }
                          }
                      }
                      var elPool = Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0];
                      //Initialize contents of the 'pool' list to the option pool minus pre-selected values
                      Logger.log(aryToPool.length + " elements to the pool.", "debug", "ListDialog.RefreshList");
                      this._PopulateSelectElement(elPool, aryToPool);
                      
                      //Initialize the out list to the preselected values
                      var elOut = Dom.getElementsByClassName(ListDialog.CSS_OUTLIST, "select", this.id)[0];
                      Logger.log(aryToOut.length + " elements to the out list.", "debug", "ListDialog.RefreshList");
                      this._PopulateSelectElement(elOut, aryToOut);
                      
                      //List the total tally of values, by counting the Option lists' length (don't use aryToPool and aryToOut lengths, they may be too long if the enforcedistinct configuration is set)
                      Dom.get(this.id + "_tally").innerHTML = elPool.options.length + elOut.options.length;
                  }
              }
          }
          
          this.ListRefreshedEvent.fire();
          Logger.log("Finished.", "trace", "ListDialog.RefreshList");
      },
      
      RefreshVarID: function(){
          Logger.log("Called.", "trace", "ListDialog.RefreshVarID");
          
          if(!this.hasBodyBeenSet){
              Logger.log("Body hasn't been initialized yet; nop.", "debug", "ListDialog.RefreshInorEx");
          } else {
              var elHid = Dom.get(this.id + "_varid");
              if(!elHid){
                  Logger.log("Unable to find element id'd '" + this.id + "_varid'.  Nop.", "error", "ListDialog.RefreshVarID");
              } else {
                  elHid.value = this.cfg.getProperty("varid");
              }
          }
          
          Logger.log("Finished.", "trace", "ListDialog.RefreshVarID");
      },
      
      RefreshSelection: function(){
          var arySelectedValues = this.cfg.getProperty("preselected");
          if(arySelectedValues.length <= 0){
              Logger.log("No values preselected.", "warning", "ListDialog.RefreshSelection");
          } else {
              //Index values for lookup during the loop
              var objSelectedValues = {};
              for(var i=0, imax=arySelectedValues.length; i<imax; i++){
                  objSelectedValues[ arySelectedValues[i] ] = true;
              }
              
              var elList = Dom.getElementsByClassName(ListDialog.CSS_POOL, "select", this.id)[0];
              for(var j=0, jmax=elList.options.length; j<jmax; j++){
                  elList.options[j].selected = !!objSelectedValues[ elList.options[j] ];
              }
              
              this.SelectionRefreshedEvent.fire();
          }
      },
      
      /**
        Workaround for a display bug in IE:  On initial display of the dialog, any Buttons appear to be blank.
      */
      show: function(){
          Logger.log("Called.", "trace", "ListDialog.show");
          ListDialog.superclass.show.call(this);
          for(var i=0, aryButtons = this._aButtons/*this.cfg.getProperty("buttons")*/; aryButtons.length && i<aryButtons.length; i++){
              //Logger.log(aryButtons[i], "debug", "ListDialog.show");
              aryButtons[i].addStateCSSClasses("hover");
              aryButtons[i].removeStateCSSClasses("hover");
          }
          Logger.log("Finished.", "trace", "ListDialog.show");
      }
    });
})();

