// This file, has the logic for show /hide of survey elements.
// Here we have used 'this.' with the variables. It contexts to the variables
// those are declared in this class.

// Layout types
var LAYOUT_SINGLE_PAGE = 0;
var LAYOUT_ITEM_PER_PAGE = 1;
var LAYOUT_SECTION_PER_PAGE = 2;
// Skip question id type.

var SKIP_QUESTION = 1;
var SKIP_SECTION = 2;
var SKIP_ROW = 3;
var SKIP_ELEMENT = 4;
var SKIP_CHOICE = 5;

var skipId;
var skipIdType;

function SkipQuestion( skipId, skipIdType ){
    this.skipId = skipId;
    this.skipIdType = skipIdType;
}

// Filter attributes.

var values;
var operator;
var name;
var valueType;
var id;

// Filter value type.

var STRING = 1;
var NUMERIC = 2;
var DATE_TIME = 3;
var DATE = 4;
var CHOICE = 5;

// Filter Operators.

var LESS_THAN = 1;
var GREATER_THAN = 2;
var EQUAL_TO = 3;
var LESS_THAN_EQUAL_TO = 4;
var GREATER_THAN_EQUAL_TO = 5;
var NOT_EQUAL_TO = 6;
var CONTAINS = 7;
var NOT_CONTAINS = 8;
var ANSWERED = 9;
var NOT_ANSWERED = 10;


var filterList =  new Array();
var filterGroupList =  new Array();
var dropDownChoiceList = new Array();
var totalSkipQList = new Array();
var skipQList;
var layoutType;
// Filter constructor.

function Filter( id, filterValues, filterOperator, filterName, filterValueType ){
    this.id = id;
    this.values = filterValues;
    this.operator = filterOperator;
    this.name = filterName;
    this.valueType = filterValueType;
}



// Create Map.
var key;
var value = false;
var array;
var resultMap =  new Map();
var respMap = new Map();
var skipQMap = new Map();
var ftrGroupMap = new Map();
var dropDownQChoiceMap = new Map();

function KeyValue( key, value )
{
    this.key = key;
    this.value = value;
}

function Map()
{
    this.array = new Array();
}

Map.prototype.put = function( key, value )
{
    if (this.get(key) == "") {
      this.remove(key);
    }
    if( ( typeof key != "undefined" ) && ( typeof value != "undefined" ) )
    {
        this.array[this.array.length] = new KeyValue( key, value );
    }
}

Map.prototype.get = function( key )
{
    for( var k = 0 ; k < this.array.length ; k++ )
    {
        if( this.array[k].key == key ) {
            return this.array[k].value;
        }
    }
    return "";
}

Map.prototype.length = function()
{
    return this.array.length;
}

Map.prototype.remove = function ( key )
{
    for( var k = 0 ; k < this.array.length ; k++ )
    {
        if( this.array[k].key == key ) {
            this.array.splice(k,1);
        }
    }

}

        function getElementsByClass(node,searchClass,tag) {
        var classElements = new Array();
        if ( node == null )
                node = document;
        if ( tag == null )
                tag = '*';
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;
        var pattern = new RegExp('(^|\\\\s)'+searchClass+'(\\\\s|$)');
        for (i = 0, j = 0; i < elsLen; i++) {
                if ( pattern.test(els[i].className) ) {
                        classElements[j] = els[i];
                        j++;
                }
        }
        return classElements;
      }
      function getItemNumberSpanTags(itemDescTableTags) {
        var itemNumberSpans = new Array();
        // var pattern = new RegExp('(^|\\\\s)'+'qSeqNum'+'(\\\\s|$)');
        var pattern = new RegExp('qSeqNum[0-9]+');
        for (i = 0; i < itemDescTableTags.length; i++) {
           var els = itemDescTableTags[i].getElementsByTagName('span');
           for (j = 0; j < els.length; j++) {
             if (pattern.test(els[j].id)) {
               itemNumberSpans[i] = els[j];
             }
           } 
        }
        return itemNumberSpans;
      }

      function getFirstNumber(itemNumberSpans) {

       return itemNumberSpans[0].firstChild.nodeValue;
      }


      function isVisible(obj)
{
    if (obj == document) return true
    
    if (!obj) return false
    if (!obj.parentNode) return false
    if (obj.style) {
        if (obj.style.display == 'none') return false
        if (obj.style.visibility == 'hidden') return false
    }
    
    //Try the computed style in a standard way
    if (window.getComputedStyle) {
        var style = window.getComputedStyle(obj, "")
        if (style.display == 'none') return false
        if (style.visibility == 'hidden') return false
    }
    
    //Or get the computed style using IE's silly proprietary way
    var style = obj.currentStyle
    if (style) {
        if (style['display'] == 'none') return false
        if (style['visibility'] == 'hidden') return false
    }
    
    return isVisible(obj.parentNode)
}

      function renumberItemNumbers(firstNumberStr, itemNumberSpans, itemDescTableTags) {

         // convert firstNumber to string
         var firstNumber = firstNumberStr - 0;
         
         for (i = 0; i < itemDescTableTags.length; i++) {

           if (isVisible(itemDescTableTags[i])) {
              itemNumberSpans[i].firstChild.nodeValue = firstNumber++;
           }
         }
         
      }

      function showHideSeparators() {

        var itemDescTableTags = getElementsByClass(document,'itemDescription','table');
        // Find its parent itemArea divs. Show hide these divs based on if the table is shown
        for (i = 0; i < itemDescTableTags.length; i++) {
          var element = $(itemDescTableTags[i]);
          var parent = element.parents("div[id='itemArea']");
          var separator = parent.children("div[id='separator']");
          if (false == element.is(':visible')) {
            separator.hide();
          }
          else {
            separator.show();
          }
        }
      }
      function renumberQuestions() {

        var itemDescTableTags = getElementsByClass(document,'itemDescription','table');
        var itemNumberSpans = getItemNumberSpanTags(itemDescTableTags);
        if ((itemNumberSpans == null) || (itemNumberSpans.length <= 0)) {
        	return;
        }
        var firstNumber = getFirstNumber(itemNumberSpans);
        renumberItemNumbers(firstNumber, itemNumberSpans, itemDescTableTags);
      }

