Mobile Optimization

Forsta HX Platform - API Scripting Guide

Mobile Optimization

Device Detection:

/**
 * isMobile - Detects if user is on a mobile device
 * @returns {boolean} - Returns true if mobile device detected
 * @description Uses regex to test navigator.userAgent against known
 *              mobile device identifiers. Includes smartphones and tablets.
 *              Note: User agent detection can be spoofed.
 */
function isMobile() {
    // Test user agent string against common mobile device patterns
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
        .test(navigator.userAgent);
}

/**
 * isTablet - Detects if user is on a tablet device
 * @returns {boolean} - Returns true if tablet device detected
 * @description Checks for tablet-specific user agents (iPad, Android tablets)
 *              while excluding smaller mobile devices.
 */
function isTablet() {
    return /iPad|Android/i.test(navigator.userAgent) && 
           !isMobile();
}

// Apply mobile-specific logic
if(isMobile()) {
    // Simplify complex grids
    f(AQ1).addClass('mobile-simplified');
    
    // Enable touch-friendly controls
    $('.slider').css('height', '44px');
    
    // Show one question at a time
    $('.question').hide();
    $('.question').first().show();
}

Touch Event Handling:

/**
 * bindTouchOrClick - Attaches handler to both click and touch events
 * @param {jQuery} element - The jQuery element to bind events to
 * @param {Function} handler - The callback function to execute
 * @description Creates cross-platform event handling that works on both
 *              desktop (click) and mobile (touchend) devices.
 *              Prevents default to avoid double-firing on some devices.
 */
function bindTouchOrClick(element, handler) {
    // Bind to both click (desktop) and touchend (mobile) events
    element.on('click touchend', function(e) {
        e.preventDefault(); // Prevent default browser behavior
        handler.call(this, e); // Execute handler with proper 'this' context
    });
}

// Swipe detection for mobile navigation
var touchStartX = 0;
var touchEndX = 0;

f(AQ1).on('touchstart', function(e) {
    touchStartX = e.changedTouches[0].screenX;
});

f(AQ1).on('touchend', function(e) {
    touchEndX = e.changedTouches[0].screenX;
    handleSwipe();
});

/**
 * handleSwipe - Processes swipe gestures for mobile navigation
 * @description Compares start and end X coordinates of touch.
 *              If difference exceeds 50px threshold, triggers navigation:
 *              - Swipe left (negative diff): go to next question
 *              - Swipe right (positive diff): go to previous question
 */
function handleSwipe() {
    // Check for left swipe (end position < start position by 50+ pixels)
    if(touchEndX < touchStartX - 50) {
        // Swipe left - navigate forward
        goToNextQuestion();
    }
    // Check for right swipe (end position > start position by 50+ pixels)
    if(touchEndX > touchStartX + 50) {
        // Swipe right - navigate backward
        goToPreviousQuestion();
    }
}

Orientation Change:

// Handle orientation changes
window.addEventListener('orientationchange', function() {
    setTimeout(function() {
        // Recalculate layouts
        adjustLayout();
        
        // Warn if portrait is required
        if(window.orientation === 90 || window.orientation === -90) {
            if(requiresPortrait()) {
                showOrientationWarning();
            }
        }
    }, 200);
});

/**
 * showOrientationWarning - Displays a message asking user to rotate device
 * @description Creates a fixed-position overlay div prompting the user
 *              to switch to portrait mode. Styled with orange background
 *              and centered on screen. Useful for surveys requiring portrait.
 */
function showOrientationWarning() {
    // Create warning div with jQuery, styled inline for immediate display
    var warning = $('<div class="orientation-warning">')
        .text('Please rotate your device to portrait mode')
        .css({
            'position': 'fixed',
            'top': '50%',
            'left': '50%',
            'transform': 'translate(-50%, -50%)',
            'background': '#ff9800',
            'padding': '20px',
            'border-radius': '8px',
            'z-index': 10000
        });
    $('body').append(warning);
}