/**
  * Create the arrays needed by ExpressionManager to process LimeSurvey strings.
  * The long part of this function should only be called once per page display (e.g. only if $fieldMap changes)
  *
  * @param <integer> $surveyid
  * @param <Boolean> $forceRefresh
  * @param <Boolean> $anonymized
  * @param <Boolean> $allOnOnePage - if true (like for survey_format), uses certain optimizations
  * @return boolean - true if $fieldmap had been re-created, so ExpressionManager variables need to be re-set
  */
 private function setVariableAndTokenMappingsForExpressionManager($surveyid, $forceRefresh = false, $anonymized = false, $allOnOnePage = false)
 {
     if (isset($_SESSION['LEMforceRefresh'])) {
         unset($_SESSION['LEMforceRefresh']);
         $forceRefresh = true;
     } else {
         if (!$forceRefresh && isset($this->knownVars) && !$this->sPreviewMode) {
             return false;
             // means that those variables have been cached and no changes needed
         }
     }
     $now = microtime(true);
     $this->em->SetSurveyMode($this->surveyMode);
     // TODO - do I need to force refresh, or trust that createFieldMap will cache langauges properly?
     $fieldmap = createFieldMap($surveyid, $style = 'full', $forceRefresh, false, $_SESSION['LEMlang']);
     $this->sid = $surveyid;
     $this->runtimeTimings[] = array(__METHOD__ . '.createFieldMap', microtime(true) - $now);
     //      LimeExpressionManager::ShowStackTrace();
     $now = microtime(true);
     if (!isset($fieldmap)) {
         return false;
         // implies an error occurred
     }
     $this->knownVars = array();
     // mapping of VarName to Value
     $this->qcode2sgqa = array();
     $this->tempVars = array();
     $this->qid2code = array();
     // List of codes for each question - needed to know which to NULL if a question is irrelevant
     $this->jsVar2qid = array();
     $this->qcode2sgq = array();
     $this->alias2varName = array();
     $this->varNameAttr = array();
     $this->questionId2questionSeq = array();
     $this->questionId2groupSeq = array();
     $this->questionSeq2relevance = array();
     $this->groupId2groupSeq = array();
     $this->qid2validationEqn = array();
     $this->groupSeqInfo = array();
     $this->gseq2relevanceStatus = array();
     // Since building array of allowable answers, need to know preset values for certain question types
     $presets = array();
     $presets['G'] = array('M' => $this->gT("Male"), 'F' => $this->gT("Female"));
     $presets['Y'] = array('Y' => $this->gT("Yes"), 'N' => $this->gT("No"));
     $presets['C'] = array('Y' => $this->gT("Yes"), 'N' => $this->gT("No"), 'U' => $this->gT("Uncertain"));
     $presets['E'] = array('I' => $this->gT("Increase"), 'S' => $this->gT("Same"), 'D' => $this->gT("Decrease"));
     $this->gseq2info = $this->getGroupInfoForEM($surveyid, $_SESSION['LEMlang']);
     foreach ($this->gseq2info as $aGroupInfo) {
         $this->groupId2groupSeq[$aGroupInfo['gid']] = $aGroupInfo['group_order'];
     }
     $qattr = $this->getQuestionAttributesForEM($surveyid, 0, $_SESSION['LEMlang']);
     $this->qattr = $qattr;
     $this->runtimeTimings[] = array(__METHOD__ . ' - question_attributes_model->getQuestionAttributesForEM', microtime(true) - $now);
     $now = microtime(true);
     $this->qans = $this->getAnswerSetsForEM($surveyid, NULL, $_SESSION['LEMlang']);
     $this->runtimeTimings[] = array(__METHOD__ . ' - answers_model->getAnswerSetsForEM', microtime(true) - $now);
     $now = microtime(true);
     $q2subqInfo = array();
     $this->multiflexiAnswers = array();
     foreach ($fieldmap as $fielddata) {
         if (!isset($fielddata['fieldname']) || !preg_match('#^\\d+X\\d+X\\d+#', $fielddata['fieldname'])) {
             continue;
             // not an SGQA value
         }
         $sgqa = $fielddata['fieldname'];
         $type = $fielddata['type'];
         $mandatory = $fielddata['mandatory'];
         $fieldNameParts = explode('X', $sgqa);
         $groupNum = $fieldNameParts[1];
         $aid = isset($fielddata['aid']) ? $fielddata['aid'] : '';
         $sqid = isset($fielddata['sqid']) ? $fielddata['sqid'] : '';
         if ($this->sPreviewMode == 'question') {
             $fielddata['relevance'] = 1;
         }
         if ($this->sPreviewMode == 'group') {
             $fielddata['grelevance'] = 1;
         }
         $questionId = $fieldNameParts[2];
         $questionNum = $fielddata['qid'];
         $relevance = isset($fielddata['relevance']) ? $fielddata['relevance'] : 1;
         $SQrelevance = isset($fielddata['SQrelevance']) ? $fielddata['SQrelevance'] : 1;
         $grelevance = isset($fielddata['grelevance']) ? $fielddata['grelevance'] : 1;
         $hidden = isset($qattr[$questionNum]['hidden']) ? $qattr[$questionNum]['hidden'] == '1' : false;
         $scale_id = isset($fielddata['scale_id']) ? $fielddata['scale_id'] : '0';
         $preg = isset($fielddata['preg']) ? $fielddata['preg'] : NULL;
         // a perl regular exrpession validation function
         $defaultValue = isset($fielddata['defaultvalue']) ? $fielddata['defaultvalue'] : NULL;
         if (trim($preg) == '') {
             $preg = NULL;
         }
         $help = isset($fielddata['help']) ? $fielddata['help'] : '';
         $other = isset($fielddata['other']) ? $fielddata['other'] : '';
         if (isset($this->questionId2groupSeq[$questionNum])) {
             $groupSeq = $this->questionId2groupSeq[$questionNum];
         } else {
             $groupSeq = isset($fielddata['groupSeq']) ? $fielddata['groupSeq'] : -1;
             $this->questionId2groupSeq[$questionNum] = $groupSeq;
         }
         if (isset($this->questionId2questionSeq[$questionNum])) {
             $questionSeq = $this->questionId2questionSeq[$questionNum];
         } else {
             $questionSeq = isset($fielddata['questionSeq']) ? $fielddata['questionSeq'] : -1;
             $this->questionId2questionSeq[$questionNum] = $questionSeq;
         }
         if (!isset($this->groupSeqInfo[$groupSeq])) {
             $this->groupSeqInfo[$groupSeq] = array('qstart' => $questionSeq, 'qend' => $questionSeq);
         } else {
             $this->groupSeqInfo[$groupSeq]['qend'] = $questionSeq;
             // with each question, update so know ending value
         }
         // Create list of codes associated with each question
         $codeList = isset($this->qid2code[$questionNum]) ? $this->qid2code[$questionNum] : '';
         if ($codeList == '') {
             $codeList = $sgqa;
         } else {
             $codeList .= '|' . $sgqa;
         }
         $this->qid2code[$questionNum] = $codeList;
         $readWrite = 'Y';
         // Set $ansArray
         switch ($type) {
             case '!':
                 //List - dropdown
             //List - dropdown
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
             //LIST WITH COMMENT drop-down/radio-button list + textarea
             case '1':
                 //Array (Flexible Labels) dual scale  // need scale
             //Array (Flexible Labels) dual scale  // need scale
             case 'H':
                 //ARRAY (Flexible) - Column Format
             //ARRAY (Flexible) - Column Format
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'R':
                 //RANKING STYLE
                 $ansArray = isset($this->qans[$questionNum]) ? $this->qans[$questionNum] : NULL;
                 if ($other == 'Y' && ($type == 'L' || $type == '!')) {
                     if (preg_match('/other$/', $sgqa)) {
                         $ansArray = NULL;
                         // since the other variable doesn't need it
                     } else {
                         $_qattr = isset($qattr[$questionNum]) ? $qattr[$questionNum] : array();
                         if (isset($_qattr['other_replace_text']) && trim($_qattr['other_replace_text']) != '') {
                             $othertext = trim($_qattr['other_replace_text']);
                         } else {
                             $othertext = $this->gT('Other:');
                         }
                         $ansArray['0~-oth-'] = '0|' . $othertext;
                     }
                 }
                 break;
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case '5':
                 //5 POINT CHOICE radio-buttons
                 $ansArray = NULL;
                 break;
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case ';':
                 //ARRAY (Multi Flexi) Text
             //ARRAY (Multi Flexi) Text
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
             //Multiple choice with comments checkbox + text
             case 'D':
                 //DATE
             //DATE
             case '*':
                 //Equation
             //Equation
             case 'I':
                 //Language Question
             //Language Question
             case '|':
                 //File Upload
             //File Upload
             case 'X':
                 //BOILERPLATE QUESTION
                 $ansArray = NULL;
                 break;
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
                 $ansArray = $presets[$type];
                 break;
         }
         // set $subqtext text - for display of primary sub-question
         $subqtext = '';
         switch ($type) {
             default:
                 $subqtext = isset($fielddata['subquestion']) ? $fielddata['subquestion'] : '';
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $subqtext = isset($fielddata['subquestion1']) ? $fielddata['subquestion1'] : '';
                 $ansList = array();
                 if (isset($fielddata['answerList'])) {
                     foreach ($fielddata['answerList'] as $ans) {
                         $ansList['1~' . $ans['code']] = $ans['code'] . '|' . $ans['answer'];
                     }
                     $this->multiflexiAnswers[$questionNum] = $ansList;
                 }
                 break;
         }
         // Set $varName (question code / questions.title), $rowdivid, $csuffix, $sqsuffix, and $question
         $rowdivid = NULL;
         // so that blank for types not needing it.
         $sqsuffix = '';
         switch ($type) {
             case '!':
                 //List - dropdown
             //List - dropdown
             case '5':
                 //5 POINT CHOICE radio-buttons
             //5 POINT CHOICE radio-buttons
             case 'D':
                 //DATE
             //DATE
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'I':
                 //Language Question
             //Language Question
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
             //LIST WITH COMMENT drop-down/radio-button list + textarea
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'X':
                 //BOILERPLATE QUESTION
             //BOILERPLATE QUESTION
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case '|':
                 //File Upload
             //File Upload
             case '*':
                 //Equation
                 $csuffix = '';
                 $sqsuffix = '';
                 $varName = $fielddata['title'];
                 if ($fielddata['aid'] != '') {
                     $varName .= '_' . $fielddata['aid'];
                 }
                 $question = $fielddata['question'];
                 break;
             case '1':
                 //Array (Flexible Labels) dual scale
                 $csuffix = $fielddata['aid'] . '#' . $fielddata['scale_id'];
                 $sqsuffix = '_' . $fielddata['aid'];
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'] . '_' . $fielddata['scale_id'];
                 $question = $fielddata['subquestion'] . '[' . $fielddata['scale'] . ']';
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion'] . '[' . $fielddata['scale'] . ']';
                 $rowdivid = substr($sgqa, 0, -2);
                 break;
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
             //ARRAY (Increase/Same/Decrease) radio-buttons
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'H':
                 //ARRAY (Flexible) - Column Format    // note does not have javatbd equivalent - so array filters don't work on it
             //ARRAY (Flexible) - Column Format    // note does not have javatbd equivalent - so array filters don't work on it
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION         // note does not have javatbd equivalent - so array filters don't work on it, but need rowdivid to process validations
             //MULTIPLE NUMERICAL QUESTION         // note does not have javatbd equivalent - so array filters don't work on it, but need rowdivid to process validations
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
             //Multiple choice with comments checkbox + text
             case 'Q':
                 //MULTIPLE SHORT TEXT                 // note does not have javatbd equivalent - so array filters don't work on it
             //MULTIPLE SHORT TEXT                 // note does not have javatbd equivalent - so array filters don't work on it
             case 'R':
                 //RANKING STYLE                       // note does not have javatbd equivalent - so array filters don't work on it
                 $csuffix = $fielddata['aid'];
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'];
                 $question = $fielddata['subquestion'];
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion'];
                 if ($type != 'H') {
                     if ($type == 'P' && preg_match("/comment\$/", $sgqa)) {
                         //                            $rowdivid = substr($sgqa,0,-7);
                     } else {
                         $sqsuffix = '_' . $fielddata['aid'];
                         $rowdivid = $sgqa;
                     }
                 }
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $csuffix = $fielddata['aid'];
                 $sqsuffix = '_' . substr($fielddata['aid'], 0, strpos($fielddata['aid'], '_'));
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'];
                 $question = $fielddata['subquestion1'] . '[' . $fielddata['subquestion2'] . ']';
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion1'] . '[' . $fielddata['subquestion2'] . ']';
                 $rowdivid = substr($sgqa, 0, strpos($sgqa, '_'));
                 break;
         }
         // $onlynum
         $onlynum = false;
         // the default
         switch ($type) {
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
                 $onlynum = true;
                 break;
             case '*':
                 // Equation
             // Equation
             case ';':
                 //ARRAY (Multi Flexi) Text
             //ARRAY (Multi Flexi) Text
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case 'S':
                 //SHORT FREE TEXT
                 if (isset($qattr[$questionNum]['numbers_only']) && $qattr[$questionNum]['numbers_only'] == '1') {
                     $onlynum = true;
                 }
                 break;
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
                 if (isset($qattr[$questionNum]['other_numbers_only']) && $qattr[$questionNum]['other_numbers_only'] == '1' && preg_match('/other$/', $sgqa)) {
                     $onlynum = true;
                 }
                 break;
             default:
                 break;
         }
         // Set $jsVarName_on (for on-page variables - e.g. answerSGQA) and $jsVarName (for off-page  variables; the primary name - e.g. javaSGQA)
         switch ($type) {
             case 'R':
                 //RANKING STYLE
                 $jsVarName_on = 'answer' . $sgqa;
                 $jsVarName = 'java' . $sgqa;
                 break;
             case 'D':
                 //DATE
             //DATE
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'X':
                 //BOILERPLATE QUESTION
                 $jsVarName_on = 'answer' . $sgqa;
                 $jsVarName = 'java' . $sgqa;
                 break;
             case '!':
                 //List - dropdown
                 if (preg_match("/other\$/", $sgqa)) {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = 'othertext' . substr($sgqa, 0, -5);
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
             case 'L':
                 //LIST drop-down/radio-button list
                 if (preg_match("/other\$/", $sgqa)) {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = 'answer' . $sgqa . "text";
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
             case '5':
                 //5 POINT CHOICE radio-buttons
             //5 POINT CHOICE radio-buttons
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'I':
                 //Language Question
             //Language Question
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case '*':
                 //Equation
             //Equation
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
             //ARRAY (Increase/Same/Decrease) radio-buttons
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'H':
                 //ARRAY (Flexible) - Column Format
             //ARRAY (Flexible) - Column Format
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
                 if ($type == 'O' && preg_match('/_comment$/', $varName)) {
                     $jsVarName_on = 'answer' . $sgqa;
                 } else {
                     $jsVarName_on = 'java' . $sgqa;
                 }
                 $jsVarName = 'java' . $sgqa;
                 break;
             case '1':
                 //Array (Flexible Labels) dual scale
                 $jsVarName = 'java' . str_replace('#', '_', $sgqa);
                 $jsVarName_on = $jsVarName;
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $jsVarName = 'java' . $sgqa;
                 $jsVarName_on = 'answer' . $sgqa;
                 break;
             case '|':
                 //File Upload
                 $jsVarName = $sgqa;
                 $jsVarName_on = $jsVarName;
                 break;
             case 'P':
                 //Multiple choice with comments checkbox + text
                 if (preg_match("/(other|comment)\$/", $sgqa)) {
                     $jsVarName_on = 'answer' . $sgqa;
                     // is this true for survey.php and not for group.php?
                     $jsVarName = 'java' . $sgqa;
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
         }
         // Hidden question are never on same page (except for equation)
         if ($hidden && $type != "*") {
             $jsVarName_on = '';
         }
         if (!is_null($rowdivid) || $type == 'L' || $type == 'N' || $type == '!' || $type == 'O' || !is_null($preg) || $type == 'S' || $type == 'D' || $type == 'T' || $type == 'U' || $type == '|') {
             if (!isset($q2subqInfo[$questionNum])) {
                 $q2subqInfo[$questionNum] = array('qid' => $questionNum, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'sgqa' => $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'mandatory' => $mandatory, 'varName' => $varName, 'type' => $type, 'fieldname' => $sgqa, 'preg' => $preg, 'rootVarName' => $fielddata['title']);
             }
             if (!isset($q2subqInfo[$questionNum]['subqs'])) {
                 $q2subqInfo[$questionNum]['subqs'] = array();
             }
             switch ($type) {
                 case 'L':
                     // What using sq: it's only on question + one other if other is set. This don't set the other subq here.
                 // What using sq: it's only on question + one other if other is set. This don't set the other subq here.
                 case '!':
                     if (!is_null($ansArray)) {
                         foreach (array_keys($ansArray) as $key) {
                             $parts = explode('~', $key);
                             if ($parts[1] == '-oth-') {
                                 $parts[1] = 'other';
                             }
                             $q2subqInfo[$questionNum]['subqs'][] = array('rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum . $parts[1], 'varName' => $varName, 'sqsuffix' => '_' . $parts[1]);
                         }
                     }
                     break;
                 case 'O':
                     if (strlen($varName) > 8 && substr_compare($varName, '_comment', -8) === 0) {
                         $q2subqInfo[$questionNum]['subqs'][] = array('varName' => $varName, 'rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum . 'comment', 'jsVarName' => $jsVarName, 'jsVarName_on' => $jsVarName_on, 'sqsuffix' => '_comment');
                     } else {
                         $q2subqInfo[$questionNum]['subqs'][] = array('varName' => $varName, 'rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'jsVarName' => $jsVarName, 'jsVarName_on' => $jsVarName_on);
                     }
                     break;
                 case 'N':
                 case 'S':
                 case 'D':
                 case 'T':
                 case 'U':
                     $q2subqInfo[$questionNum]['subqs'][] = array('varName' => $varName, 'rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'jsVarName' => 'java' . $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'jsVarName_on' => $jsVarName_on);
                     break;
                 default:
                     $q2subqInfo[$questionNum]['subqs'][] = array('rowdivid' => $rowdivid, 'varName' => $varName, 'jsVarName_on' => $jsVarName_on, 'jsVarName' => $jsVarName, 'csuffix' => $csuffix, 'sqsuffix' => $sqsuffix);
                     break;
             }
         }
         $ansList = '';
         if (isset($ansArray) && !is_null($ansArray)) {
             $answers = array();
             foreach ($ansArray as $key => $value) {
                 $answers[] = "'" . $key . "':'" . htmlspecialchars(preg_replace('/[[:space:]]/', ' ', $value), ENT_QUOTES) . "'";
             }
             $ansList = ",'answers':{ " . implode(",", $answers) . "}";
         }
         // Set mappings of variable names to needed attributes
         $varInfo_Code = array('jsName_on' => $jsVarName_on, 'jsName' => $jsVarName, 'readWrite' => $readWrite, 'hidden' => $hidden, 'question' => $question, 'qid' => $questionNum, 'gid' => $groupNum, 'grelevance' => $grelevance, 'relevance' => $relevance, 'SQrelevance' => $SQrelevance, 'qcode' => $varName, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'type' => $type, 'sgqa' => $sgqa, 'ansList' => $ansList, 'ansArray' => $ansArray, 'scale_id' => $scale_id, 'default' => $defaultValue, 'rootVarName' => $fielddata['title'], 'subqtext' => $subqtext, 'rowdivid' => is_null($rowdivid) ? '' : $rowdivid, 'onlynum' => $onlynum);
         $this->questionSeq2relevance[$questionSeq] = array('relevance' => $relevance, 'grelevance' => $grelevance, 'qid' => $questionNum, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'jsResultVar_on' => $jsVarName_on, 'jsResultVar' => $jsVarName, 'type' => $type, 'hidden' => $hidden, 'gid' => $groupNum, 'mandatory' => $mandatory, 'eqn' => '', 'help' => $help, 'qtext' => $fielddata['question'], 'code' => $varName, 'other' => $other, 'default' => $defaultValue, 'rootVarName' => $fielddata['title'], 'rowdivid' => is_null($rowdivid) ? '' : $rowdivid, 'aid' => $aid, 'sqid' => $sqid);
         $this->knownVars[$sgqa] = $varInfo_Code;
         $this->qcode2sgqa[$varName] = $sgqa;
         $this->jsVar2qid[$jsVarName] = $questionNum;
         $this->qcode2sgq[$fielddata['title']] = $surveyid . 'X' . $groupNum . 'X' . $questionNum;
         // Create JavaScript arrays
         $this->alias2varName[$varName] = array('jsName' => $jsVarName, 'jsPart' => "'" . $varName . "':'" . $jsVarName . "'");
         $this->alias2varName[$sgqa] = array('jsName' => $jsVarName, 'jsPart' => "'" . $sgqa . "':'" . $jsVarName . "'");
         $this->varNameAttr[$jsVarName] = "'" . $jsVarName . "':{ " . "'jsName':'" . $jsVarName . "','jsName_on':'" . $jsVarName_on . "','sgqa':'" . $sgqa . "','qid':" . $questionNum . ",'gid':" . $groupNum . ",'type':'" . $type . "','default':'" . (is_null($defaultValue) ? '' : str_replace("'", "\\'", $defaultValue)) . "','rowdivid':'" . (is_null($rowdivid) ? '' : $rowdivid) . "','onlynum':'" . ($onlynum ? '1' : '') . "','gseq':" . $groupSeq . $ansList;
         if ($type == 'M' || $type == 'P') {
             $this->varNameAttr[$jsVarName] .= ",'question':'" . htmlspecialchars(preg_replace('/[[:space:]]/', ' ', $question), ENT_QUOTES) . "'";
         }
         $this->varNameAttr[$jsVarName] .= "}";
     }
     $this->q2subqInfo = $q2subqInfo;
     // Now set tokens
     if (Survey::model()->hasTokens($surveyid) && isset($_SESSION[$this->sessid]['token']) && $_SESSION[$this->sessid]['token'] != '') {
         //Gather survey data for tokenised surveys, for use in presenting questions
         $this->knownVars['TOKEN:TOKEN'] = array('code' => $_SESSION[$this->sessid]['token'], 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         $token = Token::model($surveyid)->findByToken($_SESSION[$this->sessid]['token']);
         foreach ($token as $key => $val) {
             $this->knownVars["TOKEN:" . strtoupper($key)] = array('code' => $anonymized ? '' : $val, 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         }
     } else {
         // Read list of available tokens from the tokens table so that preview and error checking works correctly
         $attrs = array_keys(getTokenFieldsAndNames($surveyid));
         $blankVal = array('code' => '', 'type' => '', 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         foreach ($attrs as $key) {
             if (preg_match('/^(firstname|lastname|email|usesleft|token|attribute_\\d+)$/', $key)) {
                 $this->knownVars['TOKEN:' . strtoupper($key)] = $blankVal;
             }
         }
     }
     // set default value for reserved 'this' variable
     $this->knownVars['this'] = array('jsName_on' => '', 'jsName' => '', 'readWrite' => '', 'hidden' => '', 'question' => 'this', 'qid' => '', 'gid' => '', 'grelevance' => '', 'relevance' => '', 'SQrelevance' => '', 'qcode' => 'this', 'qseq' => '', 'gseq' => '', 'type' => '', 'sgqa' => '', 'rowdivid' => '', 'ansList' => '', 'ansArray' => array(), 'scale_id' => '', 'default' => '', 'rootVarName' => 'this', 'subqtext' => '', 'rowdivid' => '');
     $this->runtimeTimings[] = array(__METHOD__ . ' - process fieldMap', microtime(true) - $now);
     usort($this->questionSeq2relevance, 'cmpQuestionSeq');
     $this->numQuestions = count($this->questionSeq2relevance);
     $this->numGroups = count($this->groupSeqInfo);
     return true;
 }
Exemplo n.º 2
0
 /**
  * Create the arrays needed by ExpressionManager to process LimeSurvey strings.
  * The long part of this function should only be called once per page display (e.g. only if $fieldMap changes)
  *
  * @param <integer> $surveyid
  * @param <Boolean> $forceRefresh
  * @param <Boolean> $anonymized
  * @param <Boolean> $allOnOnePage - if true (like for survey_format), uses certain optimizations
  * @return boolean - true if $fieldmap had been re-created, so ExpressionManager variables need to be re-set
  */
 private function setVariableAndTokenMappingsForExpressionManager($surveyid, $forceRefresh = false, $anonymized = false, $allOnOnePage = false)
 {
     if (isset($_SESSION['LEMforceRefresh'])) {
         unset($_SESSION['LEMforceRefresh']);
         $forceRefresh = true;
     } else {
         if (!$forceRefresh && isset($this->knownVars)) {
             return false;
             // means that those variables have been cached and no changes needed
         }
     }
     $now = microtime(true);
     $this->em->SetSurveyMode($this->surveyMode);
     // TODO - do I need to force refresh, or trust that createFieldMap will cache langauges properly?
     $fieldmap = createFieldMap($surveyid, $style = 'full', $forceRefresh, false, $_SESSION['LEMlang']);
     $this->sid = $surveyid;
     $this->runtimeTimings[] = array(__METHOD__ . '.createFieldMap', microtime(true) - $now);
     //      LimeExpressionManager::ShowStackTrace();
     $now = microtime(true);
     if (!isset($fieldmap)) {
         return false;
         // implies an error occurred
     }
     $this->knownVars = array();
     // mapping of VarName to Value
     $this->qcode2sgqa = array();
     $this->tempVars = array();
     $this->qid2code = array();
     // List of codes for each question - needed to know which to NULL if a question is irrelevant
     $this->jsVar2qid = array();
     $this->qcode2sgq = array();
     $this->alias2varName = array();
     $this->varNameAttr = array();
     $this->questionId2questionSeq = array();
     $this->questionId2groupSeq = array();
     $this->questionSeq2relevance = array();
     $this->groupId2groupSeq = array();
     $this->qid2validationEqn = array();
     $this->groupSeqInfo = array();
     $this->gseq2relevanceStatus = array();
     // Since building array of allowable answers, need to know preset values for certain question types
     $presets = array();
     $presets['G'] = array('M' => $this->gT("Male"), 'F' => $this->gT("Female"));
     $presets['Y'] = array('Y' => $this->gT("Yes"), 'N' => $this->gT("No"));
     $presets['C'] = array('Y' => $this->gT("Yes"), 'N' => $this->gT("No"), 'U' => $this->gT("Uncertain"));
     $presets['E'] = array('I' => $this->gT("Increase"), 'S' => $this->gT("Same"), 'D' => $this->gT("Decrease"));
     $this->gseq2info = $this->getGroupInfoForEM($surveyid, $_SESSION['LEMlang']);
     for ($i = 0; $i < count($this->gseq2info); ++$i) {
         $gseq = $this->gseq2info[$i];
         $this->groupId2groupSeq[$gseq['gid']] = $i;
         // Only used for preview group
     }
     $qattr = $this->getQuestionAttributesForEM($surveyid, NULL, $_SESSION['LEMlang']);
     $this->qattr = $qattr;
     $this->runtimeTimings[] = array(__METHOD__ . ' - question_attributes_model->getQuestionAttributesForEM', microtime(true) - $now);
     $now = microtime(true);
     $this->qans = $this->getAnswerSetsForEM($surveyid, NULL, $_SESSION['LEMlang']);
     $this->runtimeTimings[] = array(__METHOD__ . ' - answers_model->getAnswerSetsForEM', microtime(true) - $now);
     $now = microtime(true);
     $q2subqInfo = array();
     $this->multiflexiAnswers = array();
     foreach ($fieldmap as $fielddata) {
         if (!isset($fielddata['fieldname']) || !preg_match('#^\\d+X\\d+X\\d+#', $fielddata['fieldname'])) {
             continue;
             // not an SGQA value
         }
         $sgqa = $fielddata['fieldname'];
         $type = $fielddata['type'];
         $mandatory = $fielddata['mandatory'];
         $fieldNameParts = explode('X', $sgqa);
         $groupNum = $fieldNameParts[1];
         $aid = isset($fielddata['aid']) ? $fielddata['aid'] : '';
         $sqid = isset($fielddata['sqid']) ? $fielddata['sqid'] : '';
         $questionId = $fieldNameParts[2];
         $questionNum = $fielddata['qid'];
         $relevance = isset($fielddata['relevance']) ? $fielddata['relevance'] : 1;
         $grelevance = isset($fielddata['grelevance']) ? $fielddata['grelevance'] : 1;
         $hidden = isset($qattr[$questionNum]['hidden']) ? $qattr[$questionNum]['hidden'] == '1' : false;
         $scale_id = isset($fielddata['scale_id']) ? $fielddata['scale_id'] : '0';
         $preg = isset($fielddata['preg']) ? $fielddata['preg'] : NULL;
         // a perl regular exrpession validation function
         $defaultValue = isset($fielddata['defaultvalue']) ? $fielddata['defaultvalue'] : NULL;
         if (trim($preg) == '') {
             $preg = NULL;
         }
         $help = isset($fielddata['help']) ? $fielddata['help'] : '';
         $other = isset($fielddata['other']) ? $fielddata['other'] : '';
         if (isset($this->questionId2groupSeq[$questionNum])) {
             $groupSeq = $this->questionId2groupSeq[$questionNum];
         } else {
             $groupSeq = isset($fielddata['groupSeq']) ? $fielddata['groupSeq'] : -1;
             $this->questionId2groupSeq[$questionNum] = $groupSeq;
         }
         if (isset($this->questionId2questionSeq[$questionNum])) {
             $questionSeq = $this->questionId2questionSeq[$questionNum];
         } else {
             $questionSeq = isset($fielddata['questionSeq']) ? $fielddata['questionSeq'] : -1;
             $this->questionId2questionSeq[$questionNum] = $questionSeq;
         }
         if (!isset($this->groupSeqInfo[$groupSeq])) {
             $this->groupSeqInfo[$groupSeq] = array('qstart' => $questionSeq, 'qend' => $questionSeq);
         } else {
             $this->groupSeqInfo[$groupSeq]['qend'] = $questionSeq;
             // with each question, update so know ending value
         }
         // Create list of codes associated with each question
         $codeList = isset($this->qid2code[$questionNum]) ? $this->qid2code[$questionNum] : '';
         if ($codeList == '') {
             $codeList = $sgqa;
         } else {
             $codeList .= '|' . $sgqa;
         }
         $this->qid2code[$questionNum] = $codeList;
         $readWrite = 'Y';
         // Set $ansArray
         switch ($type) {
             case '!':
                 //List - dropdown
             //List - dropdown
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
             //LIST WITH COMMENT drop-down/radio-button list + textarea
             case '1':
                 //Array (Flexible Labels) dual scale  // need scale
             //Array (Flexible Labels) dual scale  // need scale
             case 'H':
                 //ARRAY (Flexible) - Column Format
             //ARRAY (Flexible) - Column Format
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'R':
                 //RANKING STYLE
                 $ansArray = isset($this->qans[$questionNum]) ? $this->qans[$questionNum] : NULL;
                 if ($other == 'Y' && ($type == 'L' || $type == '!')) {
                     if (preg_match('/other$/', $sgqa)) {
                         $ansArray = NULL;
                         // since the other variable doesn't need it
                     } else {
                         $_qattr = isset($qattr[$questionNum]) ? $qattr[$questionNum] : array();
                         if (isset($_qattr['other_replace_text']) && trim($_qattr['other_replace_text']) != '') {
                             $othertext = trim($_qattr['other_replace_text']);
                         } else {
                             $othertext = $this->gT('Other:');
                         }
                         $ansArray['0~-oth-'] = '0|' . $othertext;
                     }
                 }
                 break;
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case '5':
                 //5 POINT CHOICE radio-buttons
                 $ansArray = NULL;
                 break;
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case ';':
                 //ARRAY (Multi Flexi) Text
             //ARRAY (Multi Flexi) Text
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
             //Multiple choice with comments checkbox + text
             case 'D':
                 //DATE
             //DATE
             case '*':
                 //Equation
             //Equation
             case 'I':
                 //Language Question
             //Language Question
             case '|':
                 //File Upload
             //File Upload
             case 'X':
                 //BOILERPLATE QUESTION
                 $ansArray = NULL;
                 break;
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
                 $ansArray = $presets[$type];
                 break;
         }
         // set $subqtext text - for display of primary sub-question
         $subqtext = '';
         switch ($type) {
             default:
                 $subqtext = isset($fielddata['subquestion']) ? $fielddata['subquestion'] : '';
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $subqtext = isset($fielddata['subquestion1']) ? $fielddata['subquestion1'] : '';
                 $ansList = array();
                 if (isset($fielddata['answerList'])) {
                     foreach ($fielddata['answerList'] as $ans) {
                         $ansList['1~' . $ans['code']] = $ans['code'] . '|' . $ans['answer'];
                     }
                     $this->multiflexiAnswers[$questionNum] = $ansList;
                 }
                 break;
         }
         // Set $varName (question code / questions.title), $rowdivid, $csuffix, $sqsuffix, and $question
         $rowdivid = NULL;
         // so that blank for types not needing it.
         $sqsuffix = '';
         switch ($type) {
             case '!':
                 //List - dropdown
             //List - dropdown
             case '5':
                 //5 POINT CHOICE radio-buttons
             //5 POINT CHOICE radio-buttons
             case 'D':
                 //DATE
             //DATE
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'I':
                 //Language Question
             //Language Question
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
             //LIST WITH COMMENT drop-down/radio-button list + textarea
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'X':
                 //BOILERPLATE QUESTION
             //BOILERPLATE QUESTION
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case '|':
                 //File Upload
             //File Upload
             case '*':
                 //Equation
                 $csuffix = '';
                 $sqsuffix = '';
                 $varName = $fielddata['title'];
                 if ($fielddata['aid'] != '') {
                     $varName .= '_' . $fielddata['aid'];
                 }
                 $question = $fielddata['question'];
                 break;
             case '1':
                 //Array (Flexible Labels) dual scale
                 $csuffix = $fielddata['aid'] . '#' . $fielddata['scale_id'];
                 $sqsuffix = '_' . $fielddata['aid'];
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'] . '_' . $fielddata['scale_id'];
                 $question = $fielddata['subquestion'] . '[' . $fielddata['scale'] . ']';
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion'] . '[' . $fielddata['scale'] . ']';
                 $rowdivid = substr($sgqa, 0, -2);
                 break;
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
             //ARRAY (Increase/Same/Decrease) radio-buttons
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'H':
                 //ARRAY (Flexible) - Column Format    // note does not have javatbd equivalent - so array filters don't work on it
             //ARRAY (Flexible) - Column Format    // note does not have javatbd equivalent - so array filters don't work on it
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION         // note does not have javatbd equivalent - so array filters don't work on it, but need rowdivid to process validations
             //MULTIPLE NUMERICAL QUESTION         // note does not have javatbd equivalent - so array filters don't work on it, but need rowdivid to process validations
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
             //Multiple choice with comments checkbox + text
             case 'Q':
                 //MULTIPLE SHORT TEXT                 // note does not have javatbd equivalent - so array filters don't work on it
             //MULTIPLE SHORT TEXT                 // note does not have javatbd equivalent - so array filters don't work on it
             case 'R':
                 //RANKING STYLE                       // note does not have javatbd equivalent - so array filters don't work on it
                 $csuffix = $fielddata['aid'];
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'];
                 $question = $fielddata['subquestion'];
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion'];
                 if ($type != 'H') {
                     if ($type == 'P' && preg_match("/comment\$/", $sgqa)) {
                         //                            $rowdivid = substr($sgqa,0,-7);
                     } else {
                         $sqsuffix = '_' . $fielddata['aid'];
                         $rowdivid = $sgqa;
                     }
                 }
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $csuffix = $fielddata['aid'];
                 $sqsuffix = '_' . substr($fielddata['aid'], 0, strpos($fielddata['aid'], '_'));
                 $varName = $fielddata['title'] . '_' . $fielddata['aid'];
                 $question = $fielddata['subquestion1'] . '[' . $fielddata['subquestion2'] . ']';
                 //                    $question = $fielddata['question'] . ': ' . $fielddata['subquestion1'] . '[' . $fielddata['subquestion2'] . ']';
                 $rowdivid = substr($sgqa, 0, strpos($sgqa, '_'));
                 break;
         }
         // $onlynum
         $onlynum = false;
         // the default
         switch ($type) {
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
                 $onlynum = true;
                 break;
             case '*':
                 // Equation
             // Equation
             case ';':
                 //ARRAY (Multi Flexi) Text
             //ARRAY (Multi Flexi) Text
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case 'S':
                 //SHORT FREE TEXT
                 if (isset($qattr[$questionNum]['numbers_only']) && $qattr[$questionNum]['numbers_only'] == '1') {
                     $onlynum = true;
                 }
                 break;
             case 'L':
                 //LIST drop-down/radio-button list
             //LIST drop-down/radio-button list
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'P':
                 //Multiple choice with comments checkbox + text
                 if (isset($qattr[$questionNum]['other_numbers_only']) && $qattr[$questionNum]['other_numbers_only'] == '1' && preg_match('/other$/', $sgqa)) {
                     $onlynum = true;
                 }
                 break;
             default:
                 break;
         }
         // Set $jsVarName_on (for on-page variables - e.g. answerSGQA) and $jsVarName (for off-page  variables; the primary name - e.g. javaSGQA)
         switch ($type) {
             case 'R':
                 //RANKING STYLE
                 $jsVarName_on = 'fvalue_' . $fieldNameParts[2];
                 $jsVarName = 'java' . $sgqa;
                 break;
             case 'D':
                 //DATE
             //DATE
             case 'N':
                 //NUMERICAL QUESTION TYPE
             //NUMERICAL QUESTION TYPE
             case 'S':
                 //SHORT FREE TEXT
             //SHORT FREE TEXT
             case 'T':
                 //LONG FREE TEXT
             //LONG FREE TEXT
             case 'U':
                 //HUGE FREE TEXT
             //HUGE FREE TEXT
             case 'Q':
                 //MULTIPLE SHORT TEXT
             //MULTIPLE SHORT TEXT
             case 'K':
                 //MULTIPLE NUMERICAL QUESTION
             //MULTIPLE NUMERICAL QUESTION
             case 'X':
                 //BOILERPLATE QUESTION
                 $jsVarName_on = 'answer' . $sgqa;
                 $jsVarName = 'java' . $sgqa;
                 break;
             case '!':
                 //List - dropdown
                 if (preg_match("/other\$/", $sgqa)) {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = 'othertext' . substr($sgqa, 0, -5);
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
             case 'L':
                 //LIST drop-down/radio-button list
                 if (preg_match("/other\$/", $sgqa)) {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = 'answer' . $sgqa . "text";
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
             case '5':
                 //5 POINT CHOICE radio-buttons
             //5 POINT CHOICE radio-buttons
             case 'G':
                 //GENDER drop-down list
             //GENDER drop-down list
             case 'I':
                 //Language Question
             //Language Question
             case 'Y':
                 //YES/NO radio-buttons
             //YES/NO radio-buttons
             case '*':
                 //Equation
             //Equation
             case '1':
                 //Array (Flexible Labels) dual scale
             //Array (Flexible Labels) dual scale
             case 'A':
                 //ARRAY (5 POINT CHOICE) radio-buttons
             //ARRAY (5 POINT CHOICE) radio-buttons
             case 'B':
                 //ARRAY (10 POINT CHOICE) radio-buttons
             //ARRAY (10 POINT CHOICE) radio-buttons
             case 'C':
                 //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             //ARRAY (YES/UNCERTAIN/NO) radio-buttons
             case 'E':
                 //ARRAY (Increase/Same/Decrease) radio-buttons
             //ARRAY (Increase/Same/Decrease) radio-buttons
             case 'F':
                 //ARRAY (Flexible) - Row Format
             //ARRAY (Flexible) - Row Format
             case 'H':
                 //ARRAY (Flexible) - Column Format
             //ARRAY (Flexible) - Column Format
             case 'M':
                 //Multiple choice checkbox
             //Multiple choice checkbox
             case 'O':
                 //LIST WITH COMMENT drop-down/radio-button list + textarea
                 if ($type == 'O' && preg_match('/_comment$/', $varName)) {
                     $jsVarName_on = 'answer' . $sgqa;
                 } else {
                     $jsVarName_on = 'java' . $sgqa;
                 }
                 $jsVarName = 'java' . $sgqa;
                 break;
             case ':':
                 //ARRAY (Multi Flexi) 1 to 10
             //ARRAY (Multi Flexi) 1 to 10
             case ';':
                 //ARRAY (Multi Flexi) Text
                 $jsVarName = 'java' . $sgqa;
                 $jsVarName_on = 'answer' . $sgqa;
                 break;
             case '|':
                 //File Upload
                 // Only want the use the one that ends in '_filecount'
                 //                        $goodcode = preg_replace("/^(.*?)(_filecount)?$/","$1",$sgqa);
                 //                        $jsVarName = $goodcode . '_filecount';
                 $jsVarName = $sgqa;
                 $jsVarName_on = $jsVarName;
                 break;
             case 'P':
                 //Multiple choice with comments checkbox + text
                 if (preg_match("/(other|comment)\$/", $sgqa)) {
                     $jsVarName_on = 'answer' . $sgqa;
                     // is this true for survey.php and not for group.php?
                     $jsVarName = 'java' . $sgqa;
                 } else {
                     $jsVarName = 'java' . $sgqa;
                     $jsVarName_on = $jsVarName;
                 }
                 break;
         }
         if (!is_null($rowdivid) || $type == 'L' || $type == 'N' || $type == '!' || !is_null($preg) || $type == 'S' || $type == 'T' || $type == 'U' || $type == '|') {
             if (!isset($q2subqInfo[$questionNum])) {
                 $q2subqInfo[$questionNum] = array('qid' => $questionNum, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'sgqa' => $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'mandatory' => $mandatory, 'varName' => $varName, 'type' => $type, 'fieldname' => $sgqa, 'preg' => $preg, 'rootVarName' => $fielddata['title']);
             }
             if (!isset($q2subqInfo[$questionNum]['subqs'])) {
                 $q2subqInfo[$questionNum]['subqs'] = array();
             }
             if ($type == 'L' || $type == '!') {
                 if (!is_null($ansArray)) {
                     foreach (array_keys($ansArray) as $key) {
                         $parts = explode('~', $key);
                         if ($parts[1] == '-oth-') {
                             $parts[1] = 'other';
                         }
                         $q2subqInfo[$questionNum]['subqs'][] = array('rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum . $parts[1], 'varName' => $varName, 'sqsuffix' => '_' . $parts[1]);
                     }
                 }
             } else {
                 if ($type == 'N' || $type == 'S' || $type == 'T' || $type == 'U') {
                     $q2subqInfo[$questionNum]['subqs'][] = array('varName' => $varName, 'rowdivid' => $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'jsVarName' => 'java' . $surveyid . 'X' . $groupNum . 'X' . $questionNum, 'jsVarName_on' => $jsVarName_on);
                 } else {
                     $q2subqInfo[$questionNum]['subqs'][] = array('rowdivid' => $rowdivid, 'varName' => $varName, 'jsVarName_on' => $jsVarName_on, 'jsVarName' => $jsVarName, 'csuffix' => $csuffix, 'sqsuffix' => $sqsuffix);
                 }
             }
         }
         $ansList = '';
         if (isset($ansArray) && !is_null($ansArray)) {
             $answers = array();
             foreach ($ansArray as $key => $value) {
                 $answers[] = "'" . $key . "':'" . htmlspecialchars(preg_replace('/[[:space:]]/', ' ', $value), ENT_QUOTES) . "'";
             }
             $ansList = ",'answers':{ " . implode(",", $answers) . "}";
         }
         // Set mappings of variable names to needed attributes
         $varInfo_Code = array('jsName_on' => $jsVarName_on, 'jsName' => $jsVarName, 'readWrite' => $readWrite, 'hidden' => $hidden, 'question' => $question, 'qid' => $questionNum, 'gid' => $groupNum, 'grelevance' => $grelevance, 'relevance' => $relevance, 'qcode' => $varName, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'type' => $type, 'sgqa' => $sgqa, 'ansList' => $ansList, 'ansArray' => $ansArray, 'scale_id' => $scale_id, 'default' => $defaultValue, 'rootVarName' => $fielddata['title'], 'subqtext' => $subqtext, 'rowdivid' => is_null($rowdivid) ? '' : $rowdivid, 'onlynum' => $onlynum);
         $this->questionSeq2relevance[$questionSeq] = array('relevance' => $relevance, 'grelevance' => $grelevance, 'qid' => $questionNum, 'qseq' => $questionSeq, 'gseq' => $groupSeq, 'jsResultVar_on' => $jsVarName_on, 'jsResultVar' => $jsVarName, 'type' => $type, 'hidden' => $hidden, 'gid' => $groupNum, 'mandatory' => $mandatory, 'eqn' => $type == '*' ? $question : '', 'help' => $help, 'qtext' => $fielddata['question'], 'code' => $varName, 'other' => $other, 'default' => $defaultValue, 'rootVarName' => $fielddata['title'], 'rowdivid' => is_null($rowdivid) ? '' : $rowdivid, 'aid' => $aid, 'sqid' => $sqid);
         $this->knownVars[$sgqa] = $varInfo_Code;
         $this->qcode2sgqa[$varName] = $sgqa;
         $this->jsVar2qid[$jsVarName] = $questionNum;
         $this->qcode2sgq[$fielddata['title']] = $surveyid . 'X' . $groupNum . 'X' . $questionNum;
         // Create JavaScript arrays
         $this->alias2varName[$varName] = array('jsName' => $jsVarName, 'jsPart' => "'" . $varName . "':'" . $jsVarName . "'");
         $this->alias2varName[$sgqa] = array('jsName' => $jsVarName, 'jsPart' => "'" . $sgqa . "':'" . $jsVarName . "'");
         $this->varNameAttr[$jsVarName] = "'" . $jsVarName . "':{ " . "'jsName':'" . $jsVarName . "','jsName_on':'" . $jsVarName_on . "','sgqa':'" . $sgqa . "','qid':" . $questionNum . ",'gid':" . $groupNum . ",'type':'" . $type . "','default':'" . (is_null($defaultValue) ? '' : $defaultValue) . "','rowdivid':'" . (is_null($rowdivid) ? '' : $rowdivid) . "','onlynum':'" . ($onlynum ? '1' : '') . "','gseq':" . $groupSeq . $ansList;
         if ($type == 'M' || $type == 'P') {
             $this->varNameAttr[$jsVarName] .= ",'question':'" . htmlspecialchars(preg_replace('/[[:space:]]/', ' ', $question), ENT_QUOTES) . "'";
         }
         $this->varNameAttr[$jsVarName] .= "}";
     }
     $this->q2subqInfo = $q2subqInfo;
     // Now set tokens
     if (isset($_SESSION['token']) && $_SESSION['token'] != '') {
         //Gather survey data for tokenised surveys, for use in presenting questions
         $_SESSION['thistoken'] = getTokenData($surveyid, $_SESSION['token']);
         $this->knownVars['TOKEN:TOKEN'] = array('code' => $_SESSION['token'], 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         //queXS Addition
         $this->knownVars['TOKEN:CASEID'] = array('code' => $_SESSION['token'], 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
     } else {
         //queXS Addition
         $this->knownVars['TOKEN:CASEID'] = array('code' => 0, 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
     }
     if (isset($_SESSION['thistoken'])) {
         foreach (array_keys($_SESSION['thistoken']) as $tokenkey) {
             if ($anonymized) {
                 $val = "";
             } else {
                 $val = $_SESSION['thistoken'][$tokenkey];
             }
             $key = "TOKEN:" . strtoupper($tokenkey);
             $this->knownVars[$key] = array('code' => $val, 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         }
         //queXS Addition
         //Add all sample variables for this case
         global $connect;
         $use_call = true;
         if (strlen($_SESSION['token']) == 15) {
             $use_call = false;
         }
         if ($use_call) {
             $sql = "SELECT sivr.var,sv.val\n\t\t\t\tFROM sample_var as sv, `case` as c, `call` as cl, `sample_import_var_restrict` as sivr\n\t\t\t\tWHERE c.sample_id = sv.sample_id\n\t\t\t\tAND c.case_id = cl.case_id\n\t\t\t\tAND sv.var_id = sivr.var_id\n\t\t\t\tAND cl.call_id = '{$_SESSION['token']}'";
         } else {
             $sql = "SELECT sivr.var,sv.val\n\t\t\t\tFROM sample_var as sv, `case` as c, `sample_import_var_restrict` as sivr\n\t\t\t\tWHERE c.sample_id = sv.sample_id\n\t\t\t\tAND sv.var_id = sivr.var_id\n\t\t\t\tAND c.token = '{$_SESSION['token']}'";
         }
         $queXSrs = $connect->GetAssoc($sql);
         foreach ($queXSrs as $samplekey => $val) {
             $key = "SAMPLE:" . strtoupper($samplekey);
             $this->knownVars[$key] = array('code' => $val, 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         }
         if ($use_call) {
             $osql = "FROM operator as o, `call` as cl\n\t\t\t\t WHERE o.operator_id = cl.operator_id\n\t\t\t\t AND cl.call_id = '{$_SESSION['token']}'";
             $rsql = "FROM respondent as r, `call` as cl\n\t\t\t \t WHERE cl.call_id = '{$_SESSION['token']}'\n\t\t\t\t AND r.respondent_id = cl.respondent_id";
         } else {
             $osql = "FROM operator as o, `case` as c\n\t \t\t\t WHERE c.token = '{$_SESSION['token']}'\n\t\t\t\t AND o.operator_id = c.current_operator_id";
             $rsql = "FROM respondent as r, `case` as c, call_attempt as ca\n\t\t\t \t WHERE c.token = '{$_SESSION['token']}'\n\t\t\t\t AND ca.case_id = c.case_id\n\t\t\t\t AND ca.end IS NULL\n\t\t\t\t AND r.respondent_id = ca.respondent_id";
         }
         //add operator and respondent details
         $this->knownVars["OPERATOR:FIRSTNAME"] = array('code' => $connect->GetOne("\tSELECT o.firstName " . $osql), 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         $this->knownVars["OPERATOR:LASTNAME"] = array('code' => $connect->GetOne("\tSELECT o.lastName " . $osql), 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         $this->knownVars["RESPONDENT:FIRSTNAME"] = array('code' => $connect->GetOne("\tSELECT r.firstName " . $rsql), 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         $this->knownVars["RESPONDENT:LASTNAME"] = array('code' => $connect->GetOne("\tSELECT r.lastName " . $rsql), 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
     } else {
         // Read list of available tokens from the tokens table so that preview and error checking works correctly
         $attrs = GetAttributeFieldNames($surveyid, false);
         $blankVal = array('code' => '', 'type' => '', 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         foreach ($attrs as $key) {
             if (preg_match('/^(firstname|lastname|email|usesleft|token|attribute_\\d+)$/', $key)) {
                 $this->knownVars['TOKEN:' . strtoupper($key)] = $blankVal;
             }
         }
         //queXS Addition
         //Add all sample variables for this questionnaire
         global $connect;
         $sql = "SELECT sivr.var,sv.val\n\t\t\t\tFROM `questionnaire` as q, questionnaire_sample as qs, sample_var as sv, `sample_import_var_restrict` as sivr\n\t\t\t\tWHERE q.lime_sid = {$surveyid}\n\t\t\t\tAND qs.questionnaire_id = q.questionnaire_id\n\t\t\t\tAND sivr.sample_import_id = qs.sample_import_id \n\t\t\t\tAND sv.var_id = sivr.var_id \n\t\t\t\tGROUP BY qs.sample_import_id,sivr.var";
         $queXSrs = $connect->GetAssoc($sql);
         foreach ($queXSrs as $samplekey => $val) {
             $key = "SAMPLE:" . strtoupper($samplekey);
             $this->knownVars[$key] = array('code' => $val, 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         }
         $oa = array("OPERATOR:FIRSTNAME", "OPERATOR:LASTNAME", "RESPONDENT:FIRSTNAME", "RESPONDENT:LASTNAME");
         foreach ($oa as $o) {
             $this->knownVars[$o] = array('code' => "", 'jsName_on' => '', 'jsName' => '', 'readWrite' => 'N');
         }
     }
     // set default value for reserved 'this' variable
     $this->knownVars['this'] = array('jsName_on' => '', 'jsName' => '', 'readWrite' => '', 'hidden' => '', 'question' => 'this', 'qid' => '', 'gid' => '', 'grelevance' => '', 'relevance' => '', 'qcode' => 'this', 'qseq' => '', 'gseq' => '', 'type' => '', 'sgqa' => '', 'rowdivid' => '', 'ansList' => '', 'ansArray' => array(), 'scale_id' => '', 'default' => '', 'rootVarName' => 'this', 'subqtext' => '', 'rowdivid' => '');
     $this->runtimeTimings[] = array(__METHOD__ . ' - process fieldMap', microtime(true) - $now);
     usort($this->questionSeq2relevance, 'cmpQuestionSeq');
     $this->numQuestions = count($this->questionSeq2relevance);
     $this->numGroups = count($this->groupSeqInfo);
     return true;
 }