// This method is called onChange event of Fill Blank Questions. It does,
// 1) Construct ResponseMap.
// 2) Check is Filter condition satisfied with submitted responses.
// 3) Create resultMap, with FilterName as Key and boolean value of
//    isSatisfied.
// 4) Parse the filterGroup and resultMap.

function evaluateFIB( qVal, type ){


    var isFilterQ = isFilterQuestion( qVal );

    // If question is not filter question, then do not
    // execute validation code.

    if ( !isFilterQ ){

        return;
    }

    constFIBRespMap( qVal, type );               // Construct response map.
  
    validateFilter();                            // validate filters.

    parseFilterGroup( qVal );                    // Parse Filter group.
}



// Check if answered question is filter question.

function isFilterQuestion( qVal ){

    for( var i =0; i < this.filterList.length; i++ ) {

        var ftr = filterList[i];

        if( ftr.id == qVal ){

            return true;
        }
    }
    return false;
}



// Add responses in responseMap, from Fill Blank questions.
// 1) In 'Date' FIB, get day, month, year value from dropdown
//    and make a javascript Date obj. Then add it to response map.
// 2) Remaining FIB includes, String, Numeric (Integer, Formula, Number ),
//    and Email type responses.

function constFIBRespMap( qId, type ){

    if ( type == 'DATE' ){

        var questionId = qId;

        // Day value.
        var dateValue = document.getElementById( questionId + '_DT' ).value;
        if ( "" != dateValue){

            var dayValue = dateValue.substring(3,5);
            var monthValue = dateValue.substring(0,2);
            var yearValue = dateValue.substring(6,10);

            // Create Date object.
            var dateObj = Date.UTC( yearValue, monthValue - 1, dayValue );

            if ( "" != this.respMap.get( questionId ) ){
                respMap.remove( questionId );
            }
            respMap.put( questionId , dateObj);
        }
    }
    else {
        var fibQuestion = document.getElementsByName( qId );

        if ( isInvalidList( fibQuestion) ){
            return;
        }

        // We have two types in FIB,
        // 1) Text field question ( Text, Numeric, Email, Integer )
        // 2) Rank Order.
        // Get question elements and check for INPUT, TEXTAREA types only,
        // and get the value from it.
        // getElementsByName return the array values, iterate through it
        // and match the nodeName.Remove old not empty response and add
        // new response, if it is not empty string.
        for( var i = 0; i < fibQuestion.length; i++ ){
            var fibElement = fibQuestion[i];

            if ( fibElement.nodeName == 'INPUT' || fibElement.nodeName == 'TEXTAREA' ){

                var fibVal = fibElement.value;
                if ( "" != this.respMap.get( qId ) ){
                    respMap.remove( qId );
                }
                if ( "" != fibVal ){
                    respMap.put( qId, fibVal );
                }
            }
        }
    }
}


// This method is called onClick of Choice question. It does,
// 1) Construct ResponseMap.
// 2) Check is Filter condition satisfied with submitted responses.
// 3) Create resultMap, with FilterName as Key and boolean value of
//    isSatisfied.
// 4) Parse the filterGroup and resultMap.

function evaluateChoice( qVal, choiceVal, layout, choiceType ){

    var isFilterQ = isFilterQuestion( qVal );

    // If question is not filter question, then do not
    // execute validation code.

    if ( !isFilterQ ){

        return;
    }


    var selected = isSelected( choiceVal, layout  );              // Is choice  selected / Un-selected.

    constructRespMap( qVal, choiceVal, selected, choiceType );    // Construct response map.

    validateFilter();                                             // validate filters.

    parseFilterGroup( qVal );                                           // Parse Filter group.

}




// Use Cases handled in isSelected method,
// 1) Single choice or Multi choice.
// 2) Layout type Inline or Multi-Line.
// 3) Dropdown type question has 'selected' attribute
//     instead of 'checked'

function isSelected( choiceVal, layout ){
    var isSelected;
    if ( 'Inline' == layout ){
        var choiceEle = document.getElementById(choiceVal);
        if( choiceEle.tagName == 'OPTION' ){
            return choiceEle.selected;
        }
        choiceEle = getFirstElementChild(choiceEle);
        if( choiceEle.type == 'radio' || choiceEle.type == 'checkbox' ){
            return  choiceEle.checked;
        }
    }
    var tdNode = getFirstElementChild(document.getElementById(choiceVal));
    isSelected = getFirstElementChild(tdNode).checked;
    return isSelected;
}

function getFirstElementChild(node){
  for(var c = node.childNodes,
   i = 0,
   j = (c && c.length) || 0,
   e;
   i < j;
   i++
  ){
   if(c[i].nodeType === 1)
    e = c[j = i];
  };
  return e;
 }


//  This method constructs the responseMap of Choice question,
//  Use cases are,
//  1) Single Choice Question :- First time response taken.
//  2) Multi Choice Question :- Multiple choices selected.
//  3) If selected choice is unselected.
function constructRespMap( qVal, choiceVal, choiceSelected, choiceType ){

    var isSingleChoice = false;
    var isResponseExist = true;
    var response = '';

    // Check single choice type question.
    if ( 'SingleChoice' == choiceType ){
        isSingleChoice = true;
    }
    else if ( 'MultiList' == choiceType ){
        addMultiListResponse( qVal );
        return;
    }

    // Check if response of question exists.

    response = this.respMap.get( qVal );

    // Check response exists.

    if ('' == response || 'undefined' == response ){
        isResponseExist = false;
    }

    if ( !( isResponseExist) && choiceSelected ){                               // First response of question.
        respMap.put( qVal, choiceVal );
    }
    else if ( isResponseExist && choiceSelected && isSingleChoice  ){           // Single choice condtion.
        // Only put the selected choice into the response map if its 
        // not the Prompt item 
        respMap.remove( qVal );
        if (choiceVal.indexOf('_default') == -1) {
          respMap.put( qVal, choiceVal );
        }

    }
    else if ( isResponseExist && choiceSelected && !isSingleChoice ){           // Multi choice Select condtion.
        respMap.remove( qVal );
        response = response + ',' + choiceVal;
        respMap.put( qVal, response );
    }
    else if ( isResponseExist  && !choiceSelected && !isSingleChoice ){         // Multi choice Un-Select condtion.
        var responseList = response.split(',');
        response = removeUnSelectedResp( responseList, choiceVal );             // Remove un-selected response from responseList.
        respMap.remove( qVal );
        if ( "" != response ){
            respMap.put( qVal, response );
        }

    }

}



