function checkVisibilityRules(changedElementId, changedElementType) {
  var changedElementValue = null;

  // fetch value assigned to the element that has changed,
  // each type of rule element provides its value in a different way
  switch (changedElementType) { 
    case 'CHECKBOX':           
    // checkbox - we take the value assigned to the input field only when
    // it is checked
    var checkboxElement = document.getElementById(changedElementId);          
    if (checkboxElement.checked) {
  		changedElementValue = 'CHECKED';
    } else {
  	  changedElementValue = 'UNCHECKED';
    }   
    break;             
          
    case 'RADIO':
        // radio group - take valeu from selected option
        var radioElement = document.getElementById(changedElementId);
        changedElementValue = radioElement.value;
        // radio groups are in fact a group of elements which has one set of
        // rules,
        // we need to remove the radio id suffix and then search for the rule
        changedElementId = changedElementId.substring(0, changedElementId.lastIndexOf("\."));          
        break;

    case 'SELECT':
        // select - drop down list - here we simply take value from the
        // selected element
        var selectElement = document.getElementById(changedElementId);          
        changedElementValue = selectElement.options[selectElement.selectedIndex].value;          
        break;          
  }

    
  // go through all rules that are based on element that has changed and check
  // how to react
  // var nodes = YAHOO.util.Selector.query('input.ruleElementId-' +
	// changedElementId);
  try {
    var nodes = getInputsWithClass('ruleElementId-' + changedElementId);
    for (i = 0; i < nodes.length; i++) {  
      var node = nodes[i];      
      var rule = decodeClassValue(node.className);  
      var ruleValue = node.value;
      // here we have a rule that is based on element that has changed,
      // now we need to compare if value assigned to this rule matches value of
      // the changed element
      if (changedElementValue == ruleValue) { 
        // entered value matches value of the rule, so we show elemetns with
        // "Show" rules and hide elements with "Hide" rules
        if (rule.showRule) {
          executeShowRule(rule.elementToModify);
        } else {
          executeHideRule(rule.elementToModify);
        }  
      } else {                 
    	  // entered value doesn't match value of the rule,
        // so we hide elements with "Show" rules and show elements with "Hide"
        // rules
        if (rule.showRule) {
          // before we hide an element we need to check wheter there is a
          // matching rule that makes it visible,
          // we can't hide elements which have at least one matching visible
          // rule
          if (!isMatchingOtherRule(rule, 'show')) {
        	  executeHideRule(rule.elementToModify);
          }
        } else {
          // before we show an element we need to check wheter there is a
          // matching rule that makes it hidden,
          // we can't show elements which have at least one matching hide rule
          if (!isMatchingOtherRule(rule, 'hide')) {
        	  executeShowRule(rule.elementToModify);
          }                  
        }                  
      }
    }     
  } catch (e) {
	  // alert("exception in checkVisibilityRules(): " + e);
  }
}

/**
 * This method searches for other rules that possibly match the current action
 * Using this method we can check if current action (show, hide) is the correct
 * choice as there might be other rules forcing opposite action.
 */
function isMatchingOtherRule(rule, action) {
  try {
  	if (!action) {
        action = 'show';
  	}
  	// collect rule nodes from html input fields storing the visibility rules
      var otherRuleNodes = getInputsWithClass('ruleType-' + action + 'Element elementId-' + rule.elementToModify);
  	var match = false;
  	for (j = 0; j < otherRuleNodes.length; j++) {
  	  var otherRuleNode = otherRuleNodes[j];
        var otherRule = decodeClassValue(otherRuleNode.className);  
        var ruleTriggeringElementValue = getElementValue(otherRule.changedElement);
  	  if (ruleTriggeringElementValue != null && ruleTriggeringElementValue == otherRuleNode.value) {
  		  // an other rule has been evaluated positive !
  		  // = trigerring element for such rule has a value matching the value
  			// configured for that rule :)
  		  match = true;
  	  }
    }	
    return match;
  } catch (e) {
	  // alert('exception in isMatchingOtherRule ' + e);
	  return false;
  }
}

