
function ChainedSelectorsUpdate()
{
    this.executePreActions();

    var firstSelectElem = this.firstSelect;
    var secondSelectElem = this.secondSelect;
    var values = this.data;
    var superVal = this.superValueId;
    var superValSelected = false;
    var options = firstSelectElem.options;

    var selectedOptions = new Array();
    var secondOptions = new Array();
    var index = 0;
    // collect the selected options in 1st SELECT
    for (i=0; i<options.length; i++) {
        var opt = options.item(i);
        if (opt.selected) {
            selectedOptions[index] = opt.value;
            if (opt.value == superVal) { superValSelected = true; }
            index++;
        }
    }
    // find the options for 2nd SELECT
    if (this.secondSelectOptionPersistentId != null) {
        secondOptions[0] = this.secondSelectOptionPersistentId;
        secondOptions[1] = this.secondSelectOptionPersistentValue;
        index = 2;
    } else {
        index = 0;
    }
    if (superValSelected) {
        for (i=0; i<values.length; i+=4) {
            secondOptions[index] = values[i+2];
            secondOptions[index+1] = values[i+3];
            index += 2;
        }
    } else {
        for (i=0; i<values.length; i+=4) {
            var firstValue = values[i];
            // look if the 1st option is selected
            for (selected in selectedOptions) {
                if (firstValue == selectedOptions[selected]) {
                    secondOptions[index] = values[i+2];
                    secondOptions[index+1] = values[i+3];
                    index += 2;
                }
            }
        }
    }

    // reuse the available/existing 2nd SELECT options
    var maxReuse = index/2; // can fit in all elements in 2nd SELECT
    var noElemsToAdd = 0;
    var noElemsToSubstract = secondSelectElem.length - maxReuse;
    if (noElemsToSubstract < 0) { // can't fit in all elements
        maxReuse = secondSelectElem.length;
        noElemsToAdd = 0-noElemsToSubstract;
        noElemsToSubstract = 0;
    }
    for (i=0; i<maxReuse; i++) {
        var option = secondSelectElem.options[i];
        option.value = secondOptions[i*2];
        option.innerHTML = secondOptions[(i*2)+1];
    }
    // do we need to add elements to 2nd SELECT?
    index = maxReuse*2;
    for (i=noElemsToAdd; i>0; i--) {
        var elem = document.createElement('option');
        secondSelectElem.appendChild(elem);
        elem.value = secondOptions[index];
        elem.innerHTML = secondOptions[index+1];
        index += 2;
    }
    // do we need to substract elements to 2nd SELECT?
    index = secondSelectElem.length-1;
    for (i=noElemsToSubstract; i>0; i--) {
        secondSelectElem.remove(index);
        index--;
    }
    // IE Hack to resize the 2nd select element
    secondSelectElem.style.width="50%";
    secondSelectElem.style.width="auto";
}

// Pre-Actions and Post-Actions must be of type "function action(chainedSelectors)"
// Add the given function as an action to be executed BEFORE update()
function ChainedSelectorsAddPreAction(functionObject)
{
    this.preActions.push(functionObject);
}

// Add the given function as an action to be executed AFTER update()
function ChainedSelectorsAddPostAction(functionObject)
{
    this.postActions.push(functionObject);
}

function ChainedSelectorsExecutePreActions()
{
    for (idx in this.preActions) {
        this.preActions[idx](this);
    }
}

function ChainedSelectorsExecutePostActions()
{
    for (idx in this.postActions) {
        this.postActions[idx](this);
    }
}

// Select the options given for the SELECT element
function ChainedSelectorsSelectOptions(selectElem, optionValues)
{
    var options = selectElem.options;
    for (i=0; i<options.length; i++) {
        var option = options[i];
        if (optionValues.indexOf(option.value) > -1) {
            option.selected = true;
        } else {
            option.selected = false;
        }
    }
}

function ChainedSelectorsSelectSecondOptions(optionValues)
{
    ChainedSelectorsSelectOptions(this.secondSelect, optionValues);
}
function ChainedSelectorsSelectFirstOptions(optionValues)
{
    ChainedSelectorsSelectOptions(this.firstSelect, optionValues);
}

function ChainedSelectors(firstSelectId, secondSelectId, data, superValueId,
        // second SELECT option that's always available
        secondSelectOptionPersistentId, secondSelectOptionPersistentValue)
{
    this.firstSelect = document.getElementById(firstSelectId);     // 1st SELECT element
    this.secondSelect = document.getElementById(secondSelectId);   // 2nd SELECT element
    this.data = data; // array of select values
    this.superValueId = superValueId; // the value in 1st SELECT that display all options for 2nd SELECT
    this.secondSelectOptionPersistentId = secondSelectOptionPersistentId;
    this.secondSelectOptionPersistentValue = secondSelectOptionPersistentValue;

    this.preActions = new Array();
    this.postActions = new Array();

    this.update = ChainedSelectorsUpdate; // function to update the 2nd selector
    this.addPreAction = ChainedSelectorsAddPreAction; // add pre action
    this.addPostAction = ChainedSelectorsAddPostAction; // add post action
    this.executePreActions = ChainedSelectorsExecutePreActions;
    this.executePostActions = ChainedSelectorsExecutePostActions;
    this.selectFirstOptions = ChainedSelectorsSelectFirstOptions;
    this.selectSecondOptions = ChainedSelectorsSelectSecondOptions;
}

// These are utility Pre/Post Actions
function checkApaSaja(select)
{
  // assume first element is 'Apa Saja'
  for (var x=1; x<select.options.length; x++)
  {
    if (select.options[x].selected) {
        select.options[0].selected = false;
        return null;
    }
  }
}

function checkMaxSelected(select, max, alertMsg)
{
   // initialize the counter
   var counter = 0;
   var alerted = false;

   // Loop through the select and unselect the ones after max
   for (var x=0; x<select.options.length; x++) {
      // If an element is selected, increment the counter
      if (select.options[x].selected == true) {
         counter++;
         if (counter > max) {
            if (!alerted) {
                alert(alertMsg);
                alerted = true;
            }
            select.options[x].selected = false;
         }
      }
   }
}

function csCheckApaSaja(cs) { checkApaSaja(cs.firstSelect); }
function csCheckMax5ProvincesSelected(cs) { checkMaxSelected(cs.firstSelect, 5, "Maksimum 5 Propinsi"); }