// Get all options of list. Created response of selected
// options. Remove old option response from map.
function addMultiListResponse( qVal ){

    var response = '';
    var selectOptions = document.getElementsByName( qVal );

    var listOptions = selectOptions[0].options;
    if ( isInvalidList( listOptions ) ){
        return;
    }

    for ( var i =0; i < listOptions.length; i++ ){

        var optionNode = listOptions[i];

        if ( optionNode.tagName == 'OPTION' && optionNode.selected ){

            if ("" != response ){
                response = response + ',' +  trim(optionNode.id);
            }
            else {
                response += trim(optionNode.id);
            }
        }
    }

    if ( "" != response ){
        this.respMap.remove( qVal );
        this.respMap.put( qVal, response );
    }
    return;

}


// This method remove the un-selected response from
// responseList. This list is then converted into ','
// seperated values and added in responseMap.
function removeUnSelectedResp( responseList, choiceVal ){
    var response = '';
    if ( 0 != responseList.length ) {
        for( var i =0; i < responseList.length ; i++ ){
            if ( choiceVal == responseList[i]){
                responseList.splice(i,1);
            }
        }
    }
    if ( 0 != responseList.length ) {
        for( var j =0; j < responseList.length ; j++ ){
            response = response + responseList[j];
            if ( j != (responseList.length - 1)){
                response += ',';
            }
        }
    }
    return response;
}



// Check Filter condition of every filter from filterList.
// Add the result of validation into ResultMap.
// Avoid duplication of record, replace old record with new
// for same filter.
function validateFilter(){

    for( var i =0; i < this.filterList.length; i++ ) {
        var ftr = filterList[i];

        // Is Filter condition satisfied.

        var satisfiedCondtion = isSatisfied( ftr );

        // If filter validation result already exist, then
        // replace old result with new one.

        var mapValue = this.resultMap.get ( ftr.name );
        if ( true == mapValue || false == mapValue ){
            this.resultMap.remove( ftr.name );
        }

        this.resultMap.put( ftr.name, satisfiedCondtion );
    }
}




// This method check the Filter condition with submitted
// responses.Procedure for isSatisfied method is :-
// 1) Get Filter object attributes.
// 2) Get the response from respMap.
// 3) Make a responseList object and add responses in it.
// 4) FIB question will always have one response per question.
// 5) Evaluate ANSWERED, NOT_ANSWERED logic for FIB.
// 6) Evaluate filter conditions for different filterValueTypes.
function isSatisfied( ftr ){

    var isSatisf = false;
    var responseList;

    // Retrive attibute values from filter obj.

    var qId = trim(ftr.id);
    var filterValues = new Array();
    if ( '' != ftr.values ){
        filterValues  = (ftr.values).split(',');
    }
    var filterOperator = ftr.operator;
    var filterName = ftr.name;
    var filterValueType = ftr.valueType;

    // Get responses for the question.

    var responses = this.respMap.get(qId);

    // Initialise the responseList.
    if ( "" == responses ){
        responseList = new Array();
    }

    var isFIBResponse = filterValueType == this.DATE ||
                        filterValueType == this.DATE_TIME ||
                        filterValueType == this.STRING ||
                        filterValueType == this.NUMERIC;

    if ( isFIBResponse ){
        // For Date, Date_Time, String(Text_Field), Numeric input type questions,
        // we have only one response per question.
        // So responseList will have only one value i.e 0th element.
        responseList = new Array();
        if ( "" != responses ){
            responseList[0] = responses;
        }
    }
    else{
        // Choice type question can have multiple response, so split the
        // response and make a list of it.
        if ( "" != responses ){
            responseList = responses.split(',');
        }
    }


    // ANSWERED and NOT_ANSWERED condition logic.
    if( 0 == responseList.length && filterOperator == this.NOT_ANSWERED ){
        return true;
    }
    else if ( 0 == responseList.length && filterOperator == this.ANSWERED ) {
        return false;
    }
    else if ( 0 != responseList.length && filterOperator == this.ANSWERED ) {
        return true;
    }

    if ( filterOperator == this.NOT_ANSWERED ) {
        filterOperator = this.NOT_EQUAL_TO;
    }
    else if ( filterOperator == this.ANSWERED ) {
        filterOperator = this.EQUAL_TO;
    }

    // Validate responses with filter values.
    for( var i = 0; i < filterValues.length; i++ ){

        var ftrValue = filterValues[i];

        // Evaluate Choice, String, Numeric, Date type question.
        if ( filterValueType == this.CHOICE ){
            // isSatisf = isChoiceFtrValid( ftrValue, responseList, filterOperator, i, filterValues.length );
            isSatisf = isChoiceFtrValid(filterValues, responseList, filterOperator);
        }
        else if ( filterValueType == this.STRING ){
            isSatisf = isTxtFtrValid( ftrValue, responseList, filterOperator, i , filterValues.length);
        }
        else if ( filterValueType == this.NUMERIC || filterValueType == this.DATE || filterValueType == this.DATE_TIME ){
            isSatisf = isNumFtrValid( ftrValue, responseList, filterOperator, i , filterValues.length, filterValueType );
        }

    }

    return isSatisf;
}