function getInputsWithClass(requestedClassName) {
  try {
    var inputs = document.getElementsByTagName("input");
    var matchingInputs = new Array();
    if (inputs != null) {
      // alert(inputs);
      for (inputNumber = 0; inputNumber < inputs.length; inputNumber++) {
        if (inputs[inputNumber].className != null && inputs[inputNumber].className.indexOf(requestedClassName) != -1) {
          // alert(matchingInputs + ":" + inputs[inputNumber].className);
          matchingInputs[matchingInputs.length] = inputs[inputNumber];
        }
      }
    }
    
    return matchingInputs;
  } catch (e) {
    // alert ('exception in getInputsWithClass()');
  }
}



/**
 * class that holds "decoded" rule values
 */
function ParsedRule(changedElement, elementToModify, showRule) {
    this.changedElement = changedElement;
    this.elementToModify = elementToModify;
    this.showRule = showRule;
}



/**
 * gets a string describing a flat rule and prepares a rule object
 */ 
function decodeClassValue(classString) {
  try {
    var classParts = classString.split(" ");
    var changedElement; 
    var elementToModify;
    var ruleType;
    
    for (var k = 0; k < classParts.length; k++) {             
      if (classParts[k].indexOf('ruleType-') != -1) {
      	ruleType = classParts[k].replace('ruleType-', '');
      }
      
      if (classParts[k].indexOf('elementId-') != -1) {
      	elementToModify = classParts[k].replace('elementId-', '');
      }
  
      if (classParts[k].indexOf('ruleElementId-') != -1) {
      	changedElement = classParts[k].replace('ruleElementId-', '');
      }        
    }
  
    if (ruleType == "showElement") {
      return new ParsedRule(changedElement, elementToModify, true);
    } else {
    	return new ParsedRule(changedElement, elementToModify, false);   
    } 
  } catch (e) {
	  // alert ('exception in decodeClassValue()');
	  return null;
  }
}

/**
 * This method allows collecting value from a trigerring element Triggering
 * elements are : - checkboxes - select elements - radio groups The method
 * requires elementId of the given element. The logic required to parse
 * different html structures for these elements is hidden in this method
 */
function getElementValue(elementId) {
  // fetch value assigned to the element that has changed,
  // each type of rule element provides its value in a different way
  try {
    var elem = document.getElementById(elementId);
    if (null == elem) {
      elem = document.getElementById(elementId + '.0'); // find radio elements
      // alert('elemId: ' + elem.getAttribute('id'));
      // radio elements get id with addional numbers .x marking each radio
      // option
      // there will not be a value returned when searching for the main
      // element id in case of radio button
    }
    var nodeName = elem.nodeName;
  
    var elementValue = null;
  
    if (nodeName.toUpperCase() == 'INPUT') { 
      if (elem.getAttribute('type').toUpperCase() == 'CHECKBOX') {
        // for checkboxes returned values are either 'CHECKED' or 'UNCHECKED'
    		if (elem.checked) {
    		  elementValue = 'CHECKED';
    		} else {
    		  elementValue = 'UNCHECKED';
    		}   
  	  } else if (elem.getAttribute('type').toUpperCase() == 'RADIO') { 
    	  // for radio buttons we need to run through the radio group searching
    		// for the selected option
    	  // value of the selected option is taken as the value of the radio group
    		var elementName = elem.getAttribute('name');
    		var radioBtns = document.getElementsByName(elementName);
    		for (k = 0; k < radioBtns.length; k++) {
    		  if (radioBtns[k].checked == true) {
      			elementValue = radioBtns[k].value;
    		  }
    		}
  	  }
    } else if (nodeName.toUpperCase() == 'SELECT') {
    // select element is handled in easy way - value of current selection is
    // returned
	  elementValue = elem.options[elem.selectedIndex].value;          
    }    
    // alert('returned: ' + elementValue);
    return elementValue;  
    
  } catch (e) {
	  // alert('exception in getElementValue()');
	  return null;
  }
}


  
function executeShowRule(elementToShowId) {
	try {
  // alert("execute rule +++" + elementToShowId);
  var element = document.getElementById("wrapper" + elementToShowId);
  if (element != null) {
    element.style.display = "block";
  }

  // this element is now visible so we need to remove it from the "non
  // validation" elements
  var hiddenInputFieldName = elementToShowId.replace("element", "");
  var hiddenInputFieldId = "hiddenElement_" + hiddenInputFieldName;
  hiddenInputFieldName = "workflow.hiddenElements[" + hiddenInputFieldName + "]";       
  var hiddenInput = document.getElementById(hiddenInputFieldId);
  if (hiddenInput != null) {
    // alert("remove" + hiddenInputFieldName);
    hiddenInput.parentNode.removeChild(hiddenInput);
  }
	} catch (e) {
		// alert ('exception in executeShowRule()');
	}
}


