Question Types & Handling

Forsta HX Platform - API Scripting Guide

Question Types & Handling

Grid/Matrix Questions:

// Handle grid questions (multiple rows/cols)
// Format: AQ1_r1 (row 1), AQ1_r2 (row 2), etc.

/**
 * getGridResponses - Collects all responses from a grid/matrix question
 * @param {string} baseQid - The base question ID prefix (e.g., 'AQ1')
 * @returns {Object} - Object with row IDs as keys and values as responses
 * @description Finds all elements matching the grid pattern (baseQid_r*)
 *              and builds an object containing all row responses.
 */
function getGridResponses(baseQid) {
    var responses = {}; // Object to store row ID -> value pairs
    $('[id^="' + baseQid + '_r"]').each(function() {
        var rowId = $(this).attr('id');
        responses[rowId] = $(this).val();
    });
    return responses;
}

/**
 * validateGrid - Validates that a minimum number of grid rows are completed
 * @param {string} baseQid - The base question ID prefix (e.g., 'AQ1')
 * @param {number} requiredRows - Minimum number of rows that must have values
 * @returns {boolean} - Returns true if enough rows are completed
 * @description Gets all grid responses and counts how many have non-empty values.
 *              Compares against the required minimum threshold.
 */
function validateGrid(baseQid, requiredRows) {
    // Get all responses from the grid question
    var responses = getGridResponses(baseQid);
    var completed = Object.keys(responses).filter(function(key) {
        return responses[key] && responses[key] !== '';
    }).length;
    
    return completed >= requiredRows;
}

// Usage
if(!validateGrid('AQ1', 5)) {
    alert('Please complete all 5 rows');
}

Rank Order Questions:

/**
 * getRankOrder - Extracts the current ranking order from a drag-and-drop question
 * @param {string} qid - The question ID containing ranked items
 * @returns {Array} - Array of objects with position, value, and label for each item
 * @description Iterates through ranked items and builds an array of rank data.
 *              Each object contains the item's position (1-indexed), data value,
 *              and display label text.
 */
function getRankOrder(qid) {
    var ranks = []; // Array to store rank position objects
    f(qid).find('.rank-item').each(function(index) {
        ranks.push({
            position: index + 1,
            value: $(this).data('value'),
            label: $(this).text()
        });
    });
    return ranks;
}

/**
 * validateRanking - Validates that no duplicate ranks are assigned
 * @param {string} qid - The ranking question ID to validate
 * @returns {boolean} - Returns true if all ranks are unique, false if duplicates found
 * @description Collects all selected rank values from dropdown selects,
 *              creates a Set to find unique values, and compares lengths.
 *              If lengths differ, duplicates exist.
 */
function validateRanking(qid) {
    // Get all selected values from rank dropdowns using jQuery .map()
    var values = f(qid).find('select').map(function() {
        return $(this).val(); // Get each dropdown's selected value
    }).get(); // Convert jQuery object to plain array
    
    var unique = [...new Set(values)];
    if(unique.length !== values.length) {
        alert('Please ensure each rank is unique');
        return false;
    }
    return true;
}

Open-End Text Processing:

/**
 * countWords - Counts the number of words in a text string
 * @param {string} text - The text to count words in
 * @returns {number} - The number of words found
 * @description Trims whitespace, splits on whitespace characters (\s+),
 *              filters out empty strings, and returns the count.
 *              Used for minimum word count validation on open-ends.
 */
function countWords(text) {
    // .trim() removes leading/trailing whitespace
    // .split(/\s+/) splits on one or more whitespace chars
    // .filter() removes any empty strings from the array
    return text.trim().split(/\s+/).filter(function(word) {
        return word.length > 0;
    }).length;
}

// Validate minimum word count
f(AQ1).on('blur', function() {
    var text = $(this).val();
    var wordCount = countWords(text);
    
    if(wordCount < 10) {
        $(this).addClass('error');
        alert('Please provide at least 10 words');
    } else {
        $(this).removeClass('error');
    }
});

/**
 * bannedWords - Array of words to filter from open-end responses
 * @type {Array}
 * @description List of prohibited words for basic profanity/spam detection.
 *              Expand this array based on your specific filtering needs.
 */
var bannedWords = ['spam', 'test', 'asdf'];

/**
 * containsProfanity - Checks if text contains any banned words
 * @param {string} text - The text to check for banned words
 * @returns {boolean} - Returns true if any banned word is found
 * @description Converts text to lowercase for case-insensitive matching,
 *              then uses .some() to check if any banned word exists.
 *              Returns true on first match found.
 */
function containsProfanity(text) {
    var lower = text.toLowerCase(); // Case-insensitive comparison
    // .some() returns true if callback returns true for any element
    return bannedWords.some(function(word) {
        return lower.includes(word); // Check if banned word exists in text
    });
}