function isChoiceFtrValid( filterValues, responseList, operator){

	for( var i = 0; i < filterValues.length; i++ ){

        var ftrValue = filterValues[i];
        ftrValue = 'C' + trim(ftrValue);  // Append alies.
        var respContains = isRespContains( ftrValue, responseList );
        if (respContains == true) {
        	break;
        }

	}
    
    // Check is response exist in filter values.
    
    // Conditional check.
    if ( operator == this.EQUAL_TO && respContains  ) {
        return true;
    }
    else if ( operator == this.NOT_EQUAL_TO && (false == respContains)  ) {
        return true;
    }
    
    return false;
}

// Validate Choice (Single, Multi, Yes/ No, Group, Rating,
// Include Other in choice ) type questions.
// Append 'C' alies with filter value and check if
// filter value choice id exists in response list.
// And then do validation based on filter operator.
function isChoiceFtrValid1( ftrValue, responseList, operator, counter, filterSize   ){

    ftrValue = 'C' + trim(ftrValue);  // Append alies.

    // Check is response exist in filter values.
    var respContains = isRespContains( ftrValue, responseList );

    // Conditional check.
    if ( operator == this.EQUAL_TO && respContains  ) {
        return true;
    }
    else if ( operator == this.NOT_EQUAL_TO && respContains  ) {
        return false;
    }

    if ( counter  == ( filterSize - 1 ) ) {
        if ( operator == this.EQUAL_TO ) {
            return false;
        }
        else {
            return true;
        }
    }
    return false;
}


// Check is response exist.
function isRespContains( ftrValue, responseList ){
    for( var i = 0; i < responseList.length; i++ ){
        if ( ftrValue == responseList[i] ){
            return true;
        }
    }
    return false;
}


// Validate TEXT FIELD, COMMENTS, OTHER type questions.
// Validation is of filter value with response based on
// filter operator.
function isTxtFtrValid( ftrValue, responseList, filterOperator, counter, filterSize ){

    // For one textField question, we will have only one response.
    var txtFldValue = responseList[0];

    if ( null == txtFldValue ) {
        txtFldValue = "";
    }

    // Validate response with filter value based on Operator type.

    if ( filterOperator == this.CONTAINS && txtFldValue.indexOf( ftrValue ) < 0 ) {
        return false;
    }
    else if ( filterOperator == this.EQUAL_TO && !(txtFldValue == ftrValue ) ){
        return false;
    }
    else if ( filterOperator == this.NOT_CONTAINS && txtFldValue.indexOf( ftrValue ) > -1 ) {
        return false;
    }
    else if ( filterOperator == this.NOT_EQUAL_TO && txtFldValue ==  ftrValue )  {
        return false;
    }

    if ( counter  == ( filterSize - 1 ) ) {
        return true;
    }

    return false;
}


// ValIdation for NUMERIC, RANK ORDER, DATE type question.
// 1) Parse filter value and response value as per type.
// 2) Validate values based on filter operator.
// 3) Return boolean result of validation.
function isNumFtrValid( ftrValue, responseList, filterOperator, counter, filterSize, filterValueType ){

    // For one Numeric question, we will have only one response.

    var responseNum = responseList[0];

    if ( null == responseNum ){
        responseNum = "";
    }

    // If type is NUMERIC then parse response and
    // filler value into Number format.
    // else if type is DATE then convert filter value date string
    // into millisecond.
    if ( filterValueType == this.NUMERIC ){

        responseNum = Number(responseNum);
        if (isNaN(responseNum)) {
            return false;
        }
        ftrValue= Number(ftrValue);
        if (isNaN(ftrValue)) {
            return false;
        }
    }
    else if ( filterValueType == this.DATE || filterValueType  == this.DATE_TIME ){

        var dateVals = ftrValue.split('/');                                        // Split mm/dd/yyyy and get list.
        var filterDate = Date.UTC( dateVals[2] , dateVals[0] - 1, dateVals[1]);         // Create Javascript Date. yyyy/MM/dd
        ftrValue = filterDate;                                       // Parse it to Milisecond.
    }

    // Validate response with filter value based on Operator type.

    if ( filterOperator == this.EQUAL_TO && responseNum != ftrValue ) {
        return false;
    }
    else if ( filterOperator == this.GREATER_THAN && responseNum <= ftrValue ) {
        return false;
    }
    else if ( filterOperator == this.GREATER_THAN_EQUAL_TO &&  responseNum < ftrValue ) {
        return false;
    }
    else if ( filterOperator == this.LESS_THAN && responseNum >= ftrValue ) {
        return false;
    }
    else if ( filterOperator == this.LESS_THAN_EQUAL_TO && responseNum > ftrValue ) {
        return false;
    }
    else if ( filterOperator == this.NOT_EQUAL_TO && responseNum == ftrValue ) {
        return false;
    }

    if ( counter == ( filterSize - 1 ) ) {
        return true;
    }

    return false;
}




// One survey can have multiple skip rules.
// One skip rule can have one filter group.
// One filter group can have multiple filters.
// One filter can have multiple skip questions.
// Now, when user will submit response for question,
// get the questionId, find the filters which has
// filterQuestionId =  questionId.
// Evaluate only those filterGroups which has
// above condition true.
// So:- Step 1) Iterate trough all filterGroups.
// 2) Get filters involded in filterGroup.
// 3) Match filterQuestionId with methodParameter qId.
// 4) Then get skip question List and parse the condition.
function parseFilterGroup( qVal ){

    if ( isInvalidList(filterGroupList) ){
        return;
    }
   
    var isSkipQShowMap = new Map();
    for ( var j = 0; j < this.filterGroupList.length; j++ ){
        var group = filterGroupList[j];

        // Get the filter by filter question id.
        if ( isInvalidList(filterList) ){
            return;
        }

        // for( var i = 0; i < this.filterList.length; i++ ){

          //  var ftr = filterList[i];

           //  if( trim(ftr.id) == trim(qVal) ){
             //   var ftrStr = this.ftrGroupMap.get( group );
              //  ftrStr = ftrStr.substring( 1, ftrStr.length - 1 );
               //  var ftrList = ftrStr.split( ',');

                // if ( !isContain( ftrList, ftr )){

                  //  continue;
               //  }
                var skipQList = this.skipQMap.get( group );
                parseCondition( group, this.resultMap, skipQList, isSkipQShowMap );
           // }
       //  }
    }
}