function executeHideRule(elementToHideId) {
	try {
  // alert("execute rule ---" + elementToHideId);
  var element = document.getElementById("wrapper" + elementToHideId);
  if (element != null) {
    element.style.display = "none";
  }    

  // server side validation is going to validate all fields that aren't
  // present on the "hiddenElements" list
  // to avoid validation of a hidden element we need to add it to that list -
  // create hidden input field with correct name
  var hiddenInputFieldName = elementToHideId.replace("element", "");
  var hiddenInputFieldId = "hiddenElement_" + hiddenInputFieldName;
  hiddenInputFieldName = "workflow.hiddenElements[" + hiddenInputFieldName + "]";
  // first we need to check if such element does exist
  var hiddenInput = document.getElementById(hiddenInputFieldId);
  
  if (hiddenInput == null) {        
  	// alert("add" + hiddenInputFieldName);
  	var hiddenElementsParent = document.getElementById('hiddenElementsParent');
    var newHiddenElement = document.createElement("input");
    newHiddenElement.setAttribute("name", hiddenInputFieldName);
    newHiddenElement.setAttribute("id", hiddenInputFieldId);
    newHiddenElement.setAttribute("value", "true");
    newHiddenElement.setAttribute("type", "hidden");
  	hiddenElementsParent.appendChild(newHiddenElement);
  }
	} catch(e) {
		// alert ('exception in executeHideRule() ')
	}
}  




/** -- FLOATING CALENDAR FUNCTIONS - START --* */
function showCalendar(calendarContainerId, targetFieldId, datePattern, locale) {
  var displayDate = new Date();    
  // displayDate.setDate(22);
  // currently parsing of the selected date is not implemented so selected and
	// today are equal

  var selectedDate = new Date();    
  
  hideCalendar(calendarContainerId);
  createCalendar(calendarContainerId, selectedDate, datePattern, targetFieldId, displayDate, locale);     
  return false;
}


function findPos(obj, objToSet) {
  var curleft = curtop = 0;
  if (obj.offsetParent) {
    do {
      curleft += obj.offsetLeft;
      curtop += obj.offsetTop;
    } while (obj = obj.offsetParent);
    // alert(curleft + ":" + curtop);

    objToSet.style.left = curleft + "px";      
    objToSet.style.top = curtop + "px";            
  }
}
           

    

function createCalendar(calendarContainerId, displayDate, datePattern, targetFieldId, selectedDate, locale) {        
  var calendarContainer = document.getElementById(calendarContainerId);      
  var calendarDiv = document.createElement("div");
  calendarDiv.id = calendarContainerId + "content";
  calendarDiv.className = "ifCalendar";
  calendarDiv.appendChild(createCalendarHeader(calendarContainerId, displayDate, datePattern, targetFieldId, selectedDate, locale));      
  calendarDiv.appendChild(createCalendarContent(calendarContainerId, displayDate, datePattern, targetFieldId, selectedDate));

  findPos(calendarContainer, calendarDiv);           
  document.body.appendChild(calendarDiv);
  calendarDiv.setAttribute('style', calendarDiv.getAttribute('style'));
}



