<!-- 
 | 
  The MultiselectPicklist component implements a multiselect picklist similar 
 | 
  to that seen when adding tabs to a Force.com application. 
 | 
   
 | 
  HTML elements use the same classes as the native multiselect picklist, to 
 | 
  keep visual consistency in the UI. 
 | 
   
 | 
  In addition to the visible elements, the component contains two hidden input 
 | 
  elements, the purpose of which is to hold a string representation of the 
 | 
  contents of each listbox. As options are added, removed or moved within the 
 | 
  listboxes, the content of the hidden elements is synchronized to the content 
 | 
  of the listboxes. When the Visualforce page is submitted, the  
 | 
  MultiselectController updates its SelectOption[] variables from these hidden  
 | 
  elements. 
 | 
 --> 
 | 
<apex:component controller="MultiselectController" id="MultiselectController"> 
 | 
  <apex:attribute name="leftLabel" description="Label on left listbox." 
 | 
    type="String" required="true" /> 
 | 
  <apex:attribute name="rightLabel" description="Label on right listbox." 
 | 
    type="String" required="true" /> 
 | 
  <apex:attribute name="showUpDown" description="show Up Down btn on right listbox." 
 | 
    type="Boolean" required="true" /> 
 | 
  <apex:attribute name="size" description="Size of listboxes." 
 | 
    type="Integer" required="true" /> 
 | 
  <apex:attribute name="width" description="Width of listboxes." 
 | 
    type="String" required="true" /> 
 | 
  
 | 
  <apex:attribute name="leftOptions" 
 | 
    description="Options list for left listbox." type="SelectOption[]" 
 | 
    required="true" assignTo="{!leftOptions}" /> 
 | 
  <apex:attribute name="rightOptions" 
 | 
    description="Options list for right listbox." type="SelectOption[]" 
 | 
    required="true" assignTo="{!rightOptions}" /> 
 | 
  
 | 
  <apex:outputPanel id="multiselectPanel" layout="block" styleClass="duelingListBox"> 
 | 
    <table class="layout"> 
 | 
      <tbody> 
 | 
        <tr> 
 | 
          <td class="selectCell"> 
 | 
            <select id="{!$Component.multiselectPanel}:leftList"  
 | 
              class="multilist" multiple="multiple" size="{!size}"  
 | 
              style="width: {!width};"> 
 | 
              <!--  
 | 
                Visualforce prepends the correct prefix to the outputLabel's  
 | 
                'for' attribute 
 | 
              --> 
 | 
              <optgroup style="text-decoration:none;" label="{!leftLabel}"> 
 | 
              <apex:repeat value="{!leftOptions}" var="option"> 
 | 
                <option value="{!option.value}">{!option.label}</option> 
 | 
              </apex:repeat> 
 | 
              </optgroup> 
 | 
            </select> 
 | 
          </td> 
 | 
          <td class="buttonCell"> 
 | 
            <apex:outputPanel layout="block" styleClass="text"> 
 | 
              <apex:outputLink value="javascript:moveSelectedOptions('{!$Component.multiselectPanel}:leftList',  
 | 
                  '{!$Component.multiselectPanel}:rightList', '{!$Component.leftHidden}',  
 | 
                  '{!$Component.rightHidden}');" 
 | 
                id="btnRight"> 
 | 
                <apex:image value="/s.gif" alt="Add" styleClass="rightArrowIcon" 
 | 
                  title="Add" /> 
 | 
              </apex:outputLink> 
 | 
            </apex:outputPanel> 
 | 
            <apex:outputPanel layout="block" styleClass="text"> 
 | 
              <apex:outputLink value="javascript:moveSelectedOptions('{!$Component.multiselectPanel}:rightList',  
 | 
                  '{!$Component.multiselectPanel}:leftList', '{!$Component.rightHidden}',  
 | 
                  '{!$Component.leftHidden}');" 
 | 
                id="btnLeft"> 
 | 
                <apex:image value="/s.gif" alt="Remove" 
 | 
                  styleClass="leftArrowIcon" title="Remove" /> 
 | 
              </apex:outputLink> 
 | 
            </apex:outputPanel> 
 | 
          </td> 
 | 
          <td class="selectCell"> 
 | 
            <select id="{!$Component.multiselectPanel}:rightList"  
 | 
              class="multilist" multiple="multiple" size="{!size}"  
 | 
              style="width: {!width};"> 
 | 
              <optgroup style="text-decoration:none;" label="{!rightLabel}"> 
 | 
              <apex:repeat value="{!rightOptions}" var="option"> 
 | 
                <option value="{!option.value}">{!option.label}</option> 
 | 
              </apex:repeat> 
 | 
              </optgroup> 
 | 
            </select> 
 | 
          </td> 
 | 