function isContain( ftrList, filter ){
    for( var i = 0; i < ftrList.length; i++ ){
        var ftr = trim(ftrList[i]);
        var ftrName = trim(filter.name);
        if( ftr  == ftrName ){
            return true;
        }
    }

    return false;
}


function addElementVisibleKeepVisible(skipElementId, isValid, isShowQMap) {

  // Check the map to see if the skipElementId is already present. 
  var isSkipElementInMap = isShowQMap.get(skipElementId);
  // If not available, then add skipElementId to map only if it needs to be shown, i.e. isValid = true
  if (isSkipElementInMap != true) {

    if (isValid == true) {

      isShowQMap.put(skipElementId, true);
    }
  }
}

function isElementAlreadySeen(skipElementId, isShowQMap) {

  var isSkipElementInMap = isShowQMap.get(skipElementId);
  if (isSkipElementInMap == true) {

    return true;
  }
  else {
    return false;

  }
}
// Parse the condtionFilter result. We have,
// 1) group = Filter group string.
// 2) resultMap = Map having key = filterName
//    and value = boolean result filter validation.
// 3) skipQLiat = List of questions to be skip.
// 4) For group string filters fetch the results
//    from resultMap and create a stack of it.
// 5) This stack will have boolean values and logical
//    operators, evaluate that and get final isValid result.
// 6) Iterate through skip questions and append the
//    alies in front of question id based on type.
// 7) Get elements to skip from dom either with id or
//    name. Handle two condition differently.
function parseCondition( group, resultMap, skipQList, isSkipQShowMap ){

    // parse into an ArrayList.
    var resultList = parseResult( group, resultMap );

    // Parse the result.
    var isValid = evaluateResult( resultList );
    
    if( isInvalidList( skipQList) ) {
        return;
    }

    var copyMasterList = new Array();
    var skipQuestionName = "";
    for ( var j = 0; j < skipQList.length; j++ ){
        var skipQ = skipQList[j];
	    if ( -1 != skipQ.skipId.indexOf('@') || isOptionSkipQ(skipQ) ){
            // From dropdown choices, we will not condtrol option display via
            // CSS. For this we have following steps.
            // 1) Inside hideOnLoad(), we create a map {QID, {Options}}.This hass
            //    all the records of options for each dropdown on the page.
            // 2) In the same method, Hide the Options, by comparing their ids
            //    with skip Question ids ( From totalSkipQList collection ).
            // 3) Edit skipQ id under skipQList as format to  'QId@ChoiceId'.
            // 4) Here when skipQ id will be in above format, then split this.
            //    We do this in order to have control of Question id and Option id
            // 5) Create collection of Master DropDownChoiceList which will have
            //    All the option records ( without hiding, means all default options ).
            // 6) Create collection of skip question ids ( these are the skip options
            //    which be hide or shown.
            // 7) Then in makeCopyMasterList(), copy the Master DropDownChoiceList to
            //    another collection.Show / Hide will be done on this copied list.
            // 8) Inside buildDropdownList(), based on is valid condition edit the
            //    copied option list to add or remove the options.
            // 9) Inside assignOptionList(), make <select...><option>..</option> HTML
            //    script by strippig the records from edited copied list. Assign the
            //    attribute to tags accordingly (e.g selected = true/false, multiple = true/false )
            //    We have div node ( id = dropDownContainer ), replace the innerHTML
            //    of it with HTML script.
		    var skipQuestionChoice = skipQ.skipId.split('@');
            skipQuestionName = skipQuestionChoice[0];  // Question id from split string arrray.
	        this.dropDownChoiceList = dropDownQChoiceMap.get(skipQuestionName);
		    makeCopyMasterList( this.dropDownChoiceList, copyMasterList );
            buildDropdownList( skipQuestionChoice[1], copyMasterList, isValid );
            assignOptionList( skipQuestionName, copyMasterList);
	     }

        var skipElementId = getIdByType( skipQ );


        if (isElementAlreadySeen(skipElementId, isSkipQShowMap) == false) { 

          // For Single, Multi, Group choices, Rank Order question.
          var skipElement = document.getElementById( skipElementId );

          showHideSkipElement( isValid, skipElement );                            // Id elements.

          // Show / Hide Scales of Group questions.
          if( skipQ.skipIdType == this.SKIP_ELEMENT && null != skipElement ){     // Scale Elements.
		   showHideScaleElements( skipElement, skipElementId, isValid );
          }

          hideLableElements( skipQ, skipElementId, isValid );                     // Label elements by ID.

          // For Rating choice ( it has radio as well as text below choice ),
          // FIB, Scale question, Item lable, Date type.
          hideElementsByName( skipQ, skipElementId, isValid );                    // Name elements.

          // For total and formula field we have special name case.
          // We are handeling it seperately;
          hideTotalFormulaField( skipQ, skipElementId, isValid );

        }
        if (isValid) {

          addElementVisibleKeepVisible(skipElementId, isValid, isSkipQShowMap);
        }
    } // for loop of skipQList.


}


// Check if skip option id matches with option id from total list.

function isOptionSkipQ(skipQ){
      for( var j = 0; j < totalSkipQList.length; j ++ ){
        var skipQValue = totalSkipQList[j];
        if(-1 != skipQValue.skipId.indexOf('@') && -1 != skipQValue.skipId.indexOf(skipQ.skipId)){
		skipQ.skipId = skipQValue.skipId;
            return true;
        }
      }
    return false;
}