function createCalendarHeader(calendarContainerId, displayDate, datePattern, targetFieldId, selectedDate, locale) {              
  var leftHeaderDiv = document.createElement("div");
  leftHeaderDiv.className = "ifCalendarLeftHeader";
  
  var rightHeaderDiv = document.createElement("div");
  rightHeaderDiv.className = "ifCalendarRightHeader";
  
  var headerContent = document.createElement("div");
  headerContent.className = "ifHeaderContent";
  
  leftHeaderDiv.appendChild(rightHeaderDiv);
  rightHeaderDiv.appendChild(headerContent);
  
  var prevLink = document.createElement('a');
  prevLink.setAttribute('href', 'javascript:void();');
  prevLink.onclick = new Function('prevMonth("' + displayDate.toString() + '", "' + calendarContainerId + '", "' + targetFieldId + '", "' + datePattern + '", "' + selectedDate.toString() + '", "' + locale + '");return false;');
  prevLink.className = "ifCalendatNavigation ifPrevMonth";
  prevLink.appendChild(document.createTextNode("\u00a0"));
  headerContent.appendChild(prevLink);

  var monthSpan = document.createElement('span');
  monthSpan.appendChild(document.createTextNode(localizationTable['month'+displayDate.getMonth()] + " " + displayDate.getFullYear()));        
  headerContent.appendChild(monthSpan);
  
  var nextLink = document.createElement('a');
  nextLink.setAttribute('href', 'javascript:void();');
  nextLink.onclick = new Function('nextMonth("' + displayDate.toString() + '", "' + calendarContainerId + '", "' + targetFieldId + '", "' + datePattern + '", "' + selectedDate.toString()+ '", "' + locale + '");return false;');
  nextLink.className = "ifCalendatNavigation ifNextMonth";
  nextLink.appendChild(document.createTextNode("\u00a0"));
  headerContent.appendChild(nextLink);        
  
  // alert("createCalendarContentEnd");
  return leftHeaderDiv;
}


function prevMonth(oldDisplayDateString, calendarContainerId, targetFieldId, datePattern, selectedDateString, locale) {
  // alert(oldDisplayDateString + ":" + selectedDateString);
  var oldDisplayDate = new Date(oldDisplayDateString);
  var selectedDate = new Date(selectedDateString);
  hideCalendar(calendarContainerId);
  oldDisplayDate.setMonth(oldDisplayDate.getMonth() - 1);        
  createCalendar(calendarContainerId, oldDisplayDate, datePattern, targetFieldId, selectedDate, locale);        
}

function nextMonth(oldDisplayDateString, calendarContainerId, targetFieldId, datePattern, selectedDateString, locale) {        
  var oldDisplayDate = new Date(oldDisplayDateString);
  var selectedDate = new Date(selectedDateString);
  hideCalendar(calendarContainerId);
  oldDisplayDate.setMonth(oldDisplayDate.getMonth() + 1);
  createCalendar(calendarContainerId, oldDisplayDate, datePattern, targetFieldId, selectedDate, locale);        
}

    
function createCalendarContent(calendarContainerId, displayDate, datePattern, targetFieldId, selectedDate) {      
  var calendarContentDiv = document.createElement("div");
  calendarContentDiv.className = "ifCalendarContent";

  var startDate = new Date(displayDate.toString());        
  startDate.setDate(1);
  var startMonth = startDate.getMonth();

  do {
      calendarContentDiv.appendChild(createCalendarWeek(startDate, calendarContainerId, startMonth, datePattern, targetFieldId, selectedDate));
  } while (startMonth == startDate.getMonth());    

  calendarContentDiv.appendChild(createCloseRow(calendarContainerId));
  return calendarContentDiv;
}

    
function getCalendarDayOfTheWeek(jsDayOfTheWeek) {
  if (jsDayOfTheWeek == 0) {
    return 6;
  } else {
    return jsDayOfTheWeek - 1;
  }         
}



function createCalendarWeek(startDate, calendarContainerId, startMonth, datePattern, targetFieldId, selectedDate) {
  // alert("create week");

  var weekDiv = document.createElement("div");
  weekDiv.className = "ifCalendarRow";

  for (wDay = 0; wDay < 7; wDay++) {           
     if (getCalendarDayOfTheWeek(startDate.getDay()) != wDay) {              
       weekDiv.appendChild(createCalendarCell(null, calendarContainerId, false, selectedDate, datePattern, targetFieldId));
     } else {              
       if (startDate.getMonth() == startMonth) {
         weekDiv.appendChild(createCalendarCell(startDate, calendarContainerId, true, selectedDate, datePattern, targetFieldId));
       } else {
         weekDiv.appendChild(createCalendarCell(null, calendarContainerId, false, selectedDate, datePattern, targetFieldId));
       }
       startDate.setDate(startDate.getDate() + 1);
     }
  }
  return weekDiv;
}
    