<apex:outputPanel layout="none" rendered="{!showUpDown}"> 
 | 
          <td class="buttonCell"><apex:outputPanel layout="block" 
 | 
              styleClass="text">Up</apex:outputPanel> 
 | 
            <apex:outputPanel layout="block" styleClass="text"> 
 | 
              <apex:outputLink value="javascript:slideSelectedOptionsUp('{!$Component.multiselectPanel}:rightList',  
 | 
                  '{!$Component.rightHidden}');" 
 | 
                id="upBtn"> 
 | 
                <apex:image value="/s.gif" alt="Up" styleClass="upArrowIcon" 
 | 
                  title="Up" /> 
 | 
              </apex:outputLink> 
 | 
            </apex:outputPanel> 
 | 
            <apex:outputPanel layout="block" styleClass="text"> 
 | 
              <apex:outputLink value="javascript:slideSelectedOptionsDown('{!$Component.multiselectPanel}:rightList',  
 | 
                  '{!$Component.rightHidden}');" 
 | 
                id="downBtn"> 
 | 
                <apex:image value="/s.gif" alt="Down" styleClass="downArrowIcon" 
 | 
                  title="Down" /> 
 | 
              </apex:outputLink> 
 | 
            </apex:outputPanel> 
 | 
            <apex:outputPanel layout="block" styleClass="text">Down</apex:outputPanel> 
 | 
          </td> 
 | 
</apex:outputPanel> 
 | 
        </tr> 
 | 
      </tbody> 
 | 
    </table> 
 | 
    <apex:inputHidden value="{!leftOptionsHidden}" id="leftHidden" /> 
 | 
    <apex:inputHidden value="{!rightOptionsHidden}" id="rightHidden" /> 
 | 
  </apex:outputPanel> 
 | 
  <script type="text/javascript"> 
 | 
    if (!buildOutputString) { 
 | 
      // Create a string from the content of a listbox 
 | 
      var buildOutputString = function(listBox, hiddenInput) { 
 | 
        var str = ''; 
 | 
  
 | 
        for ( var x = 0; x < listBox.options.length; x++) { 
 | 
          str += encodeURIComponent(listBox.options[x].value) + '&' 
 | 
              + encodeURIComponent(listBox.options[x].text) + '&'; 
 | 
        } 
 | 
        str.length--; 
 | 
  
 | 
        hiddenInput.value = str.slice(0, -1); 
 | 
      } 
 | 
    } 
 | 
  
 | 
    if (!moveSelectedOptions) { 
 | 
      // Move the selected options in the idFrom listbox to the idTo 
 | 
      // listbox, updating the corresponding strings in idHdnFrom and 
 | 
      // idHdnTo 
 | 
      var moveSelectedOptions = function(idFrom, idTo, idHdnFrom, idHdnTo) { 
 | 
        listFrom = document.getElementById(idFrom).children[0]; 
 | 
        listTo = document.getElementById(idTo).children[0]; 
 | 
  
 | 
        for ( var x = 0; x < listTo.children.length; x++) { 
 | 
          listTo.children[x].selected = false; 
 | 
        } 
 | 
  
 | 
        for ( var x = 0; x < listFrom.children.length; x++) { 
 | 
          if (listFrom.children[x].selected == true) { 
 | 
            listTo.appendChild(listFrom.children[x]); 
 | 
            x--; 
 | 
          } 
 | 
        } 
 | 
  
 | 
        listTo.focus(); 
 | 
  
 | 
        buildOutputString(listFrom, document.getElementById(idHdnFrom)); 
 | 
        buildOutputString(listTo, document.getElementById(idHdnTo)); 
 | 
      } 
 | 
    } 
 | 
  
 | 
    if (!slideSelectedOptionsUp) { 
 | 
      // Slide the selected options in the idList listbox up by one position, 
 | 
      // updating the corresponding string in idHidden 
 | 
      var slideSelectedOptionsUp = function(idList, idHidden) { 
 | 
        listBox = document.getElementById(idList); 
 | 
  
 | 
        var len = listBox.options.length; 
 | 
  
 | 
        if (len > 0 && listBox.options[0].selected == true) { 
 | 
          return; 
 | 
        } 
 | 
  
 | 
        for ( var x = 1; x < len; x++) { 
 | 
          if (listBox.options[x].selected == true) { 
 | 
            listBox.insertBefore(listBox.options[x], 
 | 
                listBox.options[x - 1]); 
 | 
          } 
 | 
        } 
 | 
  
 | 
        listBox.focus(); 
 | 
  
 | 
        buildOutputString(listBox, document.getElementById(idHidden)); 
 | 
      } 
 | 
    } 
 | 
  
 | 
    if (!slideSelectedOptionsDown) { 
 | 
      // Slide the selected options in the idList listbox down by one position, 
 | 
      // updating the corresponding string in idHidden 
 | 
      var slideSelectedOptionsDown = function(idList, idHidden) { 
 | 
        listBox = document.getElementById(idList); 
 | 
  
 | 
        var len = listBox.options.length; 
 | 
  
 | 
        if (len > 0 && listBox.options[len - 1].selected == true) { 
 | 
          return; 
 | 
        } 
 | 
  
 | 
        for ( var x = listBox.options.length - 2; x >= 0; x--) { 
 | 
          if (listBox.options[x].selected == true) { 
 | 
            listBox.insertBefore(listBox.options[x + 1], 
 | 
                listBox.options[x]); 
 | 
          } 
 | 
        } 
 | 
  
 | 
        listBox.focus(); 
 | 
  
 | 
        buildOutputString(listBox, document.getElementById(idHidden)); 
 | 
      } 
 | 
    } 
 | 
     
 | 
    // initialize the string representations 
 | 
    buildOutputString(document.getElementById('{!$Component.multiselectPanel}:leftList'),  
 | 
        document.getElementById('{!$Component.leftHidden}')); 
 | 
    buildOutputString(document.getElementById('{!$Component.multiselectPanel}:rightList'),  
 | 
        document.getElementById('{!$Component.rightHidden}')); 
 | 
  </script> 
 | 
</apex:component> 
 |