// Parse group condition, and convert it into list.
// This is like creating the Stack of boolean operators
// and values.
function parseResult( group, resultMap ){
    var list = new Array();
    var str = "";
    var counter = 0;

    for ( var i = 0; i < group.length; i++ ) {
        var c = group.charAt( i );

        if ( c == ')' || c == '(' || c == ' ' ) {

            if ( trim(str).length != 0 ) {

                if ( trim(str.toUpperCase()) == "AND" ) {
                    list[counter] = "AND";
                    counter++;
                }
                else if ( trim(str.toUpperCase()) == "OR" ) {
                    list[counter] = "OR";
                    counter++;
                }
                else {        // must be a filter's result.

                    list[counter] = resultMap.get(str);
                    counter++;
                }
                str = "";
            }
            continue;
        }


        if ( trim(str.toUpperCase()) == "AND" || trim(str.toUpperCase()) == "OR"  )  {

            // get the next char and make sure this isnt a part of a word!!

            var nextChar = str.charAt( i + 1 );

            if ( (' ' == nextChar ) || nextChar == '('  || nextChar == ')' ) {

                if ( trim(str.toUpperCase()) == "AND" ) {
                    list[counter] = "AND";
                    counter++;
                }
                else if ( trim(str.toUpperCase()) == "OR" ) {
                    list[counter] = "OR";
                    counter++;
                }
                str = "";
                continue;
            }
        }

        // if we came here, add the char to the str

        str += c;
    }

    if ( trim(str).length != 0 ) {
        list[counter] = resultMap.get(str);
        counter++;
    }

    return list;

}


// Evaluate the parse result list and return the
// final validation result.
function evaluateResult( parseList){

    if ( isInvalidList( parseList ) ) {
        return false;
    }

    for ( var i = 0; i < parseList.length; i++ ) {
        if ( parseList.length == 1 ) {
            break;
        }

        var result = parseList[i];

        if ( result == "AND" ) {
            var  r2 = parseList[i + 1];
            if ( !r2 ) {
                parseList.splice( i - 1, 1 );
                parseList.splice( i - 1 , 1);
            }
            else {        // either Both results are true or r1 might be not true
                parseList.splice( i, 1 );
                parseList.splice( i, 1 );
            }

            // start over

            i = 0;
        }
    }

    // Now the parseList should only contain ParserResult objs with an 'OR' operator
    // inbetween them.
    // Time to return the result!!

    for ( var  j = 0; j < parseList.length; j++ ) {
        var resultVal = parseList[j];

        if ( resultVal && ( 'AND' != resultVal && 'OR' != resultVal ) ) {
            return true;
        }

    }

    // return false since we got here!!

    return false;

}


// Append alies before skip id based on
// skip id type.
function getIdByType( skipQ ){

    var idType = skipQ.skipIdType;
    var skipId = skipQ.skipId;

    if ( idType == this.SKIP_QUESTION ){
        skipId = 'Q' + trim(skipId);
    }
    else if ( idType == this.SKIP_SECTION ){
        skipId = 'S' + trim(skipId);
    }
    else if ( idType == this.SKIP_ROW ){
        skipId = 'R' + trim(skipId);
    }
    else if ( idType == this.SKIP_ELEMENT ){
        skipId = 'E' + trim(skipId);
    }
    else if ( idType == this.SKIP_CHOICE ){
        skipId = 'C' + trim(skipId);
    }

    return skipId;
}


// We have id assigned to Scale Label. Below that we have scale
// headers. we have to hide those header.
// For that get the tr, tbody.From tbody always 2nd row will have
// scale headers. Match the id pattern and then execute show/hide.
function showHideScaleElements( skipElement, skipElementId, isValid ){
    if ( skipElement.parentNode.nodeName == "TD" ){
        var tdNode = skipElement.parentNode;
        showHideSkipElement( isValid, tdNode );
    }
    else{
        var trNode = skipElement.parentNode;
        var tbody = trNode.parentNode;
        var scaleHeaderRow = tbody.rows[1];
        var scaleHeaderCells = scaleHeaderRow.cells;
        for( var i = 0; i < scaleHeaderCells.length; i++ ){
            var scale = scaleHeaderCells[i];
            if( -1 !=scale.id.indexOf(skipElementId) ){
                showHideSkipElement( isValid, scale );
            }
        }
    }
}

// This function is to show/ hide the Label of Rating questions.

function hideLableElements( skipQ, skipElementId, isValid ){

    skipElementId = 'L' + trim(skipQ.skipId);
    var skipElement = document.getElementById( skipElementId );
    showHideSkipElement( isValid, skipElement );
}


// This method is evaluated for FIB type questions, SCALE label,
// ITEM lable. These questions has 'name' attribute given.
// Get every skip question from list. Handle special case for
// SkipQ type = 4 (SKIP_ELEMENT).In that case, one question can
// have more than one element. eg. In Mtrix type 'SCALE' has elements
// as 1,2,3,4,5 and so on. In that case we assign 'name = qID' instead
// of 'id = qId'.This name returns all elements to hide under one
// qId. Hide 'SCALE' will hide, 'SCALE' label with 1,2,3,4,5 ..
// column headers as well.
function hideElementsByName( skipQ, skipElementId, isValid ){

    var skipElements = '';

    skipElements = document.getElementsByName( skipElementId );

    if ( skipQ.skipIdType == this.SKIP_ELEMENT &&  isInvalidList( skipElements ) ){

        // The alies used for element type is 'E'. But, group choice questions
        // have already alies given to name attribute is as 'Q',
        // so inorder to get DOM node, to hide complete 'ITEM' ( means whole choices ),
        // we are doing work around. We replace 'E' with 'Q' for the case of
        // Group choices elements.
        // This is special case to SHOW / HIDE ITEM-SCALE row.

        skipElementId = 'Q' + trim(skipQ.skipId);

        // This is for the condition, suppose we have only one choice question
        // in Group other type. And that question id skip type.
        // Then we have to hide the table containing choice question.
        // Please note that.. in this case.. there is Parent Questiont able
        // and iside that we have a table for each multiline question ( e.g choice ).
        // So we are hiding the inner table by holding id attribute.
        var groupTable = document.getElementById( skipElementId );
        if( null != groupTable ){
            showHideSkipElement( isValid, groupTable );
        }

        // This is for handeling IE browser condition.
        // We get all the radio choices. Then, we get the parent element
        // which id TD and then we control show/Hide accordingly.
        skipElements = document.getElementsByName( skipElementId );
        for( var a = 0; a < skipElements.length; a ++ ){
            var elementParentNode = skipElements[a].parentNode;
            if( trim(elementParentNode.nodeName) == "TD" ){
                showHideSkipElement( isValid, elementParentNode );
            }
        }
    }

    if ( isInvalidList( skipElements ) ) {
        return;
    }

    // This is to control show/hide of Inline group questions for Firefox and Safari.
    
    for( var i = 0; i < skipElements.length; i ++ ){
        skipElement = skipElements[i];
        showHideSkipElement( isValid, skipElement );
    }
}