function getCalendarDayOfTheWeek(jsDayOfTheWeek) {
  if (jsDayOfTheWeek == 0) {
    return 6;
  } else {
    return jsDayOfTheWeek - 1;
  }         
}    
    


function createCloseRow(calendarContainerId) {
  var closeDiv = document.createElement("div");
  closeDiv.className = "ifCloseRow";
  
  var closeLink = document.createElement('a');
  closeLink.setAttribute('href', '#');
  closeLink.onclick = new Function('hideCalendar(\'' + calendarContainerId + '\');return false;');
  closeLink.className = "ifClose";
  closeLink.appendChild(document.createTextNode(localizationTable['close']));
  closeDiv.appendChild(closeLink);          
          
  return closeDiv;
}    
    
    
    
function createCalendarCell(cellDate, calendarContainerId, active, selectedDate, datePattern, targetFieldId) {
  // alert("create cell" + selectedDate);
  
  var dayDiv = document.createElement("div");
  dayDiv.className = "ifCalendarCell";

  if (cellDate != null) {
    dayDiv.appendChild(document.createTextNode(cellDate.getDate()));
    if (isHoliday(cellDate)) {
      dayDiv.className = dayDiv.className + " holiday";
    }    

    var today = new Date(); 
    if (today.getYear() == cellDate.getYear() && 
        today.getMonth() == cellDate.getMonth() && 
        today.getDate() == cellDate.getDate()) {
      dayDiv.className = dayDiv.className + " today";
    }      

    if (selectedDate != null &&
        selectedDate.getYear() == cellDate.getYear() && 
        selectedDate.getMonth() == cellDate.getMonth() && 
        selectedDate.getDate() == cellDate.getDate()) {
      dayDiv.className = dayDiv.className + " choosen";
    }               
  } else {
    dayDiv.appendChild(document.createTextNode("\u00a0"));
  }        
    


  if (active) {
    var finalValue = convertDate(cellDate, datePattern);
    dayDiv.onclick = new Function('assignValue("' + calendarContainerId + '", "' + targetFieldId + '", "' + finalValue + '"); return false;');
    dayDiv.onmouseover = new Function('highlightCell(this);');
    dayDiv.onmouseout = new Function('deselectCell(this);');
  }
    
  return dayDiv;        
}



function isHoliday(dateToCheck) {        
  if (dateToCheck != null && dateToCheck.getDay() == 0) {
    return true;
  }
            
  return false;
}    

function hideCalendar(calendarContainerId) {
  var calendarContentDiv = document.getElementById(calendarContainerId + "content");
  if (calendarContentDiv) {
    calendarContentDiv.style.display = "none";
    calendarContentDiv.parentNode.removeChild(calendarContentDiv);
  }                
}


function convertDate(cellDate, datePattern) {
  var result = datePattern;      
  result = result.replace("dd", (cellDate.getDate() < 10 ? "0" + cellDate.getDate() : cellDate.getDate()));
  /**
   * before we display the month we need to convert from zero based to one based
   * value *
   */
  var standardFormatMonth = cellDate.getMonth() + 1;
  result = result.replace("MM", (standardFormatMonth < 10 ? "0" + standardFormatMonth : standardFormatMonth));
  result = result.replace("yyyy", cellDate.getFullYear());
  result = result.replace("yy", (cellDate.getYear() < 10 ? "0" + cellDate.getYear() : cellDate.getYear()));
  return result;       
}
    
    
function assignValue(calendarContainerId ,targetFieldId, valueToAssign) {
  // alert(targetFieldId+":END");
  var resultField = document.getElementById(targetFieldId);
  resultField.value = valueToAssign;
  hideCalendar(calendarContainerId)
  // alert(resultField + ":" + valueToAssign)
}

    
function highlightCell(element) {
  // alert(element);
  element.className = element.className + " selected";
}

function deselectCell(element) {
  // alert(element);
  element.className = element.className.replace(" selected", "");  
}    