function hideTotalFormulaField( skipQ, skipElementId, isValid ){

    var skipTotalElements = '';
    var skipFormulaElements = '';

    if ( skipQ.skipIdType == this.SKIP_ELEMENT &&  isInvalidList( skipTotalElements ) ){

        // The alies used for element type is 'E'. But, group choice questions
        // have already alies given to name attribute is as 'Q',
        // so inorder to get DOM node, to hide complete 'ITEM' ( means whole choices ),
        // we are doing work around. We replace 'E' with 'Q' for the case of
        // Group choices elements.
        // This is special case to SHOW / HIDE ITEM-SCALE row.

        skipElementId = 'QTOTAL' + trim(skipQ.skipId);

        skipTotalElements = document.getElementsByName( skipElementId );
    }

    if ( !isInvalidList( skipTotalElements ) ) {
      for( var i = 0; i < skipTotalElements.length; i ++ ){
        skipTotalElement = skipTotalElements[i];
        showHideSkipElement( isValid, skipTotalElement );
      }
    }
    if ( skipQ.skipIdType == this.SKIP_ELEMENT &&  isInvalidList( skipFormulaElements ) ){

        // The alies used for element type is 'E'. But, group choice questions
        // have already alies given to name attribute is as 'Q',
        // so inorder to get DOM node, to hide complete 'ITEM' ( means whole choices ),
        // we are doing work around. We replace 'E' with 'Q' for the case of
        // Group choices elements.
        // This is special case to SHOW / HIDE ITEM-SCALE row.

        skipElementId = 'Q' + trim(skipQ.skipId) +'_txt';

        skipFormulaElements = document.getElementsByName( skipElementId );
    }

    if ( isInvalidList( skipFormulaElements ) ) {
        return;
    }

    for( var j = 0; j < skipFormulaElements.length; j ++ ){
        skipFormulaElement = skipFormulaElements[j];
        showHideSkipElement( isValid, skipFormulaElement );
    }
}

function showHideSkipElement( isValid, skipElement ){

    if( null == skipElement ){
        return;
    }

    var errorDiv = getEnclosingDiv($(skipElement));
    if ( !isValid ){
        skipElement.style.display = 'none'; //Hide
        errorDiv.hide();
    }
    else {
        skipElement.style.display = '';  //Show
        errorDiv.show();
    }

    renumberQuestions();
    showHideSeparators();
    
}


// Perform trim on string.
function trim( s ){
    return s.replace(/^\s+|\s+$/g, '');
}


// 1) This method is used for building the DOM while submit button Click.
// 2) After show/hide skip rule evaluation we don't want hidden question
//    responses to go to DB.
// 3) Get hidden elements and remove then from DOM, so empty response will
//    will go to JAVA serverside code.
function buildDom(){

    if( isInvalidList( filterGroupList ) ) {
        return;
    }

    // For every filterGroup string, get the Skip Question List.
    // From that list, remove only those element/s which have
    // display style is 'none'.

    for ( var j = 0; j < filterGroupList.length; j++ ){

        var filterGroup = filterGroupList[j];

        this.skipQList = this.skipQMap.get( filterGroup );

        if( '' == this.skipQList && 0 == this.skipQList.length ) {
                return;
        }

        removeDomElement( skipQList );
    }
}


// 1) Iterate from skip question list.
// 2) Get elements by either id or name based on type.
// 3) If element has style = 'none', then remove
//    element from DOM ( i.e. remove from it's parent node.)
function removeDomElement( skipQList ){

    for ( var i = 0; i < skipQList.length; i++ ){

        var skipQ = skipQList[i];
        if( -1 != skipQ.skipId.indexOf('@') ){
            var skipArray = skipQ.skipId.split('@');
            skipQ.skipId = skipArray[1];
        }
        var skipElementId = getIdByType( skipQ );
        var skipElement = document.getElementById( skipElementId );

        // If skip element is not found by id then get it by name.
        // If also not found by name then return.

        if ( null == skipElement ){
            var skipElements = document.getElementsByName( skipElementId );
            if ( isInvalidList( skipElements) ){
                skipElementId = 'Q' + trim(skipQ.skipId);
                skipElements = document.getElementsByName( skipElementId );
                if ( isInvalidList( skipElements )  ){
                    continue;
                }
            }
            for ( var j = 0; j < skipElements.length; j++ ) {
                var skipEle = skipElements[j];
                if ( skipEle.style.display == 'none' ){
                    skipEle.parentNode.removeChild( skipEle );
                }
            }
        }
        else{
            if ( skipElement.style.display == 'none' ){
                skipElement.parentNode.removeChild( skipElement );
            }            
        }
    }
}



// If any of the condition gets true then it means list is invalid.
function isInvalidList( valueList ){

    var isInvalid = false;

    isInvalid = ( '' == valueList || null == valueList || 0 == valueList.length );

    return isInvalid;
}


// For filter condition = ANSWERED, we will hide skip questions
// onLoad of response page. This is because, onLoad we have no
// responses in respMap so, filter condition failes.
function hideOnLoad() {
       prepareDropDownData();
	  validateFilter();
	  parseFilterGroup('Dummy');
}
function prepareDropDownData() {
    var dropDownQuestion = "";

    for( var j = 0; j < totalSkipQList.length; j ++ ){
        var skipQValue = totalSkipQList[j];
        if ( skipQValue.skipIdType == this.SKIP_CHOICE ){

            var choiceElement = document.getElementById( 'C'+ skipQValue.skipId );
            if( null != choiceElement && choiceElement.nodeName == 'OPTION'){
                if( isInvalidList( this.dropDownChoiceList ) ){
                    buildDropDownChoiceMap(skipQValue);
                }

                dropDownQuestion = choiceElement.parentNode;
                if (choiceElement.style.display === "none") {
                    dropDownQuestion.remove(choiceElement.index);
                }
                skipQValue.skipId  = dropDownQuestion.name + '@' + skipQValue.skipId ;
                this.totalSkipQList[j] = skipQValue;
            }
        }

    }

}
function hideOnLoad1(){
    if( isInvalidList(filterList) ){
        return;
    }

    for( var i=0; i < filterList.length; i++ ){
        var ftrVal = filterList[i];
        if ( ftrVal.operator != this.ANSWERED ){
            continue;
        }
        this.resultMap.put( ftrVal.name, false );
        parseFilterGroup( ftrVal.id );
    }

    var dropDownQuestion = "";

    for( var j = 0; j < totalSkipQList.length; j ++ ){
        var skipQValue = totalSkipQList[j];
        if ((skipQValue.skipIdType == this.SKIP_SECTION ) &&
            (layoutType != this.LAYOUT_SECTION_PER_PAGE)) {
            var sectionElement = document.getElementById( 'S'+ skipQValue.skipId );
            if( (null != sectionElement) && ('' != sectionElement) ){
                sectionElement.style.display = 'none';
            }
        }
        else if ( skipQValue.skipIdType == this.SKIP_CHOICE ){

            var choiceElement = document.getElementById( 'C'+ skipQValue.skipId );
            if( null != choiceElement && choiceElement.nodeName == 'OPTION'){
                if( isInvalidList( this.dropDownChoiceList ) ){
                    buildDropDownChoiceMap(skipQValue);
                }

                dropDownQuestion = choiceElement.parentNode;
                if (choiceElement.style.display === "none") {
                    dropDownQuestion.remove(choiceElement.index);
                }
                skipQValue.skipId  = dropDownQuestion.name + '@' + skipQValue.skipId ;
                this.totalSkipQList[j] = skipQValue;
            }
        }

    }
    
 }




function buildDropDownChoiceMap( skipQValue){
    var optionId = 'C'+ skipQValue.skipId;
    var choiceElement = document.getElementById( optionId );
    var selectElement = choiceElement.parentNode;
    for ( var i = 0; i < selectElement.length ; i++){
        var optionElement = selectElement.options[i];
        this.dropDownChoiceList[i] = optionElement;
    }
    dropDownQChoiceMap.put( selectElement.name, this.dropDownChoiceList );
}


function makeCopyMasterList( dropDownChoiceList, copyMasterList ){
   if( dropDownChoiceList.length == 0 ){
	return;
   }
   for( var i = 0; i < dropDownChoiceList.length; i++ ){
	copyMasterList [i] = dropDownChoiceList[i];
   }
}


function buildDropdownList( skipQuestionChoiceId, copyMasterList, isValid ){
    if( copyMasterList.length == 0 ){
        return;
    }

    for( var i = 0; i < copyMasterList.length; i++ ){
        var option = copyMasterList[i];
        if( option.id.substring(1) == skipQuestionChoiceId ){
            showHideSkipElement( isValid, option );
        }
    }
}

function getSelectedValues(selectElement) {
	var selectedValues = new Array();
	for (var i = 0; i < selectElement.options.length; i++) {
	
		var selectedIndex = selectElement.options[i].selected;
		if (selectedIndex == true) {
			selectedValues.push(selectElement.options[i].value);
		}
	}
	
	return selectedValues;
}
function isSelectedValue(selectedValues, valueToCheck) {
	
	for (var i = 0; i < selectedValues.length; i++) {
	
		if (valueToCheck == selectedValues[i]) {
			return true;
		}
	}
	return false;
}
function assignOptionList( skipQuestionName, copyMasterList){
    var selectNames = document.getElementsByName(skipQuestionName);
    var selectElement = selectNames[0];
    // var selectedIndex = selectElement.options.selectedIndex;
    
    var selectedValues = getSelectedValues(selectElement);
    // if (selectedIndex != -1) {
    //	selectedValue = selectElement.options[selectedIndex].value;
    // }
    var choiceType = 'SingleChoice';

    var dropDownQuestionHTML = "<select name ='" + selectElement.name+ "' size = '" + selectElement.size + "' ";
    if ( selectElement.multiple == true ){
        dropDownQuestionHTML = dropDownQuestionHTML + " multiple = '"+ true + "'";
          choiceType = 'MultiChoice';
    }
    dropDownQuestionHTML = dropDownQuestionHTML +
        "onChange=\"showFormulas(this.form);evaluateChoice('" + skipQuestionName + "', this.options[this.selectedIndex].id , 'Inline', '" + choiceType +"' );\">";


     for( var i = 0; i < copyMasterList.length; i++ ){
        var option = copyMasterList[i];
        
        if ( option.style.display == "none" ){
        	continue;
        }
        dropDownQuestionHTML = dropDownQuestionHTML + "<option id = '" + option.id + "' value = '" + option.value + "'";
        if (true == isSelectedValue(selectedValues, option.value)) {
        	dropDownQuestionHTML = dropDownQuestionHTML + " selected = 'true'"; 
        	
        }
        dropDownQuestionHTML = dropDownQuestionHTML + ">" + option.text + "</option>";

     }
     dropDownQuestionHTML = dropDownQuestionHTML + "</select>";
     var divNode = document.getElementById('dropDownContainer');
     divNode.innerHTML  = dropDownQuestionHTML;
}
