$(function(){
    var isSurveyPage = (page == 'surveys/index.php');
    // Maintain original value of dataEntryFormValuesChanged. Since we're just triggering piping here, no data is changed, but the jquery triggers will set dataEntryFormValuesChanged = true.
    var thisDataEntryFormValuesChanged = dataEntryFormValuesChanged;
    // Replace instances of fake object tag
    replaceFakeObjectTag();
    // If any fields are using @SETVALUE/@PREFILL, @DEFAULT, @NOW, or @TODAY action tag, make sure piping of default value is performed on page load
    $('#questiontable tr.\\@SETVALUE, #questiontable tr.\\@PREFILL, #questiontable tr.\\@DEFAULT, #questiontable tr.\\@NOW, #questiontable tr.\\@TODAY, #questiontable tr.\\@NOW-SERVER, #questiontable tr.\\@TODAY-SERVER, #questiontable tr.\\@NOW-UTC, #questiontable tr.\\@TODAY-UTC').each(function(i, row) {
        var fname = $(row).attr('sq_id');
        var field = $(row).find('[name="'+fname+'"]');
        if (typeof field.val() != 'undefined' && field.val() != '') { // Only do anything if the field has a value
            // Escape the value we're about to pipe
             var val = filter_tags(field.val());
            // Trigger piping on page
            if ($(field).is('select')) {
                field.trigger('change');
            } else if ($('input[name="'+fname+'___radio"]').length) {
                if ($('input[name="'+fname+'___radio"][value="' + val + '"]').length) {
                    updatePipingRadiosDo($('input[name="' + fname + '___radio"][value="' + val + '"]'));
                }
            } else {
                // Set @DEFAULT field to show that it changed value (no need to worry about @PREFILL/@SETVALUE here because they are handled server side)
                if ($('#'+fname+'-tr').hasClass('@DEFAULT') && $(this).attr('ignoreDefault') != '1') {
                    $('input[name="'+fname+'"][type="text"]').addClass('calcChanged');
                }
                // For all other fields, simply propagate the value manually to minimize branching/calculation triggering on page load
                $('.piping_receiver.piperec-'+event_id+'-'+fname).html(val);
                $('.piping_receiver.piperec-'+event_id+'-'+fname+'-label').html(val);
                $('.piping_receiver.piperec-'+event_id+'-'+fname+'-value').html(val);
            }
        }
    });
    // Enable @RICHTEXT on notes fields
    $('#questiontable tr.\\@RICHTEXT').each(function(i, row) {
        var fieldname = $(row).attr('sq_id');
        $('.expandLinkParent', $(this)).hide();
        // If field also contains @READONLY, then do not enable rich text
        if (!$(this).hasClass('@READONLY') && !(isSurveyPage && $(this).hasClass('@READONLY-SURVEY')) && !(!isSurveyPage && $(this).hasClass('@READONLY-FORM'))) {
            // Regular rich text field
            $('textarea[name="'+fieldname+'"][rc-align="left"]').height(250).addClass('notesbox-richtext-left');
            $('textarea[name="'+fieldname+'"][rc-align="right"]').height(200).addClass('notesbox-richtext-right');
        } else {
            // Deal with read-only rich text field one field at a time
            var fieldclass = 'disabled-rcfield-'+fieldname;
            $('textarea[name="'+fieldname+'"][rc-align="left"]').height(250).addClass(fieldclass);
            $('textarea[name="'+fieldname+'"][rc-align="right"]').height(200).addClass(fieldclass);
            initTinyMCEglobal(fieldclass, ($('textarea[name="'+fieldname+'"][rc-align="right"]').length > 0), true, false);
            $('textarea[name="'+fieldname+'"]').removeClass(fieldclass);
        }
    });
    setTimeout(function() {
        var editorReadOnly = (page == 'DataEntry/index.php' && $('#submit-btn-saverecord:visible').length==0);
        initTinyMCEglobal('notesbox-richtext-left', false, editorReadOnly, (page == 'DataEntry/index.php'));
        initTinyMCEglobal('notesbox-richtext-right', true, editorReadOnly, (page == 'DataEntry/index.php'));
    }, 10);
    // Now set dataEntryFormValuesChanged back to its original value
    dataEntryFormValuesChanged = thisDataEntryFormValuesChanged;
    if (page != 'Design/online_designer.php') {
        // Field embedding
        doFieldEmbedding();
        // Now run @READONLY functions since they were delayed for Field Embedding
        if (pageHasEmbeddedFields) triggerActionTags();
        // Set the onclick function of all the missing data buttons for only fields that have been embedded
        if (page != 'surveys/index.php') initMissingDataBtns('.rc-field-embed .missingDataButton');
        // Initialize any File Upload fields with @INLINE action tag
        initInlineImages();
        // Now initialize auto-complete drop-downs since they were delayed for Field Embedding
        enableDropdownAutocomplete();
        // Init secondary unique identifier check
        if (typeof secondary_pk != 'undefined' && secondary_pk != '' && $('#form :input[name="'+secondary_pk+'"]').length) {
            // Create onblur event to make an ajax call to check uniqueness
            // Add extra inline onblur attribute for fastest reaction time to prevent submitting form
            var onblur = ($('#form :input[name="'+secondary_pk+'"]').attr('onblur') == null) ? '' : ($('#form :input[name="'+secondary_pk+'"]').attr('onblur')+';');
            $('#form :input[name="'+secondary_pk+'"]').attr('onblur', onblur+'checkSecondaryUniqueField($(this));');
        }
    } else {
        showDescriptiveTextImages();
    }
    // Remove scrollbars on checkbox divs if less than a certain height
    // Array.from(document.querySelectorAll('.check-box-holder')).forEach(function(item) {
    //     if (item.offsetHeight < 600) item.style.overflowY = 'hidden';
    // });
    // if (isSurveyPage) {
    //     Array.from(document.querySelectorAll('.check-box-enhanced-holder')).forEach(function (item) {
    //         if (item.offsetHeight < 1200) item.style.overflowY = 'hidden';
    //     });
    // }
    // Floating matrix headers
    enabledFloatingMatrixHeaders();
    // Set CSS via JS for piped File Upload fields using "inline" - e.g., [field:inline]
    $('#form span.piping_receiver>object>iframe').parent().parent().css({'display':'block','width':'100%'});
    // Hide parts of the page from being printed
    $('#__SUBMITBUTTONS__-tr, #__DELETEBUTTONS__-tr, #__LOCKRECORD__-tr, img.ui-datepicker-trigger').addClass('d-print-none');
    $('#questiontable td.context_msg').parent().addClass('d-print-none');
    // If a text field has a saved value, but then the field's value is manually removed, remove the "hasval" attribute
    // from the field's TR row to prevent issues related to required field detection after submitting the page.
    $('#questiontable tr[sq_id][req=1][hasval=1]').each(function(){
        var fieldOb = $("input[type=text][name='"+$(this).attr('sq_id')+"']", this);
        if (!fieldOb.length) return;
        fieldOb.blur(function(){
           if ($(this).val() == '') removeHasVal($(this).attr('name'));
        });
    });
    // Auto-scroll feature for surveys when selecting a value for a drop-down or radio field while on a mobile device
    if (isSurveyPage && isMobileDevice) autoScroll.init();
    // Warn about back button
    setCookie('redcap-survey-nav-alert', 0);
    if (isSurveyPage && (($('input[name=__page__').val() == 'undefined' ? 0 : $('input[name=__page__').val()) * 1) > 1) {
        $(window).on('beforeunload', function(e) {
            if (!window['regularSubmit']) {
                setCookie('redcap-survey-nav-alert', 1);
                console.warn('Please do not use the browser\'s back or forward buttons to navigate surveys!');
            }
        });
    }
    // Remove initial change flag from all inputs
    $('input.calcChangedInitial').one("change", function() {
        $(this).removeClass('calcChangedInitial'); 
    });
});
$(window).on('resize', function () {
    enabledFloatingMatrixHeaders();
});

// Is element visible in the browser viewport?
!function(t){var i=t(window);t.fn.visible=function(t,e,o){if(!(this.length<1)){var r=this.length>1?this.eq(0):this,n=r.get(0),f=i.width(),h=i.height(),o=o?o:"both",l=e===!0?n.offsetWidth*n.offsetHeight:!0;if("function"==typeof n.getBoundingClientRect){var g=n.getBoundingClientRect(),u=g.top>=0&&g.top<h,s=g.bottom>0&&g.bottom<=h,c=g.left>=0&&g.left<f,a=g.right>0&&g.right<=f,v=t?u||s:u&&s,b=t?c||a:c&&a;if("both"===o)return l&&v&&b;if("vertical"===o)return l&&v;if("horizontal"===o)return l&&b}else{var d=i.scrollTop(),p=d+h,w=i.scrollLeft(),m=w+f,y=r.offset(),z=y.top,B=z+r.height(),C=y.left,R=C+r.width(),j=t===!0?B:z,q=t===!0?z:B,H=t===!0?R:C,L=t===!0?C:R;if("both"===o)return!!l&&p>=q&&j>=d&&m>=L&&H>=w;if("vertical"===o)return!!l&&p>=q&&j>=d;if("horizontal"===o)return!!l&&m>=L&&H>=w}}}}(jQuery);

// Replace instances of fake object tag
function replaceFakeObjectTag()
{
    if (typeof fakeObjectTag == 'undefined') return;
    $(fakeObjectTag).each(function(){
        var parent = $(this).parent();
        if (typeof parent.html() == 'undefined') return;
        parent.html( parent.html().replace(fakeObjectTag, "object").replace("/"+fakeObjectTag, "/object") );
    });
}

// Embed fields
function doFieldEmbedding()
{
    // Is survey page?
    var isSurveyPage = (page == 'surveys/index.php');
    var embeddedFields = new Array();
    var i = 0;
    // Do not perform field embedding on the eConsent certification page (not necessary and actually causes issues with required fields).
    // Prevent this simply by removing the rc-field-embed class, but finish out this function since it is necessary for the whole flow.
    if ($('#econsent_confirm_checkbox_div').length) {
        $('.rc-field-embed').removeClass('rc-field-embed');
    }
    // Deal with survey enhanced choices: Due to labels being duplicated here, remove the embedded field in the original label.
    if (isSurveyPage) {
        $('#questiontable .enhancedchoice .rc-field-embed').each(function(){
            $('#questiontable .rc-field-embed[var="'+$(this).attr('var')+'"]:first').remove();
        });
    }
    // Loop through HTML content and substitute in native page content
    $('#questiontable .rc-field-embed').each(function ()
    {
        var this_field = $(this).attr('var'); // field_name
        // Find the 'real tr' for the field to be relocated
        var source_tr = $("tr[sq_id='" + this_field + "']");
        // Make sure field is not the record ID field. If so, give a warning.
        if (this_field == table_pk) {
            $(this).attr('error','1').html('<span class="text-danger fs11 rc-field-embed-error"><i class="fas fa-exclamation-triangle"></i> '+lang.design_799+' '+lang.design_823+'</span>');
            return;
        }
        // Make sure field is not from another instrument. If so, give a warning.
        if (source_tr.length === 0 || $(this).hasClass('embed-other-form')) {
            $(this).attr('error','1').html('<span class="text-danger fs11 rc-field-embed-error"><i class="fas fa-exclamation-triangle"></i> '+lang.design_799+' "'+this_field+'" '+lang.design_821+'</span>');
            return;
        }
        // Make sure we don't embed a field more than once. Give a warning.
        if (in_array(this_field, embeddedFields)) {
            $(this).attr('error','1').html('<span class="text-danger fs11 rc-field-embed-error"><i class="fas fa-exclamation-triangle"></i> '+lang.design_799+' "'+this_field+'" '+lang.design_800+'</span>');
            return;
        }
        // Make sure the field isn't embedded in itself
        if ($("tr[sq_id='" + this_field + "'] .rc-field-embed[var='" + this_field + "']").length) {
            $(this).attr('error','1').html('<span class="text-danger fs11 rc-field-embed-error"><i class="fas fa-exclamation-triangle"></i> '+lang.design_799+' "'+this_field+'" '+lang.design_833+'</span>');
            return;
        }
        // Do not embed a field in the Context Msg at the top of a form
        if ($("td.context_msg .rc-field-embed[var='" + this_field + "']").length && $(this).parentsUntil('td.context_msg').length) {
            $(this).removeClass('rc-field-embed');
            return;
        }
        // Move the field
        embeddedFields[i++] = this_field;
        var displayIcons = $(this).hasClass('embed-show-icons');
        var displayMissingIcon = (!displayIcons && source_tr.attr('hasmdcval') == '1');
        var align = (source_tr.children('td').length === (isSurveyPage ? 2 : 1)) ? "left": "right";
        var source_data;
        if (source_tr.attr('fieldtype') == 'descriptive') {
            // Descriptive field
            var lastTdDesc = $('tr#' + this_field + '-tr>td:last');
            if (lastTdDesc.length) {
                // Wrap in spans so that children() can pick up text when not using rich text editor
                lastTdDesc.html("<span>"+lastTdDesc.html()+"</span>");
            }
            source_data = lastTdDesc.children();
        } else if (source_tr.attr('mtxgrp')) {
            // Matrix field
            var iconsTd = null;
            var iconsHtml = "";
            // Add Data History & Field Comment Log/Data Resolution Workflow icons
            if (isSurveyPage === false && displayIcons) {
                // Place the icons into a span tag so you can do CSS to control their wrapping
                iconsTd =  $('.rc-field-icons', source_tr);
                // Place the icons into a span tag so you can do CSS to control their wrapping
                var trp = $('table[role="presentation"]:first', source_tr);
                if (trp.length) {
                    // Set icon style
                    var iconStyle = $('.rc-field-icons', this).attr('style');
                    $('.rc-field-icons', this).attr('style', iconStyle+'width:'+iconsTd.outerWidth()+'px !important;');
                    iconsTd =  $('.rc-field-icons', source_tr);
                }
            }
            var header_table = $('#' + source_tr.attr('mtxgrp') + '-mtxhdr-tr').find('table:first').clone();
            header_table.find('td:first').remove();
            var data_table = source_tr.find('table:first').clone();
            data_table.find('td:first').remove();
            source_tr.find('td:last').remove();
            source_tr.find('table:first').remove();
            if (iconsTd !== null) {
                iconsHtml = '<div class="float-left mt-2" style="width:10%;">' + iconsTd.html() + '</div>';
                header_table.css('width', '90%').addClass('float-left');
                data_table.css('width', '90%').addClass('float-left');
            }
            source_data = $("<div class='clearfix'>").append(iconsHtml).append(header_table).append(data_table).append(source_tr.find('.resetLinkParent').clone());
        } else if (align === "right") {
            // Vanilla- take the td.data
            source_data =  $("td.data", source_tr).children();
        } else if ($('a.fileuploadlink, a.filedownloadlink', source_tr).length > 0) {
            // This is a file upload field which needs an exception
            source_data = source_tr.find("td").not('questionnum').find('label').nextAll();
        } else {
            // This rule handles the majority of left-aligned fields
            // Take everything after the div.space separator
            source_data = source_tr.find("td").not('questionnum').find("div.space").nextAll();
        }

        // If we still didn't find anything - then lets log an error and continue
        if (!source_data.length) {
            return true;
        }
        // If has @INLINE action tag, add new class for embedded span
        if (source_tr.hasClass('@INLINE')) $(this).addClass('file-upload-inline-embed');
        // If has @HIDDEN action tag, add new class for embedded span to keep it hidden
        if (source_tr.hasClass('@HIDDEN')
            || (isSurveyPage  && source_tr.hasClass('@HIDDEN-SURVEY'))
            || (!isSurveyPage && source_tr.hasClass('@HIDDEN-FORM'))
        ) {
            $(this).addClass('hide');
        }
        // Adjust width of some non-hidden inputs
        $("input[type!='hidden']", source_data.parent()).each(function (i, e) {
            var type = $(e).prop('type');
            // Left aligned stuff has a rci-left class... going to leave it for now.
            if ($(e).hasClass("rci-left")) $(e).removeClass('rci-left');
            if (type == 'text' && !$(this).hasClass('hasDatepicker') && !$(this).hasClass('hiddenradio')) $(this).css({'max-width': '380px'});
            if (type === 'textarea' || (type == 'text' && !$(this).hasClass('hasDatepicker') && !$(this).hasClass('hiddenradio'))) $(this).css('width', '95%');
        });
        // Move contents of source
        $(this).html(source_data);
        // Add Data History & Field Comment Log/Data Resolution Workflow icons
        if (isSurveyPage === false && (displayIcons || displayMissingIcon)) {
            // Place the icons into a span tag so you can do CSS to control their wrapping
            if (typeof source_tr.attr('mtxgrp') == 'undefined') { // Icons cannot yet be displayed for embedded matrix fields!
                var iconsTd =  $('.rc-field-icons', source_tr);
                // Place the icons into a span tag so you can do CSS to control their wrapping
                var trp = $('table[role="presentation"]:first', source_tr);
                if (trp.length) {
                    // Replace the first TD with the data
                    trp.find('td:first').html(source_data);
                    $(this).html(trp);
                    // Set icon style
                    var iconStyle = $('.rc-field-icons', this).attr('style');
                    $('.rc-field-icons', this).attr('style', iconStyle+'width:'+iconsTd.outerWidth()+'px !important;');
                    // If the Missing icon is the only icon to be displayed, then remove all else in the rc-field-icons box
                    if (displayMissingIcon) {
                        $('.rc-field-icons :not(.missingDataButton)', this).remove();
                        $('.rc-field-icons', this).attr('style', 'padding:0px;margin-left:0px;margin-right:2px;width:14px !important;');
                    }
                }
            }
        }
    });
    // If a field is embedded inside another embedded field, then display an error
    $('#questiontable tr.row-field-embedded .rc-field-embed').each(function(){
        // Re-display the original field
        var tr = $(this).parentsUntil('tr.row-field-embedded').parent();
        var tr_id = tr.attr('sq_id');
        tr.removeClass('hide');
        // If this embedded already has an error, don't go further here
        if ($(this).attr('error') == '1') return;
        // Add warning note
        var this_field = $(this).attr('var');
        $(this).after('<div class="text-danger fs11"><i class="fas fa-exclamation-triangle"></i> '+lang.design_799+' "'+tr_id+'" '+lang.design_801+' {'+this_field+'} '+lang.design_802+' {'+this_field+'} '+lang.design_803+'</div>');
    });
    // Display the form/survey
    displayQuestionTable();
    // Open Save button tooltip	fixed at top-right of data entry forms (re-run this now that the table has been displayed)
    if (i > 0 && page == 'DataEntry/index.php') displayFormSaveBtnTooltip();
}

function displayQuestionTable()
{
    if (elementExists(document.getElementById('formtop-div'))) document.getElementById('formtop-div').style.display='none';
    if (elementExists(document.getElementById('questiontable_loading'))) document.getElementById('questiontable_loading').style.display='none';
    if (elementExists(document.getElementById('questiontable'))) document.getElementById('questiontable').style.display='table';
    if (elementExists(document.getElementById('form_response_header'))) document.getElementById('form_response_header').style.display='block';
    if (elementExists(document.getElementById('formtop-div'))) document.getElementById('formtop-div').style.display='block';
    if (elementExists(document.getElementById('inviteFollowupSurveyBtn'))) document.getElementById('inviteFollowupSurveyBtn').style.display='block';
}

// Make Descriptive field image attachments load with delay (for server performance reasons)
// This will be triggered by doBranching().
function showDescriptiveTextImages() {
    $(function() {
        var imgClass = 'rc-dt-img';
        var delayIncrement = 1;
        $('img.' + imgClass + ':visible').each(function () {
            var this_image = this;
            var src = $(this_image).attr('src') || '';
            if (!src.length > 0) {
                var lsrc = $(this_image).attr('lsrc') || '';
                if (lsrc.length > 0) {
                    setTimeout(function () {
                        this_image.src = lsrc;
                    }, 100 * (delayIncrement++));
                }
            }
            $(this_image).removeClass(imgClass);
        });
    });
}

// Floating matrix headers
var matrices = [];
function enabledFloatingMatrixHeaders()
{
    var isSurvey = is_survey();
    if (!$('#questiontable').length) return;

    var form = $('#questiontable');
    var formPosLeft = form.offset().left;
    var formWidth = form.width();
    var offset = isSurvey ? 0 : $('#west').width();
    var mtx_bgcolor = $('.labelmatrix ').css('background-color');

    // Destroy existing scroll-triggered function to reset it
    $(window).off("scroll", scrollHandler);

    // If no visible matrixes, then stop here
    if (!$('.headermatrix:visible').length) return;

    // Destroy all existing (in case this function has already been run)
    $('.floatMtxHdr').remove();

    // create floating headers
    var i = 0;
    $('.headermatrix:visible').each(function () {
        var header = $(this);
        var floatingHeader = $('<div></div>').append(header.clone());
        matrices[i++] = {
            "header": header,
            "floatingHeader": floatingHeader
        };
        floatingHeader
            .addClass('floatMtxHdr')
            .css({
                position: 'fixed',
                display: 'none',
                top: '-5px',
                left: (formPosLeft)+'px',
                width: formWidth,
                'border': '1px solid #dddddd',
                'padding-bottom': '5px',
                'padding-left': formWidth - header.width(),
                'background-color': mtx_bgcolor
            })
            .attr('data-padding-left', (formWidth - header.width())+'px')
            .attr('data-extra', ($('td.labelrc.questionnum.col-1').width() + $('td.matrix_first_col_hdr').width() + 10.7) + 'px');
        // Remove IDs of columns to prevent conflict with originals
        floatingHeader.find('td').each(function(){
            if ($(this).prop('id') != '') $(this).removeAttr('id');
        })
        // Add to body (surveys) or center div (data entry form)
        $(isSurvey ? 'body' : 'div#center').append(floatingHeader);
    });
    // MLM: Refresh floating headers
    if (REDCap && REDCap.MultiLanguage && REDCap.MultiLanguage.updateFloatingMatrixHeaders) {
        REDCap.MultiLanguage.updateFloatingMatrixHeaders();
    }

    // decide when to show each floating header based on scroll
    $(window).scroll(scrollHandler);

    // Trigger scroll to display floating headers if just disappeared
    window.scrollTo(window.scrollX, window.scrollY - 1);
    window.scrollTo(window.scrollX, window.scrollY + 1);
}
var scrollHandler = function()
{
    var isSurvey = (page == 'surveys/index.php');
    var offsetTop = (!isSurvey && $('.rcproject-navbar:visible').length) ? $('.rcproject-navbar').outerHeight() : 0;
    var scrollTop = $(window).scrollTop();
    for (var i = 0; i < matrices.length; i++) {
        try {
            var header = matrices[i].header;
            var matrixGroup = header.attr('hdrmtxgrp');
            var inViewport = false;
            $('#questiontable tr.mtxfld[mtxgrp="'+matrixGroup+'"]').each(function(){
                if ($(this).visible(true)) {
                    inViewport = true;
                    return;
                }
            });
            var floatingHeader = matrices[i].floatingHeader;
            if (inViewport) {
                var headerTop = header.offset().top;
                var lastRow = $('#questiontable tr.mtxfld[mtxgrp=' + matrixGroup + ']:visible:last');
                var lastRowTop = lastRow.offset().top;
                if (scrollTop > headerTop && scrollTop <= lastRowTop) {
                    var top = 0;
                    if (scrollTop > (lastRowTop - floatingHeader.height())) top = -(scrollTop - (lastRowTop - floatingHeader.height()) + 2);		// + 2 to prevent floating header from overlapping last row
                    floatingHeader.css({
                        display: 'block',
                        top: offsetTop + top + 'px'
                    });
                } else {
                    floatingHeader.css({
                        display: 'none'
                    });
                }
            } else {
                floatingHeader.css({
                    display: 'none'
                });
            }
        } catch(e){}
    }
}

// Update checkboxes for piping
function updatePipingCheckboxes(ob) {
    var name = $(ob).attr('name').substring(8, $(ob).attr('name').length);
    var labelsChecked = new Array(), valsChecked = new Array(), i=0;
    var labelsUnchecked = new Array(), valsUnchecked = new Array(), j=0;
    var isMatrix = $(ob).parent().hasClass('choicematrix');
    var matrixName = isMatrix ? $(ob).parentsUntil('table').parent().parentsUntil('tr').parent().attr('mtxgrp') : "";
    // Get labels of all choices checked
    $('form#form input[name="__chkn__'+name+'"]').each(function(){
        var thisCode = $(this).attr('code');
        var thisLabel = isMatrix ? $('#matrixheader-'+matrixName+'-'+thisCode).text().trim() : $(this).parent().text().trim();
        var thisChecked = $(this).prop('checked');
        var thisCheckedText = thisChecked ? window.lang.global_143 : window.lang.global_144;
        var thisCheckedVal = thisChecked ? '1' : '0';
        if (thisChecked) {
            labelsChecked[i] = thisLabel;
            valsChecked[i] = thisCode;
            i++;
        } else {
            labelsUnchecked[j] = thisLabel;
            valsUnchecked[j] = thisCode;
            j++;
        }
        // Set "checked"/"unchecked" for any using [checkbox(code)]
        $(piping_receiver_class_field_js+event_id+'-'+name+'-choice-'+thisCode+'-label').html(thisCheckedText);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-choice-'+thisCode+'-value').html(thisCheckedVal);
    });
    // If value is a Missing Data Code
    if (missing_data_codes_check && $('#'+name+'_MDLabel:visible').length) {
        labelsChecked[0] = $('#'+name+'_MDLabel').attr('label');
        valsChecked[0] = $('#'+name+'_MDLabel').attr('code');
    }
    // Set value for all piping receivers on page
    if (labelsChecked.length == 0) labelsChecked[0] = missing_data_replacement_js;
    if (labelsUnchecked.length == 0) labelsUnchecked[0] = missing_data_replacement_js;
    if (valsChecked.length == 0) valsChecked[0] = missing_data_replacement_js;
    if (valsUnchecked.length == 0) valsUnchecked[0] = missing_data_replacement_js;
    $(piping_receiver_class_field_js+event_id+'-'+name+'-checked-checked-label').html(labelsChecked.join(', '));
    $(piping_receiver_class_field_js+event_id+'-'+name+'-checked-unchecked-label').html(labelsUnchecked.join(', '));
    $(piping_receiver_class_field_js+event_id+'-'+name+'-checked-checked-value').html(valsChecked.join(', '));
    $(piping_receiver_class_field_js+event_id+'-'+name+'-checked-unchecked-value').html(valsUnchecked.join(', '));
}

// Radio fields
function updatePipingRadiosDo(ob) {
    // Remove "___radio" from end of name
    var name = ob.attr('name').substring(0, ob.attr('name').length-8);
    var label = ob.parent().html();
    // Remove radio input from label
    if (ob.attr('label') != null) {
        label = ob.attr('label');
    } else {
        label = label.substring(label.indexOf('>')+2);
    }
    // Remove any embedded fields from the label, if applicable
    try {
        if ($(label).find("span.rc-field-embed").length) {
            var element = $("<div>" + label + "</div>");
            element.find("span.rc-field-embed").remove();
            label = element.html();
        }
    } catch (e) { }
    if (label.substring(0, 7) == '<label ') {
        // In case the label is still inside a <label> tag, get the contents of the tag
        label = $(label).first().html();
    }
    var val = (ob.val() != '') ? ob.val() : missing_data_replacement_js;
    updatePipingRadiosDoValLabel(name,val,label);
}
function updatePipingRadiosDoValLabel(name,val,label) {
    // Set value for all piping receivers on page
    $(piping_receiver_class_field_js+event_id+'-'+name).html(label);
    $(piping_receiver_class_field_js+event_id+'-'+name+'-label').html(label);
    $(piping_receiver_class_field_js+event_id+'-'+name+'-value').html(val);
    // Update drop-down options separately via ajax
    try{ updatePipingDropdowns(name,val); } catch(e) { }

}
function updatePipingRadios(selector) {
    $(selector).click(function(){
        updatePipingRadiosDo($(this));
    });
}

// Drop-down fields
function updatePipingDropdownsDo($el, name, val, label) {
        if (missing_data_codes_check && in_array($el.val(), missing_data_codes)) {
            // Remove parentheses from label
            var posLastParen = label.lastIndexOf('(');
            if (posLastParen > 0) {
                label = label.substr(0, posLastParen).trim();
            }
        }
        var isblank = (val == missing_data_replacement_js);
        // Set value for all piping receivers on page
        $(piping_receiver_class_field_js+event_id+'-'+name).html(label);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-label').html(label);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-value').html(val);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-field-label').html(filter_tags($('#label-'+name).html()));
        if (isblank) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'.pipingrec-hideunderscore').html('');
            $(piping_receiver_class_field_js+event_id+'-'+name+'-label.pipingrec-hideunderscore').html('');
            $(piping_receiver_class_field_js+event_id+'-'+name+'-value.pipingrec-hideunderscore').html('');
        }
        // Update drop-down options separately via ajax
        updatePipingDropdowns(name,val);

}
function updatePipingDropdownsPre(selector) {
    $(selector).on('change', function(){
        var $this = $(this)
        var name = $this.attr('name');
        // Find selected option to get its label
        var isblank = ($this.val() == '');
        var label = !isblank ? $("form#form select[name='" + name + "'] option:selected").text() : missing_data_replacement_js;
        var val = !isblank ? $this.val() : missing_data_replacement_js;
        updatePipingDropdownsDo($this, name, val, label)
    });
}

// Text fields
function updatePipingTextFields(selector) {
    $(selector).blur(function(){
        if ($(this).hasClass('autosug-search')) {
            var idname = $(this).prop('id').split('-');
            var name = idname[0];
            var val = $('#'+$(this).prop('id')+'-span').val();
        } else {
            var name = $(this).attr('name');
            var val = $(this).val();
        }
        // If no value, set as 6 underscores
        var isblank = (val == '');
        val = isblank ? missing_data_replacement_js : filter_tags(val);
        // Does this field have the @RICHTEXT action tag? If so, don't do nl2br()
        if (!$('#'+name+'-tr').hasClass('@RICHTEXT')) {
            val = nl2br(val);
        }
        // Start piping on page
        $(piping_receiver_class_field_js+event_id+'-'+name).html(val);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-label').html(val);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-field-label').html(filter_tags($('#label-'+name).html()));
        if (isblank) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'.pipingrec-hideunderscore').html('');
            $(piping_receiver_class_field_js+event_id+'-'+name+'-label.pipingrec-hideunderscore').html('');
        }
        // Convert time/datetime to am/pm format
        if ($(piping_receiver_class_field_js+event_id+'-'+name+'-ampm').length) {
            // Split into date and time components
            var valDate = "";
            var valTime = val;
            if (val != missing_data_replacement_js) {
                if (val.indexOf(" ") > -1) {
                    valComp = val.split(" ");
                    valDate = valComp[0];
                    valTime = valComp[1];
                }
                valTime = format_time(valTime);
            }
            $(piping_receiver_class_field_js+event_id+'-'+name+'-ampm').html( trim(valDate+" "+valTime) );
            if (isblank) {
                $(piping_receiver_class_field_js+event_id+'-'+name+'-ampm.pipingrec-hideunderscore').html('');
            }
        }
        // Convert date/datetime to year, month, or day component
        if ($(piping_receiver_class_field_js+event_id+'-'+name+'-year').length) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'-year').html( val == missing_data_replacement_js ? val : year(convertDateToYMD(this)) );
        }
        if ($(piping_receiver_class_field_js+event_id+'-'+name+'-month').length) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'-month').html( val == missing_data_replacement_js ? val : month(convertDateToYMD(this)) );
        }
        if ($(piping_receiver_class_field_js+event_id+'-'+name+'-day').length) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'-day').html( val == missing_data_replacement_js ? val : day(convertDateToYMD(this)) );
        }
        if (isblank) {
            $(piping_receiver_class_field_js+event_id+'-'+name+'-year.pipingrec-hideunderscore').html('');
            $(piping_receiver_class_field_js+event_id+'-'+name+'-month.pipingrec-hideunderscore').html('');
            $(piping_receiver_class_field_js+event_id+'-'+name+'-day.pipingrec-hideunderscore').html('');
        }
        // Update drop-down options separately via ajax
        updatePipingDropdowns(name,val);
    });
}

// File Upload and Signature fields
function updatePipingFileFields(selector) {
    $(selector).change(function(){
        var name = $(this).attr('name');
        var val = $(this).val();
        var isblank = (val == '');
        var label = !isblank ? nl2br(filter_tags($('#form a.filedownloadlink[name="'+name+'"] .fu-fn').attr('vf'))) : missing_data_replacement_js;
        val = !isblank ? nl2br(filter_tags(val)) : missing_data_replacement_js;
        // Set value for all piping receivers on page
        $(piping_receiver_class_field_js+event_id+'-'+name).html(val);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-value').html(val);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-label').html(label);
        $(piping_receiver_class_field_js+event_id+'-'+name+'-field-label').html(filter_tags($('#label-'+name).html()));
        // Pipe the inline image, if applicable (set with delay to give the source field time to update first)
        setTimeout(function(){
            if ($('a#'+name+'-link.filedownloadlink').length) {
                var filename = $('#form a.filedownloadlink[name="'+name+'"] .fu-fn').attr('vf');
                var fileext = getfileextension(filename.toLowerCase());
                var srcDownload = $('a#'+name+'-link.filedownloadlink').attr('href');
                var srcView = srcDownload.replace('DataEntry/file_download.php','DataEntry/image_view.php')
                                         .replace('DataEntry%2Ffile_download.php','DataEntry%2Fimage_view.php'); // Change to image_view.php
                let html = '<i class="fa-solid fa-eye-slash text-danger" title="' + window.lang.docs_1101 + '"></i><span class="visually-hidden" data-rc-lang="docs_1101">' + window.lang.docs_1101 + '</span>';
                if (in_array(fileext, valid_image_suffixes)) {
                    // image
                    html = "<img src='"+srcView+"' style='max-width:100%;' alt='"+filename+"'>";
                } else if (fileext == 'pdf') {
                    // pdf
                    // html = "<object data='"+srcView+"' type='application/pdf' style='width:100%;max-width:100%;height:300px;'><iframe src='"+srcView+"' style='width:100%;max-width:100%;height:300px;border:none;'></iframe></object>";
                    html = renderInlinePdfContainer(srcView);
                }
                let link = "<a target='_blank' href='"+srcDownload+"' style='text-decoration:underline;'>"+filename+"</a>";
                let missing = false;
                if (val == missing_data_replacement_js) {
                    html = link = missing_data_replacement_js;
                    missing = true;
                }
                $(piping_receiver_class_field_js+event_id+'-'+name+'-inline').each(function() {
                    // Need to add unique ids for PDF resizer support
                    if (missing) {
                        $(this).html(html);
                    }
                    else {
                        const $html = $(html);
                        $html.attr('data-file-id', getFileId(name));
                        $(this).html('');
                        $(this).append($html);
                    }
                });
                $(piping_receiver_class_field_js+event_id+'-'+name+'-link').html(link);
                if (isblank) {
                    $(piping_receiver_class_field_js+event_id+'-'+name+'-link.pipingrec-hideunderscore').html('');
                }
                if (fileext == 'pdf') addFileEnhancements();
            }
        },100);
        // Update drop-down options separately via ajax
        updatePipingDropdowns(name,val);
    });
}

// Check if any checkboxes in a group are checked
function anyChecked(formname, field) {
    var numChecked = 0;
    var domfld = document.forms[formname].elements[field];
    // If field doesn't exist, it must be a "descriptive" field
    try {
        var fldexists = (domfld != null);
    } catch(e) {
        try {
            var fldexists = (domfld.value != null);
        } catch(e) {
            var fldexists = false;
        }
    }
    if (!fldexists) return 0;
    var chkLen2 = domfld.length;
    if (chkLen2) {
        for (var x = 0; x < chkLen2; x++) {
            if (document.forms[formname].elements[field][x].checked) numChecked++;
        }
    } else {
        if (document.forms[formname].elements[field].checked) numChecked++;
    }
    return numChecked;
}

//Functions used in Branching Logic for hiding/showing fields
function checkAll(flag, formname, field) {
    var this_code;
    eval("var chkLen=document."+formname+"."+field+".length;");
    if (chkLen) {
        for (var x = 0; x < chkLen; x++) {
            if (flag == 1) {
                eval("document."+formname+"."+field+"[x].checked = true;");
            } else {
                eval("document."+formname+"."+field+"[x].checked = false;"
                    +"this_code = document."+formname+"."+field+"[x].getAttribute('code');");
                try {
                    eval("document." + formname + ".__chk__" + field.substring(8) + "_RC_" + replaceDotInCheckboxCoding(this_code) + ".value='';");
                } catch(e) {
                    document.getElementById("id-__chk__" + field.substring(8) + "_RC_" + replaceDotInCheckboxCoding(this_code)).value = '';
                }
            }
        }
    } else {
        if (flag == 1) {
            eval("document."+formname+"."+field+".checked = true;");
        } else {
            eval("document."+formname+"."+field+".checked = false;"
                +"this_code = document."+formname+"."+field+".getAttribute('code');");
            try {
                eval("document." + formname + ".__chk__" + field.substring(8) + "_RC_" + replaceDotInCheckboxCoding(this_code) + ".value='';");
            } catch(e) {
                document.getElementById("id-__chk__" + field.substring(8) + "_RC_" + replaceDotInCheckboxCoding(this_code)).value = '';
            }
        }
    }
}

// Return boolean for whether DOM element exists
function elementExists(domfld) {
    try {
        return (domfld != null);
    } catch(e) {
        return false;
    }
}

// Function called when a calculated field's value gets changed via JavaScript
function calcChangeCheck(newval, oldval, this_field, isOnPageLoad) {
    if (typeof isOnPageLoad == 'undefined') isOnPageLoad = false;
    // Set data change flag
    if (!isOnPageLoad && newval != oldval) setDataEntryFormValuesChanged(this_field);
    // Add visual marker after field
    $(function(){
        var domfld = $('form#form input[name="'+this_field+'"]');
        domfld.removeClass('calcChangedInitial');
        var savedval = domfld.attr('sv');
        if (savedval+"" !== newval+"") {
            var nspwa = isOnPageLoad && $('tr[sq_id="' + this_field + '"]').hasClass('\@SAVE-PROMPT-EXEMPT-WHEN-AUTOSET');
            domfld.addClass(nspwa ? 'calcChangedInitial' : 'calcChanged');
            // domfld.change();
        } else {
            domfld.removeClass('calcChanged');
        }
    });
}

function indexEmbeddedFields() {
    var index = {};
    var spans = document.querySelectorAll('.rc-field-embed[var]');
    for (var m = 0; m < spans.length; m++) {
        if (typeof spans[m].attributes.var == 'undefined') continue;
        var val = spans[m].attributes.var.value;
        if (typeof index[val] == 'undefined') index[val] = [];
        index[val].push(spans[m]);
    }
    return index;
}

// Create a lookup table that connects field names to the elements that embed those fields 
var embeddedFieldsByVar = indexEmbeddedFields();

// Evaluate branching logic and show/hide table row based upon its evaluation
var pageHasEmbeddedFields = (Object.keys(embeddedFieldsByVar).length > 0);
function evalLogic(this_field, byPassEraseFieldPrompt, logic, parentField) {
    if (typeof parentField == 'undefined') parentField = false;
    var showHideAction = null;
    if (logic == false) {
        // HIDE ROW
        var is_chkbx = 0;
        var fldLen = 0;
        eval("var domfld = document.forms['form']."+this_field+";");
        var fldexists = (domfld != null);
        if (fldexists && domfld.value != null) fldLen = domfld.value.length;
        if (!fldexists) {
            // Checkbox fields (might also be a "descriptive" field)
            var fldLen = anyChecked("form","__chkn__"+this_field);
            is_chkbx = 1;
        }
        var hasMissingDataCode = false;
        // Hide icon for this field if displayed because it has a value but is not hidden by branching logic
        if (overrideEraseValuePrompt && !parentField && document.getElementById('icon-showfield-'+this_field) != null) {
            document.getElementById('icon-showfield-'+this_field).remove();
        }
        // HIDE ROW
        if (fldLen > 0) {
            // Determine if we should erase the value or prompt to erase the value
            if (showEraseValuePrompt && !overrideEraseValuePrompt) {
                // If using randomization, make sure we're not going to erase the randomization field or stata field values
                if (randomizationCriteriaFieldList != null && in_array(this_field, randomizationCriteriaFieldList)) {
                    // Randomization fields CANNOT be hidden after randomization has happened, so stop here.
                    return false;
                }
                // Determine if we should prompt the user and erase the value
                hasMissingDataCode = (missing_data_codes_check && typeof domfld != 'undefined' && domfld.value != '' && in_array(domfld.value, missing_data_codes));
                var eraseIt = (page == 'surveys/index.php') ? true : (byPassEraseFieldPrompt ? false : (hasMissingDataCode ? true : confirm(brErase(this_field))));
            } else {
                var eraseIt = false;
                // If a field would normally be hidden by branching logic but isn't because it has a value and because the project-level feature
                // "Prevent branching logic from hiding fields that have values" is enabled,
                // then add a visual indicator that the field would normally be hidden.
                if (overrideEraseValuePrompt && !parentField && page != 'surveys/index.php' && document.getElementById('icon-showfield-'+this_field) == null) {
                    const tr = document.getElementById(this_field+'-tr');
                    tr.style.position = 'relative';
                    // Add a FontAwesome icon on the field's table cell
                    const icon = document.createElement('i');
                    icon.setAttribute('id', 'icon-showfield-'+this_field);
                    icon.setAttribute('class', 'fas fa-eye position-absolute opacity35');
                    icon.setAttribute('style', 'cursor:pointer;cursor:hand;top:5px;right:5px;');
                    icon.setAttribute('title', lang.data_entry_601);
                    icon.setAttribute('data-rc-lang-attrs', 'title=data_entry_601');
                    tr.appendChild(icon);
                }
            }
            if (eraseIt) {
                showHideAction = 'hide';
                if (!hasMissingDataCode) {
                    if (is_chkbx) {
                        // Checkbox fields
                        checkAll(0, "form", "__chkn__" + this_field);
                    } else {
                        // If a radio field, additionally make sure the radio buttons are all unchecked
                        if (document.forms['form'].elements[this_field + '___radio'] != null) {
                            domfld.value = '';
                            uncheckRadioGroup(document.forms['form'].elements[this_field + '___radio']);
                        }
                        // If a select field with auto-complete enabled, then reset the text field value too
                        if (document.getElementById('rc-ac-input_' + this_field) != null) {
                            domfld.value = '';
                            document.getElementById('rc-ac-input_' + this_field).value = '';
                        }
                        // If a slider field, then reset the slider
                        else if (document.forms['form'].elements[this_field] != null && document.forms['form'].elements[this_field].getAttribute('class') != null
                            && document.forms['form'].elements[this_field].getAttribute('class').indexOf('sldrnum') > -1) {
                            resetSlider(this_field, true);
                        }
                        // If a file upload field, then reset the field
                        else if (elementExists(document.getElementById('fileupload-container-'+this_field)))  {
                            $(function(){
                                $('#fileupload-container-'+this_field).removeClass('hidden');
                                // If deleting a file/signature, then give delete prompt so that the user understands they are deleting a file
                                $('#fileupload-container-'+this_field+' .deletedoc-lnk').trigger('click');
                            });
                        } else {
                            // Reset field value
                            domfld.value = '';
                        }
                    }
                    // Trigger piping now that the value has been erased
                    if (is_chkbx) {
                        updatePipingCheckboxes(':input[name="__chkn__' + this_field + '"]');
                    } else if (document.forms['form'].elements[this_field + '___radio'] != null) {
                        radioResetVal(this_field,'form');
                    } else if (typeof document.forms['form'].elements[this_field] != 'undefined' && document.forms['form'].elements[this_field].tagName.toLowerCase() == 'input'
                        && document.forms['form'].elements[this_field].getAttributeNode("type").value.toLowerCase() == 'text') {
                        $(function(){ $(':input[name="'+this_field + '"]').trigger('blur'); });
                    } else {
                        $(function(){ $(':input[name="'+this_field + '"]').trigger('change'); });
                    }
                }
                document.getElementById(this_field+'-tr').style.display='none';
                // Remove "hasval" attribute from row
                removeHasVal(this_field);
            } else if (!eraseIt && parentField != false) {
                evalLogic(parentField, byPassEraseFieldPrompt, true);
            }
        } else {
            showHideAction = 'hide';
            if (elementExists(document.getElementById(this_field+'-tr'))) {
                document.getElementById(this_field+'-tr').style.display='none';
            }
        }
        // If any fields are embedded inside this field that is being hidden, then also check if their value needs to be cleared out too
        if (pageHasEmbeddedFields && elementExists(document.getElementById(this_field+'-tr'))) {
            var a = document.querySelector('#' + this_field + '-tr').querySelectorAll('.rc-field-embed');
            for (var i = 0; i < a.length; i++) {
                if ($(a[i]).parents('.piping_receiver').length > 0) continue; // skip when inside a piping receiver
                evalLogic(a[i].getAttribute('var'), byPassEraseFieldPrompt, false, this_field);
            }
        }
    } else {
        // SHOW ROW
        var showit = true;
        showHideAction = 'show';
        if (page == 'surveys/index.php') {
            // Survey page: Treat differently since it contains fields on the form that might need to remain hidden (because of multi-paging)
            if (document.getElementById(this_field+'-tr').getAttribute('class') != null) {
                if (document.getElementById(this_field+'-tr').getAttribute('class').indexOf('hidden') > -1) {
                    // If row has class 'hidden', then keep hidden
                    showit = false;
                }
            }
        }
        // Do not show it if it has any @HIDDEN action tag
        if (showit && document.getElementById(this_field+'-tr').getAttribute('class') != null) {
            var rowClasses = document.getElementById(this_field+'-tr').getAttribute('class').split(" ");
            if (in_array('@HIDDEN', rowClasses)
                || (page != 'surveys/index.php' && in_array('@HIDDEN-FORM', rowClasses))
                || (page == 'surveys/index.php' && in_array('@HIDDEN-SURVEY', rowClasses))
            ) {
                showit = false;
            }
        }
        if (showit) {
            // Now show the row, if applicable
            document.getElementById(this_field+'-tr').style.display = 'table-row';
            // If any fields are embedded inside this field that is being shown, then by default make all embedded fields visible (their individual branching will be checked below, if applicable)
            if (pageHasEmbeddedFields) {
                var a = document.querySelector('#' + this_field + '-tr').querySelectorAll('.rc-field-embed');
                if (a.length > 0) {
                    for (var i = 0; i < a.length; i++) {
                        a[i].style.display = 'inline';
                    }
                    // Re-run doBranching() for ALL after processing all fields here because there might be cases where some embedded fields with branching are not getting evaluated
                    runAllBranchingAgain = true;
                }
            }
        }
    }
    // If field is embedded inside another field, then show/hide the embedded field based on their branching logic
    if (pageHasEmbeddedFields) {
        var a = embeddedFieldsByVar[this_field];
        if (a) {
            for (var i = 0; i < a.length; i++) {
                a[i].style.display = (showHideAction == 'hide') ? 'none' : 'inline';
            }
        }
    }
}

// Remove "hasval" attribute from table row
function removeHasVal(this_field)
{
    if (document.getElementById(this_field+'-tr').getAttribute("hasval") != null) {
        document.getElementById(this_field+'-tr').removeAttribute("hasval");
    }
}

// Action Tags: Function that is run on forms and surveys to perform actions based on tags in the Field Annotation text
function triggerActionTags() {
    // Is this a survey page?
    var isSurvey = (page == 'surveys/index.php');

    // Note: @HIDDEN tags are handled via CSS and also inside doBranching()
    // on forms/surveys, so we don't need to force them to be hidden here.

    // DISABLES ANY FIELD THAT CONTAINS @READONLY
    // Disable survey and form
    $("#questiontable tr.\\@READONLY").disableRowActionTag();
    $("#questiontable tr.\\@READONLY.row-field-embedded").each(function(){
        $('.rc-field-embed[var="'+$(this).attr('sq_id')+'"]').disableRowActionTag();
    });
    // Disable survey only
    if (isSurvey) {
        $("#questiontable tr[sq_id].\\@READONLY-SURVEY").disableRowActionTag();
        $("#questiontable tr.\\@READONLY-SURVEY.row-field-embedded").each(function(){
            $('.rc-field-embed[var="'+$(this).attr('sq_id')+'"]').disableRowActionTag();
        });
    }
    // Disable form only
    else {
        $("#questiontable tr[sq_id].\\@READONLY-FORM").disableRowActionTag();
        $("#questiontable tr.\\@READONLY-FORM.row-field-embedded").each(function(){
            $('.rc-field-embed[var="'+$(this).attr('sq_id')+'"]').disableRowActionTag();
        });
    }
}

// Disable row via @READONLY action tag
(function ( $ ) {
    $.fn.disableRowActionTag = function() {
        var tr = this;
        if (tr.length < 1) return;
        // Disable buttons and all text links (ignore images surrounded by links, we just want text links)
        $('a:not(a:has(img))', tr).each(function(){
            $(this).attr('onfocus', '');
            if ($(this).hasClass('fileuploadlink')) {
                $(this).attr('href', 'javascript:;').attr('onclick', 'return false;');
            }
        });
        // Disable sliders
        $("[id^=sldrmsg-]", tr).css('visibility','hidden');
        $("[id^=slider-]", tr).attr('onmousedown', '').slider("disable");
        // Perform the rest multiple times using a delay in case of race conditions of other JS on page
        var loops = [100, 1000, 2000];
        for (var i = 0; i < loops.length; i++) {
            setTimeout(function () {
                // Disable or hide certain buttons/icons
                $('button, .ui-datepicker-trigger', tr).hide();
                $("[id^=slider-]", tr).slider("disable");
                $('.tox-toolbar button', tr).prop("disabled", true);
                // Disable all inputs row, trigger blur (to update any piping), and gray out whole row.
                // This needs to be delayed slightly in case a URL-prefilled survey is prefilling a Secondary Unique Field, which will become editable again.
                $('input, select, textarea, .tox-toolbar button', tr).prop("disabled", true);
            }, loops[i]);
        }
    };
}( jQuery ));

// Improve onchance event of text/notes fields to trigger branching logic before leaving the field
// (so that we don't skip over the next field, which becomes displayed)
var currentFocusTextField = null;
var bypassBranchingErrors = false;
function improveBranchingInitIntervalCheck() {
    // If field not set, turn on checker
    if (currentFocusTextField === null) {
        improveBranchingDisableIntervalCheck();
        return;
    }
    $.doTimeout('improveBranchingCheck', 2000, function(){
        // If this is unset by the end of the delay, then stop it
        if (currentFocusTextField === null) {
            improveBranchingDisableIntervalCheck();
            return;
        }
        // Obtain the initial value of field during onfocus
        var initValue = currentFocusTextField.data('val');
        if (initValue != currentFocusTextField.val()) {
            // Set new initial value
            currentFocusTextField.data('val', currentFocusTextField.val());
            // Do branching
            bypassBranchingErrors = true;
            doBranching(currentFocusTextField.attr('name'),true);
            bypassBranchingErrors = false;
        }
        // Wait another interval and re-run
        improveBranchingInitIntervalCheck();
    });
}
function improveBranchingDisableIntervalCheck() {
    currentFocusTextField = null;
    bypassBranchingErrors = false;
    try {
        $.doTimeout('improveBranchingCheck', false);
    } catch(e) { }
}
function improveBranchingOnchange() {
    $('form#form input[type="text"], form#form textarea').focus(function(){
        // Only set this variable for text/notes fields that will trigger branching
        currentFocusTextField = null;
        var attr = $(this).attr('onchange');
        if (typeof attr !== typeof undefined && attr !== false && attr.indexOf('doBranching(') > -1) {
            currentFocusTextField = $(this);
            // Set original value at the time of focus
            $(this).data('val', currentFocusTextField.val());
        }
        // Initiate checking value at an interval
        improveBranchingInitIntervalCheck();
    });
    $('form#form input[type="text"], form#form textarea').blur(function(){
        improveBranchingDisableIntervalCheck();
    });
}

// Run when clicking a checkbox on a survey/form
function checkboxClick(field, value, that, e, maxchecked) {
    var elementClicked = '';
    var isCheckboxClicked = false;
    var targetName = $(e.target).attr('name');
    try {
        elementClicked = e.target.nodeName.toLowerCase();
        isCheckboxClicked = (elementClicked == 'input' && $(e.target).attr('type') == 'checkbox');
    } catch(error) { }
    // Deal with other fields embedded inside the checkbox labels
    if (typeof targetName != 'undefined' && field != '__chkn__'+targetName && targetName.indexOf('___radio') < 0) {
        // If the field being clicked is not the checkbox field, then we have an embedded field being clicked in the parent checkbox's label
        e.stopPropagation();
        // if ($('#'+field+'-tr .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+value+'"] .rc-field-embed').length) {
        //     console.log('field embedded inside an enhanced choice');
        // }
    } else if (typeof targetName == 'undefined' && $('.rc-field-embed[var="'+field+'"]').length && elementClicked == 'label' && $(that).hasClass('mc')) {
        // Use the label ID to determine this embedded checkbox's ID, and then check/uncheck the associated checkbox (since it will not automatically get checked/unchecked simply by clicking its label here)
        var labelIdParts = $(that).prop('id').split('-');
        var embeddedCheckboxId = '#id-__chk__'+labelIdParts[1]+'_RC_'+labelIdParts[2];
        var isEmbeddedCheckboxClicked = $(embeddedCheckboxId).prop('checked');
        // If checkbox is disabled, then do not check it
        if ($(embeddedCheckboxId).prop('disabled')) {
            e.stopPropagation();
            return;
        }
        // Make sure the checkbox stays checked
        $(embeddedCheckboxId).prop('checked', !isEmbeddedCheckboxClicked);
        // If this is a @NONEOFTHEABOVE checkbox option, then skip stopPropagation() because it prevents the checkbox from being checked
        var fieldHasNoneOfTheAboveOption = in_array(labelIdParts[1], noneOfTheAboveFields);
        var clickedChoiceIsNoneOfTheAboveOption = in_array(labelIdParts[1]+':'+labelIdParts[2], noneOfTheAboveFieldsChoices);
        if (!clickedChoiceIsNoneOfTheAboveOption || !fieldHasNoneOfTheAboveOption) {
            e.stopPropagation();
        }
        if (fieldHasNoneOfTheAboveOption && !clickedChoiceIsNoneOfTheAboveOption) {
            // If this choice is not the @NONEOFTHEABOVE choice, click the checkbox option to manually trigger the @NONEOFTHEABOVE prompt (do it twice because of weird doubling issue)
            $(embeddedCheckboxId).trigger('click').trigger('click');
        } else if (clickedChoiceIsNoneOfTheAboveOption && !($(embeddedCheckboxId).prop('checked') && $('input[type="checkbox"][name="__chkn__'+field+'"]:checked').length > 1)) {
            // If this choice IS the @NONEOFTHEABOVE choice AND it is being unchecked, click the checkbox option to manually but ONLY if we need to trigger the @NONEOFTHEABOVE prompt (can cause double dialog if triggered generally)
            $(embeddedCheckboxId).trigger('click');
        }
    } else if (elementClicked == 'button' && $(elementClicked).hasClass('today-now-btn')) {
        // If clicking the Today/Now button for a date/time field embedded in a checkbox
        e.stopPropagation();
    } else if (elementClicked == 'a' || (elementClicked == 'span' && typeof targetName == 'undefined')) {
        // If clicking on a link inside the checkbox label
        e.stopPropagation();
    }
    try {
        if (isCheckboxClicked) {
            var checkbox = $(that);
        } else if (elementClicked == 'label') {
            var checkbox = $(that).parent().find('input[type="checkbox"]:first');
        } else {
            return;
        }
        var wasJustChecked = checkbox.prop('checked');
        if (wasJustChecked && maxchecked > 0 && $('input[type="checkbox"][name="__chkn__'+field+'"]:checked').length > maxchecked) {
            // Uncheck it
            checkbox.prop('checked',false);
            // Set value of hidden field to blank
            $('#form input[name="__chk__'+field+'_RC_'+replaceDotInCheckboxCoding(value)+'"]').val('');
            // Show temporary note about it being unchecked
            setPositionMaxcheckedActionTagAlert(checkbox);
            return;
        }
        // Set value of hidden field
        $('#form input[name="__chk__'+field+'_RC_'+replaceDotInCheckboxCoding(value)+'"]').val( wasJustChecked ? value : '' );
        calculate(field);
        doBranching(field);
    } catch(e) { }
}

function setPositionMaxcheckedActionTagAlert(that) {
    var enhanced_choice_fudge = that.hasClass('enhancedchoice') ? '250' : '100';
    var maxchoice_label = $('#maxchecked_tag_label');
    maxchoice_label.show().position({
        my:        "left top",
        at:        "left+"+enhanced_choice_fudge+" top",
        of:        that
    });
    setTimeout(function(){
        maxchoice_label.hide();
    },4000);
}

// Implement @NONEOFTHEABOVE action tag for checkboxes
var noneOfTheAboveFields = new Array();
var noneOfTheAboveFieldsChoices = new Array();
function noneOfTheAboveAlert(field, choicesCsv, regchoicesCsv, langOkay, langCancel) {
    // Add field to array
    noneOfTheAboveFields[noneOfTheAboveFields.length] = field;
    // Initialize values
    var noneOfTheAboveCurrentField = '';
    var noneOfTheAboveNotCurrentField = '';
    var choices = choicesCsv.split(",");
    var regchoices = regchoicesCsv.split(",");
    for (var i=0; i<choices.length; i++) {
        if (noneOfTheAboveCurrentField != '') noneOfTheAboveCurrentField += ', ';
        noneOfTheAboveCurrentField += 'input[name="__chkn__'+field+'"][code="'+choices[i]+'"]';
        // Add field to array
        noneOfTheAboveFieldsChoices[noneOfTheAboveFieldsChoices.length] = field+':'+choices[i];
    }
    for (var i=0; i<regchoices.length; i++) {
        if (noneOfTheAboveNotCurrentField != '') noneOfTheAboveNotCurrentField += ', ';
        noneOfTheAboveNotCurrentField += 'input[name="__chkn__'+field+'"][code="'+regchoices[i]+'"]';
    }
    // Click trigger for ALL options except the NONEOFTHEABOVE option
    $(noneOfTheAboveNotCurrentField).click(function(){
        // Deselect the NONEOFTHEABOVE option(s)
        $(noneOfTheAboveCurrentField).each(function(){
            if ($(this).prop('checked')) {
                var thisCode = $(this).attr('code');
                $(this).prop('checked', false);
                $(this).parent().find('input[type="hidden"]').val('');
                if ($("#questiontable .rc-field-embed[var='"+field+"']").length) {
                    $('#questiontable .rc-field-embed[var="'+field+'"] .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                } else {
                    $('#'+field+'-tr .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                }
                setDataEntryFormValuesChanged(field);
                try{ updatePipingCheckboxes(this); }catch(e){ }
            }
        });
    });
    // Click trigger for NONEOFTHEABOVE option
    $(noneOfTheAboveCurrentField).click(function(){
        var regChoicesChecked = 0;
        var thisCode = $(this).attr('code');
        let getInnerHtmlTextExcludeScreenReaderOnlyText = function (element) {
            return $(element)
                .contents()
                .map(function () {
                    if (this.nodeType === Node.TEXT_NODE) {
                        return this.nodeValue;
                    } else if (this.nodeType === Node.ELEMENT_NODE && !$(this).hasClass('sr-only')) {
                        return getInnerHtmlTextExcludeScreenReaderOnlyText(this);
                    }
                    return '';
                }).get().join('');
        };

        // If no other choices are selected, then do nothing
        if ($('input[name="__chkn__'+field+'"]:not([code="'+thisCode+'"]):checked').length == 0) {
            return;
        }
        // Place the choice text inside the dialog
        if ($('#label-'+field+'-'+thisCode).length) {
            $('#noneOfTheAboveLabelDialog').html(getInnerHtmlTextExcludeScreenReaderOnlyText($('#label-' + field + '-' + thisCode)));
        } else {
            $('#noneOfTheAboveLabelDialog').html(getInnerHtmlTextExcludeScreenReaderOnlyText($('#matrixheader-'+$('#'+field+'-tr').attr('mtxgrp')+'-'+thisCode)));
        }
        // Make sure it was checked (in case using @MAXCHECKED action tag)
        $('input[name="__chkn__'+field+'"][code="'+thisCode+'"]').prop('checked',true);
        $('#maxchecked_tag_label').hide();
        // Dialog
        $('#noneOfTheAboveDialog').dialog({ bgiframe: true, modal: true, width: 450,
            title: window.lang.data_entry_412,
            close: function(){
                // If close dialog, uncheck the checkbox and set the hidden input as blank
                var thisOb = $('input[name="__chkn__'+field+'"][code="'+thisCode+'"]');
                thisOb.prop('checked', false);
                thisOb.parent().find('input[type="hidden"]').val('');
                if ($("#questiontable .rc-field-embed[var='"+field+"']").length) {
                    $('#questiontable .rc-field-embed[var="'+field+'"] .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                } else {
                    $('#'+field+'-tr .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                }
                try{ updatePipingCheckboxes(thisOb); }catch(e){ }
            },
            buttons: [
                { text: window.lang.global_53, click: function() {
                        $(this).dialog('close');
                    }},
                {text: window.lang.data_entry_417, click: function() {
                        // Okay button: Uncheck all other checkbox options and set their hidden input as blank
                        var thisOb = $('input[name="__chkn__'+field+'"]:not([code="'+thisCode+'"]):checked');
                        thisOb.each(function(){
                            var thisCode2 = $(this).attr('code');
                            $(this).prop('checked', false);
                            $(this).parent().find('input[type="hidden"]').val('');
                            if ($("#questiontable .rc-field-embed[var='"+field+"']").length) {
                                $('#questiontable .rc-field-embed[var="'+field+'"] .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode2+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                            } else {
                                $('#'+field+'-tr .enhancedchoice label.selectedchkbox[comps="'+field+',code,'+thisCode2+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                            }
                        });
                        try{ updatePipingCheckboxes(thisOb); }catch(e){ }
                        setDataEntryFormValuesChanged(field);
                        calculate(field);
                        doBranching(field);
                        $(this).dialog('destroy');
                    }}
            ] });
    });
}

// Implement the @WORDLIMIT and @CHARLIMIT action tags
function wordcharlimit(field, type, goal, msg) {
    var ob = $('input[name="'+field+'"], textarea[name="'+field+'"]');
    ob.after('<div id="wordcharcounter-'+type+'-'+field+'" class="wordcharcounter"></div>');
    ob.counter({ type: type, count: 'down', msg: msg, goal: goal, target: '#wordcharcounter-'+type+'-'+field });
    // For unknown reasons, sometime multiple divs with id=undefined_counter will appear for the first instance of the action tag on the page. Remove all but the last to compensate.
    $('div#undefined_counter:not(:last-child)').remove();
}

// In case data entry forms don't fully load, which would prevent the save button group drop-downs
// from working, use this replacement method to make sure the drop-down opens regardless.
function openSaveBtnDropDown(ob,e) {
    e.stopPropagation();
    var btngroup = $(ob).parent();
    if (btngroup.hasClass('show')) {
        // Close it
        btngroup.removeClass('show');
        btngroup.find('.dropdown-menu').removeClass('show');
    } else {
        // Open it
        btngroup.addClass('show');
        btngroup.find('.dropdown-menu').addClass('show');
    }
}

// If any enhanced choice fields are hidden due to branching logic, then make sure their UI shows them as unselected
function updateHiddenEnhancedChoices() {
    $('label.selectedradio:not(:visible)').removeClass('selectedradio');
    $('label.selectedchkbox:not(:visible)').removeClass('selectedchkbox').addClass('unselectedchkbox');
}

// Action when selecting an Enhanced Choice radio or checkbox
var justClickedEnhancedChoice = false;
function enhanceChoiceSelect(ob, e, maxchecked) {
    // Ignore if clicked on a text field or notes field that is embedded inside the enhanced choice
    nodeName = '';
    try {
        var nodeName = e.target.nodeName;
    } catch(error) {
        return;
    }
    var elementClicked = nodeName.toLowerCase();
    if (elementClicked == 'input' || elementClicked == 'textarea') return;
    // Set flag to deal with choices embedded in other choices
    if (justClickedEnhancedChoice) return;
    justClickedEnhancedChoice = true;
    setTimeout("justClickedEnhancedChoice = false;", 50);
    // Get attributes
    var label = $(ob);
    var attr = label.attr('comps').split(',');
    var type = attr[1] == 'code' ? 'checkbox' : 'radio';
    // Set the element class
    if (type == 'checkbox') {
        var input = $('input[name="__chkn__'+attr[0]+'"]['+attr[1]+'="'+attr[2]+'"]');
        if (!input.length) {
            // PROMIS inputs
            input = $('input[name="'+attr[0]+'"]');
        }
        if (input.prop('checked')) {
            label.removeClass('selectedchkbox').addClass('unselectedchkbox');
        } else {
            // Deal with maxchecked action tag
            if (isNumeric(maxchecked) && maxchecked > 0 && $('input[name="__chkn__'+attr[0]+'"]:checked').length >= maxchecked) {
                // Since we hit the max, stop here to prevent it from being checked
                setPositionMaxcheckedActionTagAlert(label.parent());
                return;
            }
            label.removeClass('unselectedchkbox').addClass('selectedchkbox');
        }
    } else {
        var input = $('input[name="'+attr[0]+'___radio"]['+attr[1]+'="'+attr[2]+'"]');
        if (!input.length) {
            // PROMIS inputs
            input = $('input[name="'+attr[0]+'"]['+attr[1]+'="'+attr[2]+'"]');
        }
        // First, set all unchecked
        label.parentsUntil('div.enhancedchoice_wrapper').parent().find('div.enhancedchoice label').removeClass('selectedradio');
        // Now set the one selected one
        label.addClass('selectedradio');
    }
    // Trigger the original input
    //input.trigger('click');
    setDataEntryFormValuesChanged(attr[0]);
}

// On forms/surveys, make sure dropdowns don't get too wide so that they create horizontal scrollbar
function shrinkWideDropDowns() {
    // Get width of viewport
    var winWidth = $(window).width();
    // If we don't have a horizontal scrollbar, then do nothing
    if ($(document).width() <= winWidth) return;
    // Loop through each drop-down
    $('form#form select.x-form-text').each(function(){
        var dd = $(this);
        var posDdLeft = dd.offset().left
        var posDdRight = posDdLeft + dd.width();
        // If drop-down spills off page, then resize it
        if (posDdRight > winWidth) dd.css('width','95%');
    });
}

// Enable all action tags
function enableActionTags() {
    // If we're viewing a response on a form but not in edit mode, then stop here
    if ($('#edit-response-btn').length && $('#SurveyActionDropDown').length && getParameterByName('editresp') != '1') return;
    // If they can't save the page, do not run auto-fill the fields with values
    if (!$('button[name="submit-btn-saverecord"]').length) return;
    // Track any changes made
    var changes = {};
    // Enable NOW/TODAY action tags
    $("#questiontable tr.\\@NOW, #questiontable tr.\\@TODAY, #questiontable tr.\\@NOW-SERVER, #questiontable tr.\\@TODAY-SERVER, #questiontable tr.\\@NOW-UTC, #questiontable tr.\\@TODAY-UTC").each(function(){
        var name = $(this).attr('sq_id');
        var input = $('#questiontable input[name="'+name+'"]');
        var fv = (input.attr('fv') == null) ? '' : input.attr('fv');
        var dateFVs = new Array('date_mdy', 'date_dmy', 'date_ymd');
        var nspwa = $(this).hasClass("\@SAVE-PROMPT-EXEMPT-WHEN-AUTOSET");
        // Add value if doesn't already have a value
        if (input.val() == '') {
            if (fv == 'time') {
                // NOW for time fields
                if ($(this).hasClass("\@NOW-SERVER")) {
                    document.forms['form'].elements[name].value = mid(now,12,5);
                } else {
                    document.forms['form'].elements[name].value = currentTime('both', false, $(this).hasClass("\@NOW-UTC"));
                }
            } else if (fv == 'time_hh_mm_ss') {
                // NOW for time fields
                if ($(this).hasClass("\@NOW-SERVER")) {
                    document.forms['form'].elements[name].value = right(now,8);
                } else {
                    document.forms['form'].elements[name].value = currentTime('both', true, $(this).hasClass("\@NOW-UTC"));
                }
            } else if (fv == 'time_mm_ss') {
                // NOW for time fields
                if ($(this).hasClass("\@NOW-SERVER")) {
                    document.forms['form'].elements[name].value = right(now,5);
                } else {
                    document.forms['form'].elements[name].value = right(currentTime('both', true, $(this).hasClass("\@NOW-UTC")), 5);
                }
            } else if ($(this).hasClass("\@NOW-SERVER")) {
                // NOW-SERVER for datetime fields (if detect a date field, then fall back to inserting date instead of datetime)
                var showSeconds = (fv == '' || fv.indexOf('datetime_seconds') === 0);
                if (fv.indexOf('_dmy') > -1) {
                    var thisNow = now_dmy;
                } else if (fv.indexOf('_mdy') > -1) {
                    var thisNow = now_mdy;
                } else {
                    var thisNow = now;
                }
                if (in_array(fv, dateFVs)) thisNow = thisNow.substring(0,10);
                if (!showSeconds)  thisNow = thisNow.substring(0,16);
                document.forms['form'].elements[name].value = thisNow;
            } else if ($(this).hasClass("\@NOW-UTC")) {
                // NOW for datetime fields (if detect a date field, then fall back to inserting date instead of datetime)
                var thisNow = getCurrentDate(fv, true)+' '+currentTime('both',(fv == '' || fv.indexOf('datetime_seconds') === 0), true);
                if (in_array(fv, dateFVs)) thisNow = thisNow.substring(0,10);
                document.forms['form'].elements[name].value = thisNow;
            } else if ($(this).hasClass("\@NOW")) {
                // NOW for datetime fields (if detect a date field, then fall back to inserting date instead of datetime)
                var thisNow = getCurrentDate(fv)+' '+currentTime('both',(fv == '' || fv.indexOf('datetime_seconds') === 0));
                if (in_array(fv, dateFVs)) thisNow = thisNow.substring(0,10);
                document.forms['form'].elements[name].value = thisNow;
            } else if ($(this).hasClass("\@TODAY-SERVER")) {
                // TODAY-SERVER for date fields
                if (fv.indexOf('_dmy') > -1) {
                    var thisToday = today_dmy;
                } else if (fv.indexOf('_mdy') > -1) {
                    var thisToday = today_mdy;
                } else {
                    var thisToday = today;
                }
                document.forms['form'].elements[name].value = thisToday;
            } else if ($(this).hasClass("\@TODAY-UTC")) {
                // TODAY-UTC for date fields
                document.forms['form'].elements[name].value = getCurrentDate(fv, true);
            } else if ($(this).hasClass("\@TODAY")) {
                // TODAY for date fields
                document.forms['form'].elements[name].value = getCurrentDate(fv);
            }
            input.addClass(nspwa ? 'calcChangedInitial' : 'calcChanged');
            // Increment changes count
            changes[name] = true;
        }
    });
    // Enable LATITUTE/LONGITUDE action tags
    var changesGPS = 0; // Not used!?
    $("#questiontable tr.\\@LATITUDE, #questiontable tr.\\@LONGITUDE").each(function(){
        var name = $(this).attr('sq_id');
        // Disable field
        $('#questiontable input[name="'+name+'"]').prop('readonly',true);
        // Add GPS value
        if (document.forms['form'].elements[name].value == '') {
            if (getGeolocation(($(this).hasClass("\@LATITUDE") ? 'latitude' : 'longitude'), name, 'form') > 0) {
                changes[name] = true;
            }
        }
    });
    // Trigger branching and calculations if changes were made
    Object.keys(changes).forEach(function(name) { 
        if (!$('#questiontable tr[sq_id="'+name+'"]').hasClass('\@SAVE-PROMPT-EXEMPT-WHEN-AUTOSET')) {
            setDataEntryFormValuesChanged(name);
        }
        // Note: This may have never worked as intended before, because name was not in scope
        setTimeout(function(){try{calculate(name, true);doBranching(name);}catch(e){}},50);
    });
}

// Obtain the latitute or longitude of the user (direction = 'latitude' or 'longitude')
// and place the value inside an input field with specified 'input_name'.
// Note: It will *only* add the lat/long if the input is blank/empty.
function getGeolocation(direction,input_name,form_name,overwrite) {
    if (direction == null || input_name == null || direction == '' || input_name == '') return 0;
    if (overwrite == null) overwrite = false;
    // Get the position
    if (geoPosition.init()){
        geoPosition.getCurrentPosition(function(p){
            // Set form and input
            if (form_name == null) form_name = 'form';
            var myinput = document.forms[form_name].elements[input_name];
            // Make sure this is a textarea or input
            if (myinput.type != 'text') return;
            // Add lat or long to input
            if (overwrite == true || myinput.value == '') {
                if (direction == 'latitude') {
                    myinput.value = p.coords.latitude;
                } else if (direction == 'longitude') {
                    myinput.value = p.coords.longitude;
                }
                // Call calculations/branching
                try{calculate(input_name);doBranching(input_name);}catch(e){}
                $('#questiontable input[name="'+input_name+'"]').addClass('calcChanged');
            }
        },function(){ },{enableHighAccuracy:true});
        return 1;
    }
    return 0;
}

// AUTO-COMPLETE FOR DROP-DOWNS: Loop through drop-down fields on the form/survey and enable auto-complete for them
function enableDropdownAutocomplete(addEvents) {
    if (typeof addEvents == 'undefined') addEvents = true;
    // Class to add to select box once auto-complete has been enabled
    var selectClass = "rc-autocomplete-enabled";
    // Loop through all SELECT fields
    $('select.rc-autocomplete:not(.'+selectClass+')').each(function(){
        // If missing name attribute, then ignore
        if ($(this).attr('name') == null) return;
        // Elements
        if ($('.rc-field-embed[var="'+$(this).attr('name')+'"]').length) {
            // Field is embedded, so make the field embedding span the container here (instead of the main table row)
            var $tr = $('.rc-field-embed[var="'+$(this).attr('name')+'"]');
        } else {
            // Not embedded
            var $tr = $(this).parents('tr:first');
        }
        var $dropdown = $('select:first', $tr);
        var $input = $('input.rc-autocomplete:first', $tr);
        var $button = $('button.rc-autocomplete:first', $tr);
        // Add class to denote that drop-down already has auto-complete enabled
        $dropdown.addClass(selectClass);
        var ddwidth = $dropdown.width();
        // Make input width same as original drop-down
        if ($tr.css('display') != 'none' && ddwidth > 10) {
            $input.width( ddwidth );
        } else {
            // Drop-down is hidden by branching logic, so clone it to get its width
            var ddclone = $dropdown.clone();
            ddclone.css("visibility","hidden").appendTo('body');
            $input.width( ddclone.width() );
            ddclone.remove();
        }
        // Add event bindings
        if (addEvents) {
            // If put focus/click on blank input, open the full list
            $input.bind('focus click', function () {
                if ($(this).val() == '') {
                    $input.autocomplete('search', '');
                }
            });
            // Prevent form submission via Enter key in input
            $input.keydown(function (e) {
                if (e.which == 13) return false;
            });
            // When user changes autocomplete input to blank value
            $input.blur(function () {
                var object_clicked_local = object_clicked;
                var thisval = $(this).val();
                var ddval = $dropdown.val();
                if (thisval == '') {
                    if (ddval != '') {
                        $dropdown.val('').trigger('change');
                    }
                } else {
                    var isValid = false;
                    var valueToSelect = '';
                    $('option', $dropdown).each(function () {
                        if ($(this).text() == thisval) {
                            isValid = true;
                            valueToSelect = $(this).val();
                            return false;
                        }
                    });
                    // Check if the new value is valid
                    if (!isValid &&
                        // object_clicked_local will be null if we just blurred out of input (as opposed to clicking)
                        (object_clicked_local == null
                            // If we just clicked on the autocomplete list (to choose an option), then don't throw an error.
                            || (object_clicked_local != null && object_clicked_local.parents('ul.ui-autocomplete').length == 0))) {
                        // Not a valid value
                        simpleDialog(lang.survey_681, lang.global_287, null, null, "$('#" + $(this).attr('id') + "').focus().autocomplete('search',$('#" + $(this).attr('id') + "').val());");
                    } else {
                        // Set drop-down to same value and trigger change
                        $dropdown.val(valueToSelect);
                        if (ddval != valueToSelect) {
                            $dropdown.trigger('change');
                        }
                    }
                }
            });
            // Open full list when click button/arrow icon
            $button.mousedown(function (event) {
                $(this).attr('listopen', ($('.ui-autocomplete:visible').length ? '1' : '0'));
            });
            $button.click(function (event) {
                var targetTag = event.target.nodeName.toLowerCase();
                // Get list_open attribute from button
                var list_open = $(this).attr('listopen');
                if (list_open == '1') {
                    // Hide the autocomplete list
                    $('.ui-autocomplete').hide();
                    // Change value of listopen attribute
                    $(this).attr('listopen', '0');
                } else {
                    // If the drop-down contains more than 1000 choices, prevent it from trying to display the full list, which may be really slow.
                    if ($('option', $dropdown).length >= 1000) {
                        // Only show popover if clicked down-arrow img (not clicked item in full list)
                        if (targetTag == 'img') {
                            $(this).popover({ container: $tr, content: '<div class="fs11"><i class="fas fa-info-circle"></i> <b>'+lang.global_248+'</b> '+lang.global_249+' <b class="text-primary">'+lang.global_250+'</b>'+lang.period+'</div>', html: true, placement: 'right' }).popover('show');
                            setTimeout(function(){
                                $('#'+$tr.attr('id')+' .popover.show').addClass('rc-autocomplete-popover').removeClass('show');
                            }, 10000);
                        }
                        $input.focus();
                        return;
                    }
                    // If click the down arrow icon, put cursor inside text box and open the full list (force minLength=0 temporarily in order to do this)
                    $input.autocomplete("option", "minLength", 0);
                    $input.focus();
                    $input.autocomplete("search", "");
                    // Now that the drop-down has been fully opened, we need to reset minLength to re-enable performance limitations when searching
                    if ($input.val() == '') {
                        var numChoices = $('option', $dropdown).length;
                        var minLength;
                        if (numChoices <= 200) {
                            minLength = 0;
                        } else if (numChoices <= 500) {
                            minLength = 1;
                        } else if (numChoices <= 2000) {
                            minLength = 2;
                        } else {
                            minLength = Math.min(4, Math.ceil(numChoices/1000));
                        }
                        $input.autocomplete("option", "minLength", minLength);
                    }
                    // Change value of list_open attribute
                    $(this).attr('listopen', '1');
                }
                // Hide any progress popovers
                $('.rc-autocomplete-popover').remove();
                setTimeout(function(){ $('.rc-autocomplete-popover').remove(); }, 10);
            });
        }
        // When page loads, add existing value's label to input field
        if ($dropdown.val() != "") {
            var saved_val_text = $("option:selected", $dropdown).text();
            $input.val(saved_val_text).attr('value',saved_val_text); // Also set attr() in case using Randomization, which replaces text inside TD cell.
        }
        // Extract options from dropdown for jQueryUI autocomplete
        var list = $dropdown.children();
        var x = [];
        for (var i = 0; i<list.length; i++){
            var this_opt_val = list[i].value;
            if (this_opt_val != '') {
                x.push({ value: html_entity_decode(list[i].innerHTML), code: this_opt_val });
            }
        }
        // As the size of the option list increases, increase the minLength up to a max.
        // This is required to prevent fields with hundreds/thousands of options from being unreasonably slow.
        var minLength;
        var numChoices = x.length;
        if (numChoices <= 200) {
            minLength = 0;
        } else if (numChoices <= 500) {
            minLength = 1;
        } else if (numChoices <= 2000) {
            minLength = 2;
        } else {
            minLength = Math.min(4, Math.ceil(numChoices/1000));
        }
        // Initialize jQueryUI autocomplete
        $input.autocomplete({
            source: x,
            minLength: minLength,
            select: function (event, ui) {
                $dropdown.val(ui.item.code);
                $button.click();
                // $dropdown.change();
                $dropdown[0].dispatchEvent(new Event('change')); // Use vanilla JS to trigger onchange
                // Get field name (starts with "rc-ac-input_")
                var field_name = $input.attr('id').substring(12);
                setDataEntryFormValuesChanged(field_name);
            }
        })
        // Add escape character as HTML character code before the label because a single dash will turn into a divider
        .data('ui-autocomplete')._renderItem = function( ul, item ) {
        if (item.label == '-' || item.label == "\u2014" || item.label == "\u2013") {
            item.label = "&#27; " + item.label;
        }
        return $("<li></li>")
            .data("item", item)
            .append(item.label)
            .appendTo(ul);
        };
    });
}

// Select the radio button or checkbox inside "this" div/object (doesn't work on IE8 and below)
function sr(ob,e) {
    ob = $(ob);
    // Ignore if the radio button itself was clicked
    try {
        var nodeName = e.target.nodeName;
    } catch(error) {
        return;
    }
    var elementClicked = nodeName.toLowerCase();
    if (elementClicked == 'input') return;
    // Auto-click the radio/checkbox
    var isRadio = $('input[type="radio"]', ob).length;
    // if (isRadio && $('.rc-field-embed[var="'+$('input[type="radio"]:first', ob).prop('name').replace('___radio','')+'"]').length) {
        // Embedded radio
        // console.log($('input[type="radio"]:first', ob).prop('name')+' = '+$('input[type="radio"]:first', ob).prop('value'));
        //console.log('input[name="'+$('input[type="radio"]:first', ob).prop('name')+'"][value="'+$('input[type="radio"]:first', ob).prop('value')+'"]');
    //} else
    if (isRadio) {
        // Normal radio
        $('input[type="radio"]:first', ob).trigger('click');
    } else {
        // Checkbox
        var chkbox = $('input[type="checkbox"]:first', ob);
        var hidden = $('input[type="hidden"]:first', ob);
        var chkbox_checked = !chkbox.prop('checked');
        var chkbox_code = chkbox.attr('code');
        // Manually set the value of the hidden input field (because for some reason, having jQuery trigger click doesn't set this)
        hidden.val( (chkbox_checked ? chkbox_code : '') );
        // Click the checkbox
        chkbox.trigger('click');
    }
}

// Set autocomplete for BioPortal ontology search for ALL fields on a page
function initAllWebServiceAutoSuggest() {
    $('input.autosug-ont-field').each(function(){
        initWebServiceAutoSuggest($(this).attr('name'));
    });
}

// Set autocomplete for BioPortal ontology search for a field
function initWebServiceAutoSuggest(field_name,retriggerClick) {
    if ($('input#'+field_name+'-autosuggest').length < 1) return;
    // Check if autocomplete has been enabled already for this field
    if ($('input#'+field_name+'-autosuggest').hasClass('ui-autocomplete-input')) return;
    // If the data entry page is locked or is a non-editable response, then don't enable this feature
    if (   (!$('#__SUBMITBUTTONS__-tr').length && page != 'surveys/index.php')
        || ($('#__SUBMITBUTTONS__-tr').length && $('#__SUBMITBUTTONS__-tr').css('display') == 'none')
        || ($('#__LOCKRECORD__').length && $('#__LOCKRECORD__').prop('checked'))
    ) {
        return;
    }
    // If we need to retrigger the click (due to Online Designer not initiating this function on page load), then trigger click
    if (retriggerClick != null && retriggerClick == '1') {
        $('input[name="'+field_name+'"]').removeAttr('onclick');
        initWebServiceAutoSuggest(field_name);
        $('input[name="'+field_name+'"]').trigger('click');
        return;
    }
    // Set URLs for ajax
    if (page == 'surveys/index.php') {
        var url = dirname(app_path_webroot.substring(0,app_path_webroot.length-1))+'/surveys/index.php?s='+getParameterByName('s')+'&__passthru='+encodeURIComponent('DataEntry/web_service_auto_suggest.php')+'&field='+field_name;
        var url_cache = dirname(app_path_webroot.substring(0,app_path_webroot.length-1))+'/surveys/index.php?s='+getParameterByName('s')+'&__passthru='+encodeURIComponent('DataEntry/web_service_cache_item.php');
    } else {
        var url = app_path_webroot+'DataEntry/web_service_auto_suggest.php?pid='+pid+'&field='+field_name;
        var url_cache = app_path_webroot+'DataEntry/web_service_cache_item.php?pid='+pid;
    }
    // Init auto-complete
    $('input#'+field_name+'-autosuggest').autocomplete({
        source: url,
        minLength: 2,
        delay: 0,
        search: function( event, ui ) {
            // Show progress icon
            $('#'+field_name+'-autosuggest-progress').show();
        },
        open: function( event, ui ) {
            // Hide progress icon
            $('#'+field_name+'-autosuggest-progress').hide('fade',{ },200);
            // If user backspaces to remove all search characters, then make sure the auto-suggest list stays hidden (buggy)
            if ($('input#'+field_name+'-autosuggest').val().length == 0) {
                $('.ui-autocomplete, .ui-menu-item').hide();
            }
        },
        focus: function( event, ui ) {
            // Prevent it from putting the value in the search input (default)
            return false;
        },
        select: function( event, ui ) {
            // Prevent user from clicking "No results were returned" option
            if (strip_tags(ui.item.value) === "["+lang.report_builder_87+"]") {
                return false;
            }
            // Add raw value to original input field
            $('input[name="'+field_name+'"]').val(ui.item.value);
            // Put the label into the span
            $('#'+field_name+'-autosuggest-span').val(ui.item.preflabel);
            // Trigger blur on search input to force it to hide
            $('input#'+field_name+'-autosuggest').trigger('blur');
            // Make ajax call to store the label
            if (page != 'Design/online_designer.php') {
                $.post(url_cache, { service: ui.item.service, category: ui.item.cat, value: ui.item.value, label: ui.item.preflabel });
            }
            // Execute branching and calculations, just in case
            try{ calculate(field_name);doBranching(field_name); }catch(e){ }
            return false;
        }
    })
        .data('ui-autocomplete')._renderItem = function( ul, item ) {
        return $("<li></li>")
            .data("item", item)
            .append("<a>"+item.label+"</a>")
            .appendTo(ul);
    };
    // When user clicks or focuses on original input, put cursor in the search box
    $('#'+field_name+'-autosuggest-span, input[name="'+field_name+'"]').bind('click focus', function(){
        var current_val = $('#'+field_name+'-autosuggest-span').val();
        // Temporarily hide original input and display search input
        $('input[name="'+field_name+'"]').hide();
        $('#'+field_name+'-autosuggest-span').hide();
        $('input#'+field_name+'-autosuggest').val(current_val).show().focus();
        $('#'+field_name+'-autosuggest-instr').show();
    });
    // Re-display original input after choosing selection or leaving search field
    $('input#'+field_name+'-autosuggest').bind('blur', function(){
        $(this).hide();
        $('#'+field_name+'-autosuggest-instr, #'+field_name+'-autosuggest-progress').hide();
        $('input[name="'+field_name+'"], #'+field_name+'-autosuggest-span').show();
        // If auto-suggest value was removed or is empty, make sure the other inputs are empty as well so that it gets erased if already saved.
        if ($(this).val().length == 0) {
            $('#'+field_name+'-autosuggest-span').val('');
            $('input[name="'+field_name+'"]').val('');
            // Execute branching and calculations, just in case
            try{ calculate(field_name);doBranching(field_name); }catch(e){ }
        }
    });
}

// Open dialog with embedded video
function openEmbedVideoDlg(video_url,unknown_video_service,field_name,video_custom_html) {
    if (typeof video_custom_html == 'undefined') video_custom_html = '';
    var dlgid = 'rc-embed-video-dlg_'+field_name;
    var vidid = 'rc-embed-video_'+field_name;
    var vidwidth = 750;
    var vidheight = 500;
    if (unknown_video_service) {
        if (isIOS) {
            var rc_embed_html = '<video id="'+vidid+'" width="'+vidwidth+'" height="'+vidheight+'" playsinline controls><source src="'+video_url+'" type="video/'+getfileextension(video_url.toLowerCase())+'"></source></video>';
        } else {
            var rc_embed_html = '<embed id="'+vidid+'" src="'+video_url+'" width="'+vidwidth+'" height="'+vidheight+'" scale="aspect" controller="true" autostart="0" autostart="false"></embed>';
        }
    } else if (video_custom_html != '') {
        var rc_embed_html = html_entity_decode(video_custom_html);
    } else {
        var rc_embed_html = '<iframe id="'+vidid+'" src="'+video_url+'" type="text/html" frameborder="0" allowfullscreen width="'+vidwidth+'" height="'+vidheight+'"></iframe>';
    }
    // Add content to dialog and open it
    initDialog(dlgid);
    // Strings are from global lang
    // Close = calendar_popup_01
    // Video = data_entry_610
    $('#'+dlgid)
        .show().html(rc_embed_html)
        .dialog({ height: (vidheight+130), width: (isMobileDevice ? $(window).width() : (vidwidth+60)), open:function(){ fitDialog(this); }, close:function(){ $(this).dialog('destroy'); $('#'+dlgid).remove(); },
            buttons: [{ text: window.lang.calendar_popup_01, click: function(){ $(this).dialog('close'); } }], title: window.lang.data_entry_610, bgiframe: true, modal: true
        });
    // Mobile only: Resize video
    if (isMobileDevice) {
        $('#'+vidid).width( $('body').width()-40 );
        $('#'+vidid).height( $('#'+dlgid).height()-10 );
    }
    // Reload VidYard JS (needed to initialize any videos added after page load)
    if (video_custom_html != '') initVidYardJS();
}

// Reload VidYard JS (needed to initialize any videos added after page load)
function initVidYardJS() {
    if ($('.vidyard-player-embed:visible').length) {
        loadJS(app_path_webroot + 'Resources/js/Libraries/vidyard_v4.js');
    }
}


// Add or remove a password mask from a text input field
// Object "ob" should be passed to the function as the jQuery object of the input field.
// Boolean "add", in which false=remove password mask.
function passwordMask(ob, add) {
    // Remove any date/time picker widgets from input
    try { ob.datepicker('destroy'); }catch(e){ }
    try { ob.datetimepicker('destroy'); }catch(e){ }
    try { ob.timepicker('destroy'); }catch(e){ }
    ob.removeClass('hasDatepicker').unbind();
    // Clone input field and replace it
    ob.clone().attr('type', (add ? 'text' : 'password')).insertAfter(ob);
    ob.remove();
    // Reactivate any widgets whose connection to object gets lost with cloning
    initWidgets();
}

// Matrix field ranking validation function
function matrix_rank(crnt_val,crnt_var,grid_vars) {
    // Reset validation flag on page
    $('#field_validation_error_state').val('0');
    // array of all field_names within matrix group
    // gv[0]=>'w1',gv[1]=>'w2',gv[2]=>'w3',...
    var grid_vars = grid_vars.split(',');
    var id, i;
    var rank_remove_label = $('#matrix_rank_remove_label');
    var remove_label_time = 2500;
    // loop through other variables within this matrix group
    for (i = 0; i < grid_vars.length; i++) {
        if (crnt_var !== grid_vars[i]) {
            id = "mtxopt-"+grid_vars[i]+"_"+crnt_val;
            id = id.replace(/\./g,'\\.');
            if ($("#"+id).is(":checked")) {
                // Uncheck the input
                radioResetVal(grid_vars[i],'form');
                // Add temporary "value removed" label
                rank_remove_label.show().position({
                    my:        "center top",
                    at:        "center top+10",
                    of:        $("#"+id)
                });
                setTimeout(function(){
                    rank_remove_label.hide();
                },remove_label_time);
            }
        }
    }
}

// When stop action is triggered by clicking a survey question option, give notice before ending survey
function triggerStopAction(ob) {
    var obname = ob.prop('name');
    // Get varname of field
    var varname = '';
    if (obname.substring(0,8) == '__chkn__'){
        // Checkbox
        varname = obname.substring(8,obname.length);
    } else if (obname.substring(obname.length-8,obname.length) == '___radio'){
        // Radio
        varname = obname.substring(0,obname.length-8);
    } else {
        // Drop-down (including any auto-complete input component)
        varname = obname;
    }
    $('#stopActionPrompt').dialog({ bgiframe: true, modal: true, title: lang.survey_01, width: (isMobileDevice ? $(window).width() : 600),
        close: function(){
            // Undo last response if closing and returning to survey
            if (obname.substring(0,8) == '__chkn__'){
                // Checkbox
                $('#form :input[name="'+obname+'"]').each(function(){
                    if ($(this).attr('code') == ob.attr('code')) {
                        $(this).prop('checked',false);
                        // If using Enhanced Choices for radios, then deselect it
                        $('#'+varname+'-tr div.enhancedchoice label.selectedchkbox[comps="'+varname+',code,'+ob.attr('code')+'"]').removeClass('selectedchkbox').addClass('unselectedchkbox');
                    }
                });
                $('#form :input[name="'+obname.replace('__chkn__','__chk__')+'_RC_'+replaceDotInCheckboxCoding(ob.attr('code'))+'"]').val('');
            } else if (obname.substring(obname.length-8,obname.length) == '___radio'){
                // Radio
                radioResetVal(varname,'form');
            } else {
                // Drop-down (including any auto-complete input component)
                $('#form select[name="'+obname+'"], #rc-ac-input_'+obname).val('');
            }
        },
        buttons: [{ text: lang.survey_1312, click: function() {
                // Trigger calculations and branching logic
                $('#'+varname+'-tr>td').removeClass('greenhighlight');
                highlightTableRow(varname+'-tr',2000);
                setTimeout(function(){$('#'+varname+'-tr>td').addClass('greenhighlight');},1500);
                setTimeout(function(){calculate(varname);doBranching(varname);},50);
                setDataEntryFormValuesChanged(varname);
                $(this).dialog('close');
            } },
            { text: lang.survey_1311, click: function() {
                    // Make sure that auto-complete drop-downs get their value set prior to ending survey
                    if ($('#form select[name="'+obname+'"]').hasClass('rc-autocomplete') && $('#rc-ac-input_'+obname).length) {
                        $('#rc-ac-input_'+obname).trigger('blur');
                    }
                    // Change form action URL to force it to end the survey
                    $('#form').prop('action', $('#form').prop('action')+'&__stopaction=1&__endsurvey=1' );
                    // Submit the survey
                    dataEntrySubmit(document.getElementById('submit-action'));
                } } ]
    });
}

//Set value and enable specific slider
function setSlider(fld,val) {
    $("#slider-"+fld).slider("option", "value", val);
    $("#slider-"+fld).slider("enable");
    $("#sldrmsg-"+fld).css('visibility','hidden');
}

// If a field's value is a missing data code, remove it and set value to blank/null
function removeFieldMissingDataCode(fld) {
    if ($('#' + fld +'_MDLabel:visible').length) {
        $('tr#'+fld+'-tr img.missingDataButton').trigger('click');
        $('#MDMenu .set_btn[code=""]').trigger('click');
    }
}

//Reset slider value
function resetSlider(fld,bypassBranchingCalcs) {
    if (typeof bypassBranchingCalcs == 'undefined') bypassBranchingCalcs = false;
    var min = $('#slider-'+fld).attr('data-min')*1;
    var max = $('#slider-'+fld).attr('data-max')*1;
    var startValue = Math.floor((min+max)/2);
    $("#slider-"+fld).slider("option", "value", startValue);
    $("#slider-"+fld).slider("disable");
    $("#sldrmsg-"+fld).css('visibility','visible');
    $('form[name="form"] input[name="'+fld+'"]').val('');
    setDataEntryFormValuesChanged(fld);
    // If field has missing data code, then reset it
    removeFieldMissingDataCode(fld);
    $('#slider-'+fld).removeAttr('modified');
    if (!bypassBranchingCalcs) {
        calculate(fld);
        doBranching(fld);
    }
    try {
        $('.piping_receiver.piperec-'+event_id+'-'+fld).html(missing_data_replacement_js );
        $('.piping_receiver.piperec-'+event_id+'-'+fld+'-value').html(missing_data_replacement_js );
        $('.piping_receiver.piperec-'+event_id+'-'+fld+'-label').html(missing_data_replacement_js );
    } catch(e) { }
}

//Date field functions
function dateKeyDown(event2,fldname) {
    // eval("var fld = document.form."+fldname+";");
    if (event2.keyCode==13) {
        $('document.form.'+fldname).blur();
        return true;
    }
}

// Button to set date field to today's date
function setToday(name,valType) {
    eval("document.form."+name+".value='"+getCurrentDate(valType)+"';");
    // If user modifies any values on the data entry form, set flag to TRUE
    setDataEntryFormValuesChanged(name);
    // Trigger branching/calc fields, in case fields affected
    $('[name='+name+']').focus();
    setTimeout(function(){try{calculate(name);doBranching(name);}catch(e){}},50);
}

// Button to set time field to current time as hh:ss
function setNowTime(name,seconds) {
    if (typeof seconds == 'undefined') seconds = false;
    eval("document.form."+name+".value='"+currentTime('both',seconds)+"';");
    // If user modifies any values on the data entry form, set flag to TRUE
    setDataEntryFormValuesChanged(name);
    // Trigger branching/calc fields, in case fields affected
    $('[name='+name+']').focus();
    setTimeout(function(){try{calculate(name);doBranching(name);}catch(e){}},50);
}

// Button to set datetime field to current time as yyyy-mm-dd hh:ss
function setNowDateTime(name,showSeconds,valType) {
    eval("document.form."+name+".value='"+getCurrentDate(valType)+' '+currentTime('both',showSeconds)+"';");
    // If user modifies any values on the data entry form, set flag to TRUE
    setDataEntryFormValuesChanged(name);
    // Trigger branching/calc fields, in case fields affected
    $('[name='+name+']').focus();
    setTimeout(function(){try{calculate(name);doBranching(name);}catch(e){}},50);
}

// Open popup window for viewing a calc field's equation
function viewEq(field, isDataCalc, isCalcText, randTarget) {
    var dialogTitle = (randTarget) ? lang.form_renderer_68 : lang.form_renderer_67;
    var metadata_table = (status > 0 && page == 'Design/online_designer.php') ? 'metadata_temp' : 'metadata';
    $.get(app_path_webroot+'DataEntry/view_equation_popup.php', { pid: pid, field: field, metadata_table: metadata_table, calcdate: isDataCalc, calctext: isCalcText, randtarget: randTarget }, function(data) {
        if (!$('#viewEq').length) $('body').append('<div id="viewEq"></div>');
        $('#viewEq').html(data);
        $('#viewEq').dialog({ bgiframe: true, modal: true, title: dialogTitle+' "'+field+'"', width: 700,
            buttons: { Close: function() { $(this).dialog('close'); } }, open:function(){ fitDialog(this); } });
    });
}

// Branching Logic & Calculated Fields
var calcErrExist = true;
var brErrExist   = true;
function calcErr(fld) {
    alert(lang.global_210+'\n\n'+lang.global_211+' "'+fld+'" '+lang.global_212+'\n\n'+lang.global_213);
}
function calcErr2() {
    alert(lang.global_210+'\n\n'+lang.global_214+'\n\n'+lang.global_213);
}
function brErr(fld) {
    if (page == 'surveys/index.php') {
        // Survey page
        alert(lang.global_215+'\n\n'+lang.global_220+' "'+fld+'"'+lang.period+' '+lang.global_217);
    } else {
        // Data entry form
        alert(lang.global_216+'\n\n'+lang.global_221+' "'+fld+'" '+lang.global_222+' '+ lang.global_219+'\n\n'+ lang.global_213);
    }
}
function brErase(fld) {
    return lang.global_218+' "'+fld+'" '+lang.questionmark+'\n\n'+lang.global_223+' "'+fld+'" '+lang.global_224+'\n\n'+ lang.global_225;
}
function brErr2() {
    if (page == 'surveys/index.php') {
        // Survey page
        alert(lang.global_215+'\n\n'+lang.global_226+' '+lang.global_217);
    } else {
        // Data entry form
        alert(lang.global_216+'\n\n'+lang.global_227+' '+ lang.global_219+'\n\n'+ lang.global_213);
    }
}

// Remove all unselected options from Form Status drop-down (used when page is locked but not e-signed)
function removeUnselectedFormStatusOptions() {
    $(':input[name='+getParameterByName('page')+'_complete] option').each(function(){
        if ( $(this).prop('selected') == false ) {
            $(this).remove();
        } else {
            $(this).css('color','gray');
        }
    });
}

// Run processes when submitting form on data entry page
function formSubmitDataEntry() {
    // Disable the onbeforeunload so that we don't get an alert before we leave
    window.onbeforeunload = function() { }
    // Close the datepicker widget, if opened, for hardtyped textbox fields
    if ($('#ui-datepicker-div:visible').length && pickerOpenerField != '' && $('input[name='+pickerOpenerField+'][hardtyped]').length) {
        $('#ui-datepicker-div').hide();
        $('input[name='+pickerOpenerField+'][hardtyped]').blur();
        setTimeout("formSubmitDataEntry();",100);
        return;
    }
    // If field validation popup is visible, do not allow submission. It must be closed first.
    if ($('#redcapValidationErrorPopup:visible').length) return;
    // If a drop-down field was somehow given an invalid value, which removes it from the POST request because it has a NULL value,
    // set its value to "" so that it gets picked up by the Required Field check in POST (in case the field is required).
    $('#questiontable select').each(function(){
        var field = $(this).attr('name');
        if ($('#'+field+'-tr[req]').length && $('#'+field+'-tr[req]').attr('req') == '1' && $(this).val() === null) {
            $(this).val('');
        }
    });
    // Disable all buttons on page when submitting to prevent double submission
    $('#form :button, #formSaveTip :button').prop('disabled',true);
    // Before finally submitting the form, execute all calculated fields again just in case someone clicked Enter in a text field
    calculate();
    // Is survey page?
    var isSurveyPage = (page == 'surveys/index.php');
    // REQUIRED FIELDS: Loop through table and remove form elements from html that are hidden due to branching logic
    // (so user is not prompted to enter values for invisible fields).
    $("#questiontable .rc-field-embed[req='1']").each(function() { // Run this before the next REQUIRED FIELD block below because some embedded fields might be erased via $(this).html(''); below for a container field.
        // Is the req field hidden (i.e. on another survey page)? But not hidden via @HIDDEN.
        var parent_tr = $(this).parentsUntil('tr[sq_id], tr[mtxgrp]').parent();
        var field_tr = $('#'+$(this).attr("var")+'-tr');
        var parentHiddenNotByActionTag = (parent_tr.css("display") == "none" && !parent_tr.hasClass("\@HIDDEN") && !(isSurveyPage && parent_tr.hasClass("\@HIDDEN-SURVEY")) && !(!isSurveyPage && parent_tr.hasClass("\@HIDDEN-FORM")));
        var fieldHasHiddenActionTag = (field_tr.hasClass("\@HIDDEN") || (isSurveyPage && field_tr.hasClass("\@HIDDEN-SURVEY")) || (!isSurveyPage && field_tr.hasClass("\@HIDDEN-FORM")));
        var fieldVisible = ($(this).css("display") != "none");
        if (!$(this).find('.rc-field-embed-error').length && (
            // If parent/container is hidden (but not via @HIDDEN action tag) while the embedded field is visible on its own right (but not via @HIDDEN action tag), then add as empty-required-field
            (parentHiddenNotByActionTag && !(fieldVisible && !fieldHasHiddenActionTag))
            // If field itself or its parent/container is hidden (but not via @HIDDEN action tag), then add as empty-required-field
            || ((parentHiddenNotByActionTag || !fieldVisible) && !fieldHasHiddenActionTag)
        )) {
            // If field does not exist on this survey page, then skip it (it might be hidden on the page due to branching/calc necessity)
            if (isSurveyPage && typeof pageFields != 'undefined' && !in_array($(this).attr("var"), pageFields)) return;
            // Only remove field from form if does not already have a saved value (i.e. has 'hasval=1' as row attribute)
            $(this).html('');
            // Add to empty required field list that gets submitted
            appendHiddenInputToForm('empty-required-field[]', $(this).attr("var"));
        }
    });
    $("#questiontable tr").each(function() {
        // Is it a required field (and is not embedded)?
        if ($(this).attr("req") != null && !$(this).hasClass('row-field-embedded')) {
            // Is the req field hidden (i.e. on another survey page)?
            if ($(this).css("display") == "none") {
                // Only remove field from form if does not already have a saved value (i.e. has 'hasval=1' as row attribute)
                if ($(this).attr("hasval") != "1" && !($(this).hasClass("\@HIDDEN")
                    || ($(this).hasClass("\@HIDDEN-SURVEY") && isSurveyPage) || ($(this).hasClass("\@HIDDEN-FORM") && !isSurveyPage)))
                {
                    // If field does not exist on this survey page, then skip it (it might be hidden on the page due to branching/calc necessity)
                    if (isSurveyPage && typeof pageFields != 'undefined' && !in_array($(this).attr("sq_id"), pageFields)) return;
                    // Remove value
                    $(this).html('');
                    // Add to empty required field list that gets submitted
                    appendHiddenInputToForm('empty-required-field[]', $(this).attr("sq_id"));
                }
            }
        }
    });
    // For surveys only
    if (isSurveyPage) {
        // If using "save and return later", append to form action to point to new place
        if ($('#submit-action').val() == "submit-btn-savereturnlater") {
            $('#form').attr('action', $('#form').attr('action')+'&__return=1' );
        }
        // If using "previous page" button, append to form action to point to new place
        if ($('#submit-action').val() == "submit-btn-saveprevpage") {
            $('#form').attr('action', $('#form').attr('action')+'&__prevpage=1' );
        }
    }
    // Re-enable any disabled fields (due to field action tags and such) - make sure we leave any randomization fields disabled though
    $('#questiontable input:disabled, #questiontable select:disabled, #questiontable textarea:disabled').each(function(){
        var fld = $(this);
        if (randomizationCriteriaFieldList == null ||
            (typeof fld.parents('tr:first').attr('id') != 'undefined' && !in_array(fld.parents('tr:first').attr('id').slice(0,-3), randomizationCriteriaFieldList)) ||
            (typeof fld.parentsUntil('.rc-field-embed').parent().attr('var') != 'undefined' && !in_array(fld.parentsUntil('.rc-field-embed').parent().attr('var'), randomizationCriteriaFieldList))
        ) {
            fld.prop('disabled', false);
        }
    });
    // If Secondary Unique Field is disabled (because it's currently being checked for uniqueness via AJAX), then don't submit form
    if (secondary_pk != '' && $('#form :input[name="'+secondary_pk+'"]').length && $('#form :input[name="'+secondary_pk+'"]').prop('disabled')) {
        // Re-enable all submit buttons
        $('#form :button, #formSaveTip :button').prop('disabled',false);
        // Do not submit form
        return;
    }
    // Hide all images on page and show progress div
    $('#form img').css('visibility', 'hidden');
    showProgress(1);
    // Submit form (finally!)
    document.form.submit();
}

// Execute when buttons are clicked on data entry forms
function dataEntrySubmit(ob)
{
    window['regularSubmit'] = true;
    // Set value of hidden field used in post-processing after form is submitted
    if (typeof ob === 'string' || ob instanceof String) {
        $('#submit-action').val( ob );
    } else {
        $('#submit-action').val( $(ob).attr('name') );
    }
    if ($('#submit-action').val() == '' || $('#submit-action').val() == null) {
        $('#submit-action').val('submit-btn-saverecord');
    }

    // Clicked Save or Delete
    if ($('#submit-action').val() != "submit-btn-cancel")
    {
        // Determine esign_action
        var esign_action = "";
        if ($('#__ESIGNATURE__').length && $('#__ESIGNATURE__').prop('checked') && $('#__ESIGNATURE__').prop('disabled') == false) {
            esign_action = "save";
            // If form is not locked already or checked to be locked, then stop (because is necessary)
            if ($('#__LOCKRECORD__').prop('checked') == false) {
                simpleDialog('WARNING:\n\nThe "Lock Record" option must be checked before the e-signature can be saved. Please check the "Lock Record" check box and try again.');
                return false;
            }
        }

        // Set the lock action
        var lock_action = ($('#__LOCKRECORD__').prop("disabled") && (esign_action == "save" || esign_action == "")) ? 2 : 1;

        // "change reason" popup for existing records (and lock record, if user has rights)
        if (require_change_reason && record_exists && (dataEntryFormValuesChanged || $('#submit-action').val() == 'submit-btn-delete'))
        {
            $('#change_reason_popup').dialog({
                title: lang.data_entry_603,
                bgiframe: true, 
                modal: true, 
                width: 500, 
                zIndex: 4999, 
                buttons: [{
                    text: lang.report_builder_28,
                    click: function() {
                        if ($("#change_reason").val().length < 1) {
                            $('#change_reason_popup_error').show();
                            return false;
                        }
                        // Before submitting the form, add change reason values from dialog as form elements for submission
                        $('#form').append('<input type="hidden" name="change-reason" value="'+$("#change_reason").val().replace(/"/gi, '&quot;')+'">');
                        // Save locked value
                        if ($('#__LOCKRECORD__').prop('checked')) {
                            $('#change_reason_popup').dialog('destroy');
                            saveLocking(lock_action,esign_action);
                            // Not locked, so just submit form
                        } else {
                            formSubmitDataEntry();
                        }
                    }
                }],
                open: () => {
                    // Restore default state (error hidden)
                    $('#change_reason_popup_error').css('display','none');
                    // Update strings (if MLM is present)
                    if (typeof REDCap.MultiLanguage != 'undefined') {
                        REDCap.MultiLanguage.translateRcLang('#change_reason_popup');
                    }
                }
            });

        }
        // Do locking and/or save e-signature, then submit form
        else if ($('#__LOCKRECORD__').prop('checked') && (!$('#__LOCKRECORD__').prop("disabled") || esign_action == "save"))
        {
            saveLocking(lock_action,esign_action);
        }
        // Just submit form if neither using change_reason nor locking
        else
        {
            formSubmitDataEntry();
        }
    }
    // Clicked Cancel (requires form submission)
    else {
        formSubmitDataEntry();
    }
}

// After running branching logic, hide any section headers in which all fields in the section have been hidden
function hideSectionHeaders() {
    var this_id;
    var this_display;
    var lastSH = "";
    var numFields = 0;
    var numFieldsHidden = 0;
    var tbl = document.getElementById("questiontable");
    var rows = tbl.tBodies[0].rows;
    var matrixGroup = "";
    var lastMatrixGroup = "";
    var matrixGroups = new Array();
    var fieldIsHidden;
    var getClassTerm = 'class';
    var thisClass;
    var isSurveyPage = (page == 'surveys/index.php');
    //Get index somewhere in middle of table
    for (var i=0; i<rows.length; i++) {
        // Get id for this row
        this_id = rows[i].getAttribute("id");

        // If this row has an id, then check if SH, matrix header, matrix field, or regular field
        if (this_id != null && this_id.indexOf("-tr") > 0) {

            // If a Section Header, then check if previous section's fields were all hidden. If so, then hide the SH too.
            if (this_id.indexOf("-sh-tr") > 0) {
                if (lastSH != "") {
                    if (numFieldsHidden == numFields && numFields > 0) {
                        // Hide SH
                        document.getElementById(lastSH).style.display = 'none';
                    } else {
                        // Possibly show SH OR do nothing
                        var showit = true;
                        if (isSurveyPage) {
                            // Survey page: Treat differently since it contains fields on the form that might need to remain hidden (because of multi-paging)
                            if (document.getElementById(lastSH).getAttribute(getClassTerm) != null) {
                                if (document.getElementById(lastSH).getAttribute(getClassTerm).indexOf('hidden') > -1) {
                                    // If row has class 'hidden', then keep hidden
                                    showit = false;
                                }
                            }
                        }
                        // Make SH visible (in case it was hidden)
                        if (showit) document.getElementById(lastSH).style.display = 'table-row';
                    }
                }
                // Reset values for next section
                lastSH = this_id;
                numFields = 0;
                numFieldsHidden = 0;
                matrixGroup = "";
            }

            // If a Matrix Header, then hide the Matrix Header too.
            else if (this_id.indexOf("-mtxhdr-tr") > 0) {
                matrixGroup = lastMatrixGroup = document.getElementById(this_id).getAttribute('mtxgrp');
                matrixGroups[matrixGroup] = 0;
            }

            // If a normal field, then check its display value AND if it's in a matrix group
            else {
                // Check if hidden
                fieldIsHidden = (document.getElementById(this_id).style.display == "none"
                                || (document.getElementById(this_id).getAttribute(getClassTerm) != null && document.getElementById(this_id).getAttribute(getClassTerm).indexOf('hide') > -1));
                if (!fieldIsHidden) {
                    // Also check if has @HIDDEN action tag
                    if (document.getElementById(this_id).getAttribute(getClassTerm) != null) {
                        thisClass = document.getElementById(this_id).getAttribute(getClassTerm);
                        if (thisClass.indexOf('@HIDDEN ') > -1 || thisClass.substr(thisClass.length-7) == '@HIDDEN'
                            || (isSurveyPage && thisClass.indexOf('@HIDDEN-SURVEY') > -1)
                            || (!isSurveyPage && thisClass.indexOf('@HIDDEN-FORM') > -1))
                        {
                            // Set as hidden
                            fieldIsHidden = true;
                            document.getElementById(this_id).style.display == "none";
                        }
                    }
                }
                if (fieldIsHidden) numFieldsHidden++;
                // Count field for this section
                numFields++;
                // If field is in a matrix group, get group name
                if (document.getElementById(this_id).getAttribute('mtxgrp') != null) {
                    matrixGroup = document.getElementById(this_id).getAttribute('mtxgrp');
                    if (!fieldIsHidden) matrixGroups[matrixGroup]++;
                }
            }

        }
    }

    // For survey pages only: Check if we need to hide the last SH on the page (will not hide by itself with current logic)
    if (isSurveyPage && lastSH != "") {
        if (numFieldsHidden == numFields && numFields > 0) {
            // Hide SH
            document.getElementById(lastSH).style.display = 'none';
        } else {
            // Possibly show SH OR do nothing
            var showit = true;
            if (isSurveyPage) {
                // Survey page: Treat differently since it contains fields on the form that might need to remain hidden (because of multi-paging)
                if (document.getElementById(lastSH).getAttribute(getClassTerm) != null) {
                    if (document.getElementById(lastSH).getAttribute(getClassTerm).indexOf('hidden') > -1) {
                        // If row has class 'hidden', then keep hidden
                        showit = false;
                    }
                }
            }
            // Make SH visible (in case it was hidden)
            if (showit) document.getElementById(lastSH).style.display = 'table-row';
        }
    }

    // If any matrix groups have all their fields hidden (i.e. value=0), then hide the matrix header
    for (var grpname in matrixGroups) {
        var mtxhdr_id = grpname+'-mtxhdr-tr';
        if (matrixGroups[grpname] == 0) {
            // Hide matrix header
            document.getElementById(mtxhdr_id).style.display = 'none';
        } else {
            // Possibly show matrix header OR do nothing
            var showit = true;
            if (isSurveyPage) {
                // Survey page: Treat differently since it contains fields on the form that might need to remain hidden (because of multi-paging)
                if (document.getElementById(mtxhdr_id).getAttribute(getClassTerm) != null) {
                    if (document.getElementById(mtxhdr_id).getAttribute(getClassTerm).indexOf('hidden') > -1) {
                        // If row has class 'hidden', then keep hidden
                        showit = false;
                    }
                }
            }
            // Make matrix header visible (in case it was hidden)
            if (showit) document.getElementById(mtxhdr_id).style.display = 'table-row';
        }
    }
}

function uploadFilePreProcess() {
    $('#file_upload_vault_popup_error').hide();
    var isSignature = ($('#f1_upload_form input[name="myfile_base64"]').val().length > 0);
    var missingFile = (!isSignature && $('#f1_upload_form input[name="myfile"]').val().length + $('#f1_upload_form input[name="myfile_base64"]').val().length == 0);
    var isSurvey = (page == 'surveys/index.php');
    if (isSignature || missingFile || !file_upload_vault_enabled) {
        // Normal: Submit the form
        $('#form_file_upload').trigger('submit');
        return;
    }
    $('#file_upload_vault_popup_text1').html(basename($('#f1_upload_form input[name="myfile"]').val()));
    // Prompt for user password for Vault Storage+Password feature (excluding surveys)
    if (isSurvey) {
        // Survey
        simpleDialog(null, null, 'file_upload_vault_popup', 550, null, window.lang.global_53, "$('#form_file_upload').submit();", window.lang.design_654);
    } else {
        // Form: Password prompt
        simpleDialog(null, null, 'file_upload_vault_popup', 600, null, window.lang.global_53, function(){
            // Verify username/password
            $('#file_upload_vault_password').val( $('#file_upload_vault_password').val().trim() );
            $.post(app_path_webroot+'index.php?pid='+pid+'&route=DataEntryController:passwordVerify',{username: $('#file_upload_vault_username').val(), password: $('#file_upload_vault_password').val()},function(data){
                if (data == '1') {
                    $('#file_upload_vault_password').val('');
                    $('#form_file_upload').submit();
                } else if (data == '0') {
                    simpleDialog(window.lang.form_renderer_48, window.lang.global_01, null, 400, 'uploadFilePreProcess();');
                } else {
                    alert(woops);
                    uploadFilePreProcess();
                }
            });
        }, window.lang.design_654);
    }
    $('#file_upload_vault_popup').parent().find('div.ui-dialog-buttonpane button:eq(1)').css('color','#006000').addClass('font-weight-bold').prepend('<i class="fas fa-check"></i> ');
    $('#file_upload_vault_password').trigger('focus');
}

var file_upload_delete_reason = '';
function deleteDocumentConfirm(doc_id,this_field,id,event_id,instance,delete_page,version,version_hash) {
    if (typeof version == 'undefined') version = '';
    if (typeof version_hash == 'undefined') version_hash = '';
    var extraText = '';
    if (file_upload_vault_enabled) {
        file_upload_delete_reason = '';
        extraText = '<div class="mt-3 mb-2">' + window.lang.form_renderer_50 + '<textarea id="file_upload_delete_reason" class="x-form-field notesbox" style="height:60px;" onchange="file_upload_delete_reason=this.value;"></textarea></div>';
    }
    simpleDialog("<div class='boldish fs14'>" + window.lang.form_renderer_51 + "</div>" + extraText, window.lang.form_renderer_52, 'remove-file', 480, "$('#MDMenu').hide();", window.lang.global_53, function(){
        if ($('#file_upload_delete_reason').length) {
            $('#file_upload_delete_reason').val($('#file_upload_delete_reason').val().trim());
            if ($('#file_upload_delete_reason').val() == '') {
                simpleDialog(window.lang.form_renderer_49, window.lang.global_01, null, null,
                    "deleteDocumentConfirm('" + doc_id + "','" + this_field + "','" + id + "','" + event_id + "','" + instance + "','" + delete_page + "','" + version + "','" + version_hash + "');"
                );
                return;
            }
        }
        deleteDocument(doc_id,this_field,id,event_id,instance,delete_page,version,version_hash);
        addFileEnhancements();
    }, window.lang.design_397);
}
var codeUpdateAfterDeleteFile = "";
function deleteDocument(doc_id,this_field,id,event_id,instance,delete_page,version,version_hash) {
    // Set value to blank on form
    if (version == '') {
        eval("document.form." + this_field + ".value = '';");
        $('#form a.filedownloadlink[name="'+this_field+'"] .fu-fn').attr('vf', '');
        $('#fileupload-container-'+this_field+' .inline-pdf-viewer-loaded').remove();
    }
    // Force onchange to trigger any piping
    $('#form :input[name="'+this_field+'"]').trigger("change");
    var data = { s: getParameterByName('s'), id: doc_id, field_name: this_field, record: id, event_id: event_id, instance: instance,
        doc_version: version, doc_version_hash: version_hash };
    // Delete the value via AJAX
    $.post(delete_page+'&'+$.param(data),{ file_upload_delete_reason: file_upload_delete_reason },function(data) {
        // Set value to blank on form
        if (version == '') {
            $("#" + this_field + "-linknew").html(data);
            $("#" + this_field + "-link").hide();
            $('#' + this_field + '-sigimg').hide();
            setDataEntryFormValuesChanged(this_field);
            if (typeof REDCapFileEnhancements != 'undefined') REDCapFileEnhancements.removeFileUploadPreview(this_field);
        }
        // Display confirmation dialog
        var file_delete_dialog_id = 'file_delete_dialog';
        initDialog(file_delete_dialog_id);
        var message = version == '' ? 
            interpolateString(window.lang.form_renderer_57, [$("#" + this_field + "-link").text()]) :
            interpolateString(window.lang.form_renderer_58, [version]);
        $('#' + file_delete_dialog_id).html(message);
        simpleDialog(null, window.lang.form_renderer_59, file_delete_dialog_id);
        // Close dialog automatically with fade effect
        setTimeout(function(){
            if ($('#'+file_delete_dialog_id).hasClass('ui-dialog-content')) $('#'+file_delete_dialog_id).dialog('option', 'hide', {effect:'fade', duration: 500}).dialog('close');
            if ($('#data_history').hasClass('ui-dialog-content')) $('#data_history').dialog('destroy');
            // Destroy the dialog so that fade effect doesn't persist if reopened
            setTimeout(function(){
                if ($('#'+file_delete_dialog_id).hasClass('ui-dialog-content')) $('#'+file_delete_dialog_id).dialog('destroy');
                if (version != '') dataHist(this_field,event_id,lastDataHistWidth);
            },500);
        },2200);
        // If clicked a Missing Data Code, which deleted the file, then
        if (codeUpdateAfterDeleteFile != '' && $('#MDMenu div[code="'+codeUpdateAfterDeleteFile+'"]').length) {
            $('#MDMenu div[code="'+codeUpdateAfterDeleteFile+'"]').trigger('click');
        } else {
            $('#MDMenu').hide();
        }
        codeUpdateAfterDeleteFile = '';
        // Initialize any File Upload fields with @INLINE action tag
        initInlineImages(this_field);
    });
    // Trigger branching logic in case a "file" field is involved in branching
    doBranching(this_field);
    return true;
}

function stopUpload(success,this_field,doc_id,doc_name,doc_size,event_id,download_page,delete_page,doc_id_hash,instance,isSigField){
    var result = '';
    var study_id = getParameterByName('id');
    if (success == 1){
        if (typeof REDCapFileEnhancements != 'undefined') REDCapFileEnhancements.removeFileUploadPreview(this_field);
        $('#fileupload-container-'+this_field+' .inline-pdf-viewer-loaded').remove();
        try {
            if (typeof window.parent.lang.form_renderer_24 != 'undefined') {
                window.lang.form_renderer_24 = window.parent.lang.form_renderer_24;
                window.lang.form_renderer_43 = window.parent.lang.form_renderer_43;
                window.lang.form_renderer_25 = window.parent.lang.form_renderer_25;
                window.lang.data_entry_459 = window.parent.lang.data_entry_459;
                window.lang.form_renderer_60 = window.parent.lang.form_renderer_60;
                window.lang.dataqueries_160 = window.parent.lang.dataqueries_160;
                window.lang.global_03 = window.parent.lang.global_03;
                window.lang.form_renderer_61 = window.parent.lang.form_renderer_61;
            }
        } catch (e) { }
        if (typeof window.lang.form_renderer_24 == 'undefined') {
            window.lang.form_renderer_24 = 'Remove file';
            window.lang.form_renderer_43 = 'Remove signature';
            window.lang.form_renderer_25 = 'Send-It';
            window.lang.data_entry_459 = 'Upload new version';
            window.lang.form_renderer_60 = 'File was successfully uploaded!';
            window.lang.dataqueries_160 = 'File was successfully uploaded!';
            window.lang.global_03 = 'NOTICE';
            window.lang.form_renderer_61 = 'In order to use Send-It with this file, the current web page must first be saved by clicking the button at the bottom of the page.';
        }
        var sigimg = $('#'+this_field+'-sigimg');
        result = '<div style="font-weight:bold;font-size:14px;text-align:center;color:green;"><br><i class="fas fa-check"></i> <span data-rc-lang="form_renderer_60">' + window.lang.form_renderer_60 + '</span></div>';
        document.getElementById(this_field+"-link").style.display = 'block';
        var doc_name_full = doc_name;
        document.getElementById(this_field+"-link").title = htmlspecialchars(doc_name_full);
        doc_name = truncate_filename(doc_name, 34);
        document.getElementById(this_field+"-link").innerHTML = '<span class="fu-fn" vf="'+htmlspecialchars(doc_name_full)+'">'+doc_name+'</span>'+doc_size;
        document.getElementById(this_field+"-link").href = download_page+"&doc_id_hash="+doc_id_hash+"&id="+doc_id+"&s="+getParameterByName('s')+"&record="+htmlspecialchars(study_id)+"&page="+getParameterByName('page')+"&event_id="+event_id+"&field_name="+this_field+"&instance="+instance;
        $('#'+this_field+"-link").attr('onclick', "return appendRespHash('"+this_field+"');");
        var newlinktext = '<a href="javascript:;" class="deletedoc-lnk" style="font-size:10px;color:#C00000;" onclick=\'deleteDocumentConfirm('+doc_id+',"'+this_field+'","'+htmlspecialchars(study_id)+'",'+event_id+','+instance+',"'+delete_page+'&__response_hash__="+$("#form :input[name=__response_hash__]").val());return false;\'><i class="far fa-trash-alt me-1"></i><span data-rc-lang="' + (isSigField ? 'form_renderer_43' : 'form_renderer_24') + '">'+ (isSigField ? window.lang.form_renderer_43 : window.lang.form_renderer_24) +'</span></a>';
        if (sendit_enabled) {
            newlinktext += "<span class=\"sendit-lnk\"><span style=\"font-size:10px;padding:0 10px;\">or</span><a onclick=\"simpleDialog(window.lang.form_renderer_61,window.lang.global_03);return false;\" href=\"javascript:;\" style=\"font-size:10px;\"><i class=\"far fa-envelope me-1\"></i><span data-rc-lang=\"form_renderer_25\">"+window.lang.form_renderer_25+"</span></a>&nbsp;</span>";
        }
        if (file_upload_versioning_enabled && !sigimg.length) {
            newlinktext = '<a href="javascript:;" style="font-size:10px !important;color:green;" class="fileuploadlink" '
                + 'onclick="filePopUp(\''+this_field+'\',0,1);return false;"><i class="fas fa-upload me-1"></i><span data-rc-lang="data_entry_459">'+window.lang.data_entry_459+'</span></a>'
                + '<span style="font-size:10px;padding:0 10px;">or</span>' + newlinktext;
        }
        document.getElementById(this_field+"-linknew").innerHTML = newlinktext;
        eval("document.form."+this_field+".value = '"+doc_id+"';");
        // If a signature field, then add inline image
        if (sigimg.length) {
            sigimg.show().html('<img src="'+download_page.replace('file_download.php','image_view.php')+"&doc_id_hash="+doc_id_hash+"&id="+doc_id+"&s="+getParameterByName('s')+"&record="+htmlspecialchars(study_id)+"&page="+getParameterByName('page')+"&event_id="+event_id+"&instance="+instance+"&field_name="+this_field+'&signature=1" alt="Signature">');
        }
        addFileEnhancements();
    } else {
        result = '<div style="font-weight:bold;color:#C00000;margin-top:15px;font-size:14px;text-align:center;"><span data-rc-lang="dataqueries_160">' + window.lang.dataqueries_160 + '</div>';
    }
    document.getElementById('f1_upload_form').style.display = 'block';
    document.getElementById('f1_upload_form').innerHTML = result;
    document.getElementById('f1_upload_process').style.display = 'none';
    // Close dialog automatically with fade effect
    if ($("#file_upload").hasClass('ui-dialog-content')) {
        if (success == 1) {
            // If this is a signature field, then close dialog immediately
            if ($('#'+this_field+'-sigimg').length) {
                $('#file_upload').dialog('destroy');
                if (inIframe()) {
                    var urlparts = window.location.href.split('#');
                    window.location.href = urlparts[0]+'#'+this_field+'-tr';
                }
            } else {
                $('#file_upload').dialog('option', 'buttons', { "Close": function() { $(this).dialog("destroy"); } });
                setTimeout(function(){
                    if ($("#file_upload").hasClass('ui-dialog-content')) $('#file_upload').dialog('option', 'hide', {effect:'fade', duration: 200}).dialog('close');
                    // Destroy the dialog so that fade effect doesn't persist if reopened
                    setTimeout(function(){
                        if ($("#file_upload").hasClass('ui-dialog-content')) $('#file_upload').dialog('destroy');
                    },200);
                    if (inIframe()) {
                        var urlparts = window.location.href.split('#');
                        window.location.href = urlparts[0]+'#'+this_field+'-tr';
                    }
                },1500);
            }
        } else {
            $('#file_upload').dialog('option', 'buttons', { "Close": function() { $(this).dialog("destroy"); },
                "Try again": function() { $('#file_upload').dialog('destroy'); $('#'+this_field+'-linknew a.fileuploadlink').trigger('click'); } });
        }
    }
    // Force onchange to trigger any piping
    $('#form :input[name="'+this_field+'"]').trigger("change");
    // Trigger branching logic in case a "file" field is involved in branching
    calculate(this_field);
    doBranching(this_field);
    return true;
}

// Obtain the base64 data from a signature File Upload field
function saveSignature() {
    // Make sure we have a signature first (bypass this for IE8 and lower or iOS 6 and lower because of some strange issue)
    if ($('#f1_upload_form input[name="myfile_base64_edited"]').val() == '0' && !((isIOS && iOSv <= 6))) {
        simpleDialog(window.lang.form_renderer_46, window.lang.global_01, null, 300);
        return false;
    }
    $('#signature-div, #signature-div-actions').hide();
    $('#f1_upload_form').show();
    var data = $('#signature-div').jSignature('getData', 'default');
    $('#f1_upload_form input[name="myfile_base64"]').val( data.substring(data.indexOf(',')+1) );
    $('form#form_file_upload').trigger('submit');
}

function startUpload(){
    // If didn't select a file, give an error msg
    var isSignature = ($('#f1_upload_form input[name="myfile_base64"]').val().length > 0);
    var missingFile = (!isSignature && $('#f1_upload_form input[name="myfile"]').val().length + $('#f1_upload_form input[name="myfile_base64"]').val().length == 0);
    if (!isSignature && missingFile) {
        simpleDialog(window.lang.form_renderer_47, window.lang.global_01, null, 300);
        return false;
    } else {
        if (!isSignature && !fileTypeAllowed(basename($('#f1_upload_form input[name="myfile"]').val()))) {
            Swal.fire(window.lang.docs_1136, '', 'error');
            return false;
        }
        document.getElementById('f1_upload_process').style.display = 'block';
        document.getElementById('f1_upload_form').style.display = 'none';
        return true;
    }
}

// Truncate a file name to X characters while still maintaining the file extension
function truncate_filename(filename, charLimit, truncateMarkFromEnd)
{
    if (typeof truncateMarkFromEnd == 'undefined') truncateMarkFromEnd = 9;
    var origLength = filename.length;
    if (origLength > charLimit) {
        filename = trim(filename.substr(0, charLimit - truncateMarkFromEnd))+"..."+trim(filename.substr(origLength - truncateMarkFromEnd));
    }
    return filename;
}

//For individual field File uploads
function filePopUp(field_name, signature, replace_version) {
    // Return if field validation errors exists
    if ($('#field_validation_error_state').val() == '1')  return false;

    // Reset value of hidden field used to determine if signature was signed
    $('#f1_upload_form input[name="myfile_base64_edited"]').val('0');
    $('#f1_upload_form input[name="myfile_replace"]').val(replace_version);
    // Set dialog content, etc.
    document.getElementById('file_upload').innerHTML = getFileUploadWinHTML();
    document.getElementById('field_name').value = field_name+'-linknew';
    // Dialog
    // var label = $('#label-'+field_name).clone();
    // label.find('.requiredlabel').remove();
    // label.find('#MDMenu').remove();
    // $("#field_name_popup").html(trim(label.text()));
    var labelText = $('#label-'+field_name+' div[data-kind=field-label]').html()
    $("#field_name_popup").html(trim(labelText));
    var dlgtitle = (signature == 1 ? window.lang.form_renderer_31 : (replace_version == 1 ? (window.lang.data_entry_459 + ' ' + window.lang.data_entry_468) : window.lang.form_renderer_23));
    $('#file_upload').dialog({ title: dlgtitle, bgiframe: true, modal: true, width: (isMobileDevice ? $('#questiontable').width() : 500) });
    // Signature?
    if (signature == 1) {
        $('#signature-div, #signature-div-actions').show();
        $('#f1_upload_form').hide();
        $('#signature-div').jSignature();
    } else {
        $('#signature-div, #signature-div-actions').hide();
        // Since iOS (v5.1 and below) devices do not support file uploading on webpages in Mobile Safari, give note to user about this.
        if (isIOS && iOSv <= 5) {
            $('#this_upload_field').hide();
            $('#f1_upload_form').html("<p style='color:red;'><b>" + window.lang.form_renderer_44 + "</b><br>" + window.lang.form_renderer_45 + "</p>");
        } else {
            $('#f1_upload_form').show();
        }
    }
    // In case any unsaved data from the form needs to be piped into the label in the dialog, manually trigger onblur for the field(s)
    $('#file_upload .piping_receiver').each(function(){
        // Get class that begins with "piperec"
        var classList = $(this).attr('class').split(/\s+/);
        for (var i = 0; i < classList.length; i++) {
            classList[i] = trim(classList[i]);
            if (classList[i].indexOf('piperec') === 0) {
                var evtRec = classList[i].split('-');
                // If the event_id is the current event_id of this form
                if (evtRec[1] == event_id) {
                    // Trigger onblur/change/click of the field (cover all the bases, even radio elements)
                    if ($('form#form [name="'+evtRec[2]+'___radio"]').length) {
                        $('form#form [name="'+evtRec[2]+'___radio"]:checked').trigger('click');
                    } else if ($('form#form [name="'+evtRec[2]+'"]').prop("tagName").toLowerCase() == 'select') {
                        $('form#form [name="'+evtRec[2]+'"] option:selected').trigger('change');
                    } else {
                        $('form#form [name="'+evtRec[2]+'"]').trigger('blur');
                    }
                }
            }
        }
    });
    // Set any embedded fields from the field label as read-only to prevent confusion
    $('#file_upload .rc-field-embed :input:visible').prop('disabled', true).css('background-color','#f5f5f5');
}

//For unchecking radio buttons
function uncheckRadioGroup (radioButtonOrGroup) {
    if (radioButtonOrGroup.length) { // we have a group
        for (var b = 0; b < radioButtonOrGroup.length; b++)
            if (radioButtonOrGroup[b].checked) {
                radioButtonOrGroup[b].checked = false;
                break;
            }
    }
    else
        try{radioButtonOrGroup.checked = false}catch(err){};
}

// Append hidden input to Data Entry Form (i.e. form#form)
function appendHiddenInputToForm(name,val) {
    $('form#form').append('<input type="hidden" value="'+val+'" name="'+name+'">');
}

// Enable green row highlight for data entry form table
function enableDataEntryRowHighlight() {
    $('form#form #questiontable :input, form#form #questiontable a')
        .bind('click focus select', function(event){
            // If save buttons are not displayed (e.g., form is locked), then don't highlight row
            if ($('#__SUBMITBUTTONS__-div').css('display') == 'none') return;
            // Exclude if clicked the Data History and balloon icons for this field
            if ($(this).has('img').length) return;
            // Obtain type of html tag source that triggered this event
            var targetTag = event.target.nodeName.toLowerCase();
            // Exclude "reset" links for radios (unless directly clicked)
            if ($(this).hasClass('cclink') && event.type != 'click') return;
            // Exclude text input, textarea, and drop-down click because it would have already been triggered by focus
            if (event.type == 'click' && (targetTag == 'textarea' || targetTag == 'select'
                || (targetTag == 'input' && $(event.target).attr('type') == 'text'))) return;
            // Skip over calc fields
            if (targetTag == 'input' && $(event.target).attr('type') == 'text' && $(event.target).attr('readonly') == 'readonly') return;
            // Find row element
            var tr = $(this).closest('tr');
            // Go up one or two levels if table nested within table
            if (tr.attr('sq_id') == null) tr = tr.parent().closest('tr');
            if (tr.attr('sq_id') == null) tr = tr.parent().closest('tr');
            // If could not find the row element, then stop
            if (tr.attr('sq_id') == null || tr.attr('id') == null || tr.attr('id').indexOf('-sh-tr') > -1) return;
            // Do green highlight on row
            var containsEmbeddedFields = $(this).parentsUntil('tr[sq_id]').parent().find('.rc-field-embed').length;
            var isEmbeddedResetLink = (targetTag == 'a' && $(this).hasClass('smalllink') && $(this).parents('span.rc-field-embed').length);
            var isEmbeddedField = (typeof $(this).attr('name') != 'undefined' && ($('.rc-field-embed[var="'+$(this).attr('name')+'"]').length
                                    || $('.rc-field-embed[var="'+$(this).attr('name').replace('___radio','')+'"]').length
                                    || $('.rc-field-embed[var="'+$(this).attr('name').replace('__chkn__','')+'"]').length));
            var triggerIsTodayNowBtn = (targetTag == 'button' && $(this).hasClass('today-now-btn'));
            var thisDoGreenHighlight = (!isEmbeddedResetLink && !isEmbeddedField && !triggerIsTodayNowBtn && !containsEmbeddedFields);
            if (thisDoGreenHighlight) doGreenHighlight(tr);
            // Add custom "Save and Open Query Popup" button
            if (data_resolution_enabled == '2' && !data_locked) {
                // Get field name
                var fieldname = $(this).attr('name');
                // Has icons?
                if ($('#dc-icon-'+fieldname).length) {
                    var iconHtml = $('#dc-icon-' + fieldname).parent().html();
                    var hasExclRedIcon = (iconHtml.indexOf('balloon_exclamation.gif') > -1);
                    var hasExclBlueIcon = (iconHtml.indexOf('balloon_exclamation_blue.gif') > -1);
                    if (hasExclRedIcon || hasExclBlueIcon) {
                        // Add content to tooltip
                        $('#tooltipDRWsave').html('<div style="padding:12px 0 0 8px;overflow:hidden;">' +
                            '<button name="submit-btn-saverecord" class="jqbuttonmed" onclick="appendHiddenInputToForm(\'scroll-top\',\'' + ($(window).scrollTop()) + '\');appendHiddenInputToForm(\'dqres-fld\',\'' + fieldname + '\');dataEntrySubmit(this);return false;">' +
                            '<img src="' + app_path_images + 'balloon_exclamation' + (hasExclBlueIcon ? '_blue' : '') + '.gif"> Save and then open <br>Data Resolution Pop-up</button>' +
                            '</div>');
                        // Buttonize the Save&Open Popup button
                        $('#tooltipDRWsave button').button();
                        // Open tooltip	to right of field
                        $('#tooltipDRWsave').show().position({
                            my: "left center",
                            at: "right+20 center",
                            of: this
                        });
                    } else {
                        $('#tooltipDRWsave').hide();
                    }
                }
            }
        });
}

// Highlight a form/survey table row with green background color
function doGreenHighlight(rowob) {
    // Reset bgcolor for all rows in case others are highlighted
    $('form#form #questiontable tr td.greenhighlight').removeClass('greenhighlight');
    // If found the row element, highlight all cells
    rowob.children("td").each(function() {
        $(this).addClass('greenhighlight');
        if ($(this).hasClass('labelmatrix')) {
            $(this).find('table tr td.data_matrix, table.mtxchoicetablechk tr td.data, table.mtxchoicetable tr td.data')
                .addClass('greenhighlight');
        }
    });
}

// Run when click the "reset value" for radio button fields
function radioResetVal(field,form) {
    // Set flag to deal with choices embedded in other choices
    if (justClickedEnhancedChoice) return;
    justClickedEnhancedChoice = true;
    setTimeout("justClickedEnhancedChoice = false;", 50);
    // Put everything inside a timeout due to issues with embedded radio fields
    setTimeout(function(){
        var currentVal = $('form[name="'+form+'"] input[name="'+field+'"]').val() ?? '';
        if (currentVal == '') return;
        $('form[name="'+form+'"] input[name="'+field+'___radio"]').prop('checked',false);
        $('form[name="'+form+'"] input[name="'+field+'"]').val('');
        if (form == 'form') {
            // If using Enhanced Choices for radios, then deselect it
            if ($('.rc-field-embed[var="'+field+'"]').length) {
                $('.rc-field-embed[var="'+field+'"] div.enhancedchoice label.selectedradio').removeClass('selectedradio');
            } else {
                $('#'+field+'-tr div.enhancedchoice label.selectedradio').removeClass('selectedradio');
            }
            // Piping: Transmit blank value to all piping receiver spans
            if (event_id != null) {
                $('.piping_receiver.piperec-'+event_id+'-'+field+', .piping_receiver.piperec-'+event_id+'-'+field+'-label, .piping_receiver.piperec-'+event_id+'-'+field+'-value').html('______');
                $('.piping_receiver.piperec-'+event_id+'-'+field+'.pipingrec-hideunderscore, .piping_receiver.piperec-'+event_id+'-'+field+'-label.pipingrec-hideunderscore, .piping_receiver.piperec-'+event_id+'-'+field+'-value.pipingrec-hideunderscore').html('');
                // Update drop-down options separately via ajax
                try{ updatePipingDropdowns(field,''); } catch(e) { }
            }
            setDataEntryFormValuesChanged(field);
            // If field has missing data code, then reset it
            removeFieldMissingDataCode(field);
            removeHasVal(field);
            // Branching logic and calculations
            try { calculate(field);doBranching(field); } catch(e){ }
        }
    },10);
    // Set vars used in Missing Data Codes
    if (typeof fieldToUpdate != 'undefined') {
        fieldName = field;
        fieldToUpdate = $('[name=' + fieldName + ']');
        qtype = 'radio';
    }
    return false;
}

// Check if value is unique
function checkSecondaryUniqueField(ob)
{
    // MLM active? Wait for it to be initialized before proceeding with the check
    // (otherwise, the displayed message will not be translated because MLM may 
    // not have had a chance to run yet).
    if (typeof REDCap['MultiLanguage'] != 'undefined' && REDCap['MultiLanguage'].isInitialized() == false) {
        setTimeout(function(){ checkSecondaryUniqueField(ob); }, 100);
        return;
    }
    var instance = getParameterByName('instance');
    if (instance == '') instance = '1';
    // Disable all form buttons temporarily
    $('#formSaveTip input[type="button"], #form input[type="button"], #form :input[name="'+secondary_pk+'"]').prop('disabled', true);
    // Init values
    var url_base = 'DataEntry/check_unique_ajax.php';
    if (page == 'surveys/index.php') {
        // Survey page
        var record = ((document.form.__response_hash__.value == '') ? '' : $('#form :input[name="'+table_pk+'"]').val());
        var url = app_path_webroot_full+page+'?s='+getParameterByName('s')+'&__passthru='+encodeURIComponent(url_base);
    } else {
        // Data entry page
        var record = ((document.form.hidden_edit_flag.value == '0') ? '' : getParameterByName('id'));
        var url = app_path_webroot+url_base+'?page='+getParameterByName('page');
    }
    // If SUF is not visible, then do not do anything
    if (!$('#form :input[name="'+secondary_pk+'"]:visible').length) return;
    // Check the SUF's value
    ob.val( trim(ob.val()) );
    if (ob.val().length > 0) {
        // Make ajax request
        $.ajax({
            url: url,
            type: 'GET',
            data: { pid: pid, field_name: secondary_pk, event_id: event_id, record: record, instance: instance, value: ob.val() },
            async: false,
            success:
                function(data){
                    if (data.length == 0) {
                        alert(woops);
                        setTimeout(function () { ob.focus() }, 1);
                    } else if (data != '0') {
                        if (page == 'surveys/index.php') {
                            simpleDialog(interpolateString(lang.data_entry_575, [ob.val()]), lang.data_entry_105, 'suf_warning_dialog',500,"$('#form :input[name="+secondary_pk+"]').focus();", lang.calendar_popup_01);
                        } else {
                            simpleDialog(interpolateString(lang.data_entry_576, [secondary_pk, ob.val()]), lang.data_entry_105, 'suf_warning_dialog',500,"$('#form :input[name="+secondary_pk+"]').focus();", lang.calendar_popup_01);
                        }
                        ob.css('font-weight','bold');
                        ob.css('background-color','#FFB7BE');
                        // If this is a DDP project and the DDP "preview data" dialog is displayed, close it
                        if ($('#rtws_idfield_new_record_warning').length && $('#rtws_idfield_new_record_warning').hasClass('ui-dialog-content')) {
                            $('#rtws_idfield_new_record_warning').dialog('close');
                        }
                    } else {
                        ob.css('font-weight','normal');
                        ob.css('background-color','#FFFFFF');
                    }
                    // Enable all form buttons again
                    $('#formSaveTip input[type="button"], #form input[type="button"], #form :input[name="'+secondary_pk+'"]').prop('disabled', false);
                }
        });
    } else {
        // Enable all form buttons again
        $('#formSaveTip input[type="button"], #form input[type="button"], #form :input[name="'+secondary_pk+'"]').prop('disabled', false);
    }
}

// Initialize any File Upload fields with @INLINE action tag
var valid_image_suffixes = new Array('jpeg','jpg','jpe','gif','png','tif','tiff','bmp','webp','svg');
function initInlineImages(currentfield)
{
    if (typeof currentfield == 'undefined') currentfield = '';
    var fieldEmbedded = false;
    if (currentfield == '') {
        // Multiple fields
        var selector = "#questiontable tr.\\@INLINE a.filedownloadlink, #questiontable .rc-field-embed.file-upload-inline-embed a.filedownloadlink";
    } else if ($("#questiontable .rc-field-embed[var='"+currentfield+"']").length) {
        // Single field embedded
        var selector = "#questiontable .rc-field-embed[var='"+currentfield+"'] a.filedownloadlink";
    } else {
        // Single field non-embedded
        var selector = "#questiontable tr#"+currentfield+"-tr a.filedownloadlink";
    }
    var usleep = 0;
    // Loop through one or more images to embed
    $(selector).each(function(){
        // Attributes
        var src = $(this).attr('href').replace('DataEntry/file_download.php','DataEntry/image_view.php')
                                      .replace('DataEntry%2Ffile_download.php','DataEntry%2Fimage_view.php'); // Change to image_view.php
        src += "&usleep="+usleep;
        var field = $(this).attr('name');
        var filename = $(this).find('.fu-fn').attr('vf');
        var fileext = getfileextension(filename.toLowerCase());
        var td = $("#questiontable .rc-field-embed[var='"+field+"']").length ? $("#questiontable .rc-field-embed[var='"+field+"']") : $("#questiontable tr#"+field+"-tr>td:last");
        var maxwidth = td.width();
        var isImage = in_array(fileext, valid_image_suffixes);
        var isPdf = (fileext == 'pdf');
        var dim = $('input[type="hidden"][name="'+field+'"]').attr('inlinedim');
        if (typeof dim == 'undefined') {
            dim = new Array();
        } else {
            dim = (dim.indexOf(',') > -1) ? dim.split(',') : new Array(dim);
        }
        var width = (dim.length > 0) ? "width:"+dim[0]+(isNumeric(dim[0]) ? "px" : "")+";" : "";
        var height = (dim.length > 1) ? "height:"+dim[1]+(isNumeric(dim[1]) ? "px" : "")+";" : "";
        // Decide action to take
        var action = true;
        if ($(this).css('display') == 'none' || (!isImage && !isPdf)) {
            // If file was removed, then remove embedded image too
            $(this).parent().find('.file-upload-inline').remove();
            action = false;
        } else if ((isPdf && td.find('iframe.file-upload-inline').length) || (isImage && td.find('img.file-upload-inline').length)) {
            // Update src attribute if embedded PDF/image already exists on page
            td.find('object.file-upload-inline').attr('data', src);
            td.find('iframe.file-upload-inline, img.file-upload-inline').attr('src', src);
            td.find('img.file-upload-inline').attr('alt', filename);
        } else if (isPdf) {
            // Remove in case already existed as other tag type
            $(this).parent().find('.file-upload-inline').remove();
            // Add iframe for embedded PDF
            if (height == "") height = "height:300px;";
            // const $pdf = $("<object data='"+src+"' data-file-id='" + getFileId(field) + "' class='file-upload-inline' type='application/pdf' style='width:100%;"+width+height+"max-width:"+maxwidth+"px;'><iframe class='file-upload-inline' src='"+src+"' style='width:100%;border:none;max-width:"+maxwidth+"px;"+height+"'></iframe></object>");
            const $pdf = $(renderInlinePdfContainer(src));
            $(this).before($pdf);
            addFileEnhancements();
            initInlinePdfs();
        } else if (isImage) {
            // Remove in case already existed as other tag type
            $(this).parent().find('.file-upload-inline').remove();
            // Add img tag for embedded image
            $(this).before('<img src="'+src+'" class="file-upload-inline" style="'+width+height+'max-width:'+maxwidth+'px;" alt="'+htmlspecialchars(filename)+'">');
        } else {
            action = false;
        }
        if (action) {
            usleep += 100000;
        }
    });
}

// Auto-fill the form/survey
function autoFill()
{
    $("tr[sq_id], .rc-field-embed[var]").each(function(){
        if ($(this).is(':visible')) {
            $('html, body').animate({scrollTop: $(this).offset().top},1);
            autoFillRow(this);
            var field_name = $(this).attr($(this).hasClass('rc-field-embed') ? 'var' : 'sq_id');
            if (field_name != '{}' && field_name != '') {
                doBranching(field_name);
            }
            setDataEntryFormValuesChanged(field_name);
        }
    });
}
function autoFillRow(tr)
{
    var date_types = new Array('date_ymd', 'date_mdy', 'date_dmy', 'datetime_ymd', 'datetime_mdy', 'datetime_dmy',
                                'datetime_seconds_ymd', 'datetime_seconds_mdy', 'datetime_seconds_dmy', 'time', 'time_mm_ss', 'time_hh_mm_ss');

    // Select a dropdown value
    var options = $(tr).find('option:not([value=""])');
    if (options.length > 0) {
        var randomnumber = Math.floor(Math.random() * options.length);
        $(options[randomnumber]).prop('selected', true);
        if (options.parent().hasClass('rc-autocomplete')) {
            $(':input#rc-ac-input_'+ options.parent().prop('name')).val( $(options[randomnumber]).html() ).trigger('blur');
        }
        return;
    }

    // Check checkboxes
    var checkboxes = $(tr).find("input[type=checkbox]").filter(":visible").filter(":not([id='__LOCKRECORD__'])");
    var enhancedchoice = $(tr).find("div.enhancedchoice label.selectedchkbox, div.enhancedchoice label.unselectedchkbox").filter(":visible");
    var checkboxes_checked = $('input:checked',tr);
    if (checkboxes.length > 0 && checkboxes_checked.length == 0 && enhancedchoice.length == 0) {
        var randomnumber = Math.floor(Math.random() * checkboxes.length);
        $(checkboxes[randomnumber]).trigger('click');
        return;
    }
    // Check for enhanced checkboxes
    var enhancedCheckSelected = $(tr).find("div.enhancedchoice label.selectedchkbox").filter(":visible").length;
    if (enhancedchoice.length) {
        if (enhancedCheckSelected >= 1) return;
        var randomnumber = Math.floor(Math.random() * enhancedchoice.length);
        $(tr).find("div.enhancedchoice label:eq("+randomnumber+")").trigger('click').removeClass('unselectedchkbox').addClass('selectedchkbox');
        return;
    }

    // Check a random radio button (skip checked radios)
    var radios = $(tr).find("input[type=radio]").filter(":visible");
    var radios_checked = $(tr).find("input[type=radio]:checked");
    if ((radios.length > 0) && radios_checked.length == 0) {
        var randomnumber = Math.floor(Math.random() * radios.length);
        radios[randomnumber].checked = true;
        $(radios[randomnumber]).trigger('click').trigger('blur');
        return;
    }
    // Check for enhanced radios
    var enhancedchoice = $(tr).find("div.enhancedchoice").filter(":visible");
    if (enhancedchoice.length > 0 && radios_checked.length == 0) {
        var randomnumber = Math.floor(Math.random() * enhancedchoice.length);
        $(tr).find("div.enhancedchoice:eq("+randomnumber+") label:first").trigger('click').addClass('selectedradio');
        return;
    }

    // Set sliders
    var sliders = $(tr).find("div.slider:first");
    if (sliders.length) {
        sliders.trigger('mousedown');
        return;
    }

    // Handle text inputs
    var inputs = $(tr).find("input[type=text]").each(function(i,e){
        // Skip ones with existing values
        if ($(e).val() !== "") return;

        // Check for field-validation attribute
        var fv = (typeof $(e).attr('fv') == 'undefined') ? '' : $(e).attr('fv');
        var min='', max='';

        if (fv == 'email') {
            $(e).val('test@noreply.com');
        } else if (fv == 'integer') {
            b = $(e).attr('onblur');
            parts = b.replace(/'/g,'').split(',');
            if (typeof parts[1] != 'undefined' && parts[1] != '') {
                min = parts[1];
            }
            if (typeof parts[2] != 'undefined' && parts[2] != '') {
                max = parts[2];
            }
            val = autoFillRandomIntFromInterval(min, max);
            $(e).val(val);
        } else if (in_array(fv, date_types)) {
            b = $(e).attr('onblur');
            parts = b.replace(/'/g,'').split(',');
            if (typeof parts[1] != 'undefined' && parts[1] != '') {
                // If has min range check
                if (parts[1] === 'now') {
                    parts[1] = getCurrentDate(fv.replace(/_dmy/,'_ymd').replace(/_mdy/,'_ymd'))+' '+currentTime('both',(fv.indexOf('_seconds') > -1));
                } else if (parts[1] === 'today') {
                    parts[1] = getCurrentDate(fv.replace(/_dmy/,'_ymd').replace(/_mdy/,'_ymd'));
                }
                // Convert from ymd to mdy/dmy, if needed
                if (fv.indexOf('_mdy') > -1) {
                    parts[1] = date_ymd2mdy(parts[1]);
                } else if (fv.indexOf('_dmy') > -1) {
                    parts[1] = date_ymd2dmy(parts[1]);
                }
                $(e).val(parts[1]);
            } else if (typeof parts[2] != 'undefined' && parts[2] != '') {
                // If has max range check
                if (parts[2] === 'now') {
                    parts[2] = getCurrentDate(fv.replace(/_dmy/,'_ymd').replace(/_mdy/,'_ymd'))+' '+currentTime('both',(fv.indexOf('_seconds') > -1));
                } else if (parts[2] === 'today') {
                    parts[2] = getCurrentDate(fv.replace(/_dmy/,'_ymd').replace(/_mdy/,'_ymd'));
                }
                // Convert from ymd to mdy/dmy, if needed
                if (fv.indexOf('_mdy') > -1) {
                    parts[2] = date_ymd2mdy(parts[2]);
                } else if (fv.indexOf('_dmy') > -1) {
                    parts[2] = date_ymd2dmy(parts[2]);
                }
                $(e).val(parts[2]);
            } else {
                if (($(e).parent().find("button[onclick^='set']").length > 0)) {
                    $(e).parent().find("button").trigger('click');
                } else {
                    switch(fv) {
                        case "date_ymd":
                            $(e).val(today);
                            break;
                        case "date_mdy":
                            $(e).val(today_mdy);
                            break;
                        case "date_dmy":
                            $(e).val(today_dmy);
                            break;
                        case "datetime_ymd":
                            $(e).val(today + " 00:00");
                            break;
                        case "datetime_mdy":
                            $(e).val(today_mdy + " 00:00");
                            break;
                        case "datetime_dmy":
                            $(e).val(today_dmy + " 00:00");
                            break;
                        case "datetime_seconds_ymd":
                            $(e).val(today + " 00:00:00");
                            break;
                        case "datetime_seconds_mdy":
                            $(e).val(today_mdy + " 00:00:00");
                            break;
                        case "datetime_seconds_dmy":
                            $(e).val(today_dmy + " 00:00:00");
                            break;
                        case "time":
                            $(e).val('12:34');
                            break;
                        case "time_mm_ss":
                            $(e).val('34:57');
                            break;
                        case "time_hh_mm_ss":
                            $(e).val('12:34:57');
                            break;
                    }
                }
            }
        } else if (fv.indexOf('number') === 0) {
            b = $(e).attr('onblur');
            parts = b.replace(/'/g,'').split(',');
            // Apply min/max if defined, else generate a value
            if (typeof parts[1] != 'undefined' && parts[1] != '') {
                if (fv == 'number_comma_decimal' || ends_with(fv,'dp_comma_decimal')) {
                    // If a comma decimal number, compensate for the fact that we're parsing the validation function by comma
                    parts[1] = parts[1]+','+parts[2];
                }
                $(e).val(parts[1]);
            } else if (typeof parts[2] != 'undefined' && parts[2] != '') {
                if (fv == 'number_comma_decimal' || ends_with(fv,'dp_comma_decimal')) {
                    // If a comma decimal number, compensate for the fact that we're parsing the validation function by comma
                    parts[2] = parts[2]+','+parts[3];
                }
                $(e).val(parts[2]);
            } else {
                if (fv == 'number') {
                    $(e).val('3.14');
                } else if (fv == 'number_comma_decimal') {
                    $(e).val('3,14');
                } else if (fv.indexOf('number_') === 0 && (ends_with(fv,'dp') || ends_with(fv,'dp_comma_decimal'))) {
                    var comma = ends_with(fv,'dp_comma_decimal') ? "," : ".";
                    var numDecimals = fv.substring(7).replace('dp','').replace('_comma_decimal','');
                    var thisVal = '3'+comma;
                    for (var i=0;i<numDecimals;i++) thisVal += '4';
                    $(e).val(thisVal);
                } else {
                    $(e).val('3');
                }
            }
        } else if (fv == 'zip' ) {
            $(e).val('55112');
        } else if (fv == 'zipcode' ) {
            $(e).val('55112');
        } else if (fv == 'phone') {
            $(e).val('(555) 867-5309');
        } else if (fv == "time_shorthand") {
            $(e).val('1122');
        } else if (fv == "lab_value") {
            $(e).val('123');
        } else {
            // Get a random word
            $(e).val(autoFillGetRandomWord());
        }
        $(e).trigger('blur');
        return;
    });

    // Set textarea
    var textarea = $(tr).find('textarea');
    if (textarea.length && textarea.val() == '') {
        textarea.val(autoFillGetRandomWord());
        return;
    }
}
function autoFillGetRandomWord() {
    var random_words = ['Rock', 'Paper', 'Scissors'];
    return random_words[Math.floor(Math.random()*random_words.length)];
}
function autoFillRandomIntFromInterval(min, max) { // min and max included
    if (max == '') max = 9999;
    if (min == '') min = -9999;
    min = min*1;
    max = max*1;
    return Math.floor(Math.random() * (max - min + 1) + min);
}

//#region File Enhancements (PDF and image preview)

/** Adds enhancements to inline PDFs, such as resizing, min/max, etc. */
function addFileEnhancements() {
    initFileEnhancements();
    setTimeout(() => {
        // Add previews
        $('[data-file-preview]').each(function() {
            try {
                window.REDCapFileEnhancements.addPreview(this);
            }
            catch (ex) {} // Ignore
        })
        $('.fileupload-container[data-file-type="file"][data-can-preview="1"]').each(function() {
            try {
                window.REDCapFileEnhancements.addFileUploadPreview(this);
            }
            catch (ex) { console.error(ex);} // Ignore
        })
        // Add resizers
        $('object[type="application/pdf"], .inline-pdf-viewer').each(function() {
            try {
                window.REDCapFileEnhancements.addResizer(this);
            }
            catch (ex) {} // Ignore
        });
        // Cleanup
        $('.inline-pdf-resizer').each(function() {
            window.REDCapFileEnhancements.cleanup(this);
        })
    }, 0);
}

/** Initializes PDF enhancements */
function initFileEnhancements(debug = false) {
    if (typeof window.REDCapFileEnhancements == 'undefined') window.REDCapFileEnhancements = (() => {
        const body = document.getElementsByTagName('body')[0];
        const minH = 50;
        const pdfs = {};
        let current = false;
        const config = {
            debug: debug
        };
        //#region Preview
        /** @param {HTMLElement} fileUpload */
        function addFileUploadPreview(fileUpload) {
            const $a = $(fileUpload).find('a.filedownloadlink');
            const fileSrc = $a.attr('href').replace('file_download.php', 'image_view.php');
            const fieldName = $a.attr('name');
            if (!fileSrc || $a.attr('data-file-initialized') == '1') return; // No file or already initialized
            const fileId = getFileId(fieldName);
            const fileName = $a.find('span[vf]').attr('vf');
            const fileExt = getfileextension(fileName.toLowerCase());
            if (!(valid_image_suffixes.includes(fileExt) || fileExt == 'pdf')) return; // Not supported
            $a.attr('data-file-initialized', '1');
            $a.attr('data-file-id', fileId);
            $a.attr('data-file-type', fileExt == 'pdf' ? 'pdf' : 'img')
            const $btn = $('<div class="file-preview-button-container">\
                <button class="btn btn-secondary btn-xs rc-preview-file" type="button">\
                    <i class="fa-solid fa-magnifying-glass show-preview"></i>\
                    <i class="fa-solid fa-xmark hide-preview"></i>\
                    <span class="visually-hidden" data-rc-lang="data_entry_605">' +
                        window.lang.data_entry_605 + '\
                    </span>\
                </button></div>');
            $btn.attr('data-file-id', fileId);
            $('#'+fieldName+'-linknew').after($btn);
            if (config.debug) console.log('File Upload Preview:', fileUpload);
            $btn.on('click', function() {
                // Viewer already there?
                let viewerContainer = document.querySelector('.preview-container[data-file-id="' + fileId + '"]');
                if (viewerContainer == null) {
                    // Create preview
                    viewerContainer = document.createElement('div');
                    viewerContainer.dataset.fileId = fileId;
                    viewerContainer.classList.add('preview-container');
                    let viewer;
                    if (fileExt == 'pdf') {
                        viewer = document.createElement('div');
                        viewer.setAttribute('id', fileId);
                        viewer.setAttribute('src', fileSrc);
                        viewer.setAttribute('class', 'inline-pdf-viewer');
                        viewerContainer.appendChild(viewer);
                    }
                    else {
                        viewer = document.createElement('img');
                        viewer.dataset.fileId = fileId;
                        viewer.setAttribute('src', fileSrc);
                        viewer.style.maxWidth = '100%';
                        viewerContainer.appendChild(viewer);
                    }
                    if (fileExt == 'svg') {
                        viewerContainer.style.backgroundColor = '#FFFFFF'; // White background for SVG
                    }
                    $btn.addClass('previewing');
                    $btn.after(viewerContainer);
                    if (fileExt == 'pdf') addResizer(viewer);
                    return;
                }
                if ($btn.hasClass('previewing')) {
                    $btn.removeClass('previewing');
                    viewerContainer.style.display = 'none';
                    $('tr[data-viewer-for-pdf="' + fileId + '"]').hide();
                }
                else {
                    $btn.addClass('previewing');
                    viewerContainer.style.display = 'block';
                    $('tr[data-viewer-for-pdf="' + fileId + '"]').show();
                }
            });
        }
        function removeFileUploadPreview(fieldName) {
            const $a = $('a.filedownloadlink[name="' + fieldName + '"]');
            const fileId = $a.parents('.fileupload-container').find('[data-file-id]').attr('data-file-id');
            $a.attr('data-file-initialized', null);
            $a.attr('data-file-id', null);
            $a.attr('data-file-type', null)
            $('[data-file-id="' + fileId + '"]').remove();
            $('.inline-pdf-resizer[data-target-pdf="' + fileId + '"]').remove();
            if (config.debug) console.log('Removed file upload preview:', fieldName, fileId);
        }
        /** @param {HTMLElement} fileAttachment */
        function addPreview(fileAttachment) {
            const $fileAttachment = $(fileAttachment);
            let fileId = fileAttachment.dataset.filePreview;
            if (typeof fileId !== 'string') fileId = false;
            const fileSrc = $fileAttachment.find('a.file-link').attr('href').replace('file_download.php', 'image_view.php');
            if (!fileId || !fileSrc) return; // Missing essentials
            const $btn = $fileAttachment.find('button.rc-preview-file');
            if ($btn.attr('data-file-initialized') == '1') return; // Already initialized
            if (config.debug) console.log('Preview:', fileAttachment);
            let type = $btn.attr('data-file-type');
            if (typeof type == 'undefined') type = 'application/pdf';
            $btn.on('click', function() {
                // Viewer already there?
                let viewerContainer = document.querySelector('.preview-container[data-file-id="' + fileId + '"]');
                if (viewerContainer == null) {
                    // Create preview
                    viewerContainer = document.createElement('div');
                    viewerContainer.dataset.fileId = fileId;
                    viewerContainer.classList.add('preview-container');
                    if (type == 'application/pdf') {
                        const viewer = document.createElement('div');
                        viewer.setAttribute('id', fileId);
                        viewer.setAttribute('src', fileSrc);
                        viewer.setAttribute('class', 'inline-pdf-viewer');
                        viewerContainer.appendChild(viewer);
                        addResizer(viewer);
                    }
                    else {
                        const viewer = document.createElement('img');
                        viewer.dataset.fileId = fileId;
                        viewer.setAttribute('src', fileSrc);
                        viewer.style.maxWidth = '100%';
                        viewerContainer.appendChild(viewer);
                    }
                    if (type == 'image/svg+xml') {
                        viewerContainer.style.backgroundColor = '#FFFFFF'; // White background for SVG
                    }
                    $fileAttachment.after(viewerContainer);
                    $fileAttachment.addClass('previewing');
                    return;
                }
                if ($fileAttachment.hasClass('previewing')) {
                    $fileAttachment.removeClass('previewing');
                    viewerContainer.style.display = 'none';
                }
                else {
                    $fileAttachment.addClass('previewing');
                    viewerContainer.style.display = 'block';
                }
            });
            $btn.attr('data-file-initialized', '1');
        }
        function removePreview(fileAttachment) {
            if (config.debug) console.log('Remove preview:', fileAttachment);
            const fileId = fileAttachment.dataset.filePreview;
            fileAttachment.classList.remove('previewing');
            $(fileAttachment).find('button.rc-preview-file').attr('data-file-initilized', null);
            $('.preview-container[data-file-id="' + fileId + '"]').remove();
        }
        //#endregion
        //#region PDF Resizer
        document.addEventListener("pointermove", function(e) {
            if (!current) return;
            const h = Math.max(minH, current.resize.initialH + e.clientY - current.resize.initialY);
            current.pdf.style.height = h + 'px';
            if (!current.maximize.moved) {
                current.resizer.classList.remove('maximized');
            }
            if(e.stopPropagation) e.stopPropagation();
            if(e.preventDefault) e.preventDefault();
            e.cancelBubble = true; // Legacy (deprecated)
            e.returnValue = false; // Legacy (deprecated)
            return false;
        });
        document.addEventListener("pointerup", function(e) {
            const pdf = current;
            current = false;
            if (pdf) {
                setTimeout(() => {
                    pdf.resizing = false;
                }, 10);
                pdf.resizer.classList.remove('resizing');
                body.classList.remove('resizing-v');
                pdf.pdf.classList.remove('resizing');
                if (!pdf.maximize.moved) {
                    pdf.resizer.classList.remove('maximized');
                }
                if(e.stopPropagation) e.stopPropagation();
                if(e.preventDefault) e.preventDefault();
                e.cancelBubble = true; // Legacy (deprecated)
                e.returnValue = false; // Legacy (deprecated)
                if (config.debug) console.log('Done resizing:', pdf);
            }
        });
        /** @param {HTMLElement} pdf */
        function addResizer(pdf) {
            if (pdf.dataset.pdfEnhanced) return;
            pdf.dataset.pdfEnhanced = true;
            const pdfId = pdf.dataset.fileId;
            const $resizer = $(' \
                <div class="inline-pdf-resizer"> \
                    <i class="fa-solid fa-grip-lines resize"></i> \
                    <button class="btn btn-xs btn-light" data-action="enter-fs" type="button"> \
                        <i class="fa-solid fa-expand"></i> \
                        <span data-rc-lang="form_renderer_66" class="visually-hidden">' + lang.form_renderer_66 + '</span> \
                    </button> \
                    <button class="btn btn-xs btn-light" data-action="exit-fs" type="button"> \
                        <i class="fa-solid fa-compress"></i> \
                        <span data-rc-lang="form_renderer_66" class="visually-hidden">' + lang.form_renderer_66 + '</span> \
                    </button> \
                </div>');
            const resizer = $resizer.get(0);
            resizer.dataset.targetPdf = pdfId;
            let field = $(pdf).siblings(".filedownloadlink").attr('name');
            if (typeof field !== 'string') field = false;
            if (field) {
                // Remove and pre-existing resizers for this field
                Object.keys(pdfs).filter((id) => pdfs[id].field === field).forEach((id) => remove(id));
            }
            pdfs[pdfId] = {
                resize: {
                    initialH: 500,
                    initialY: 0
                },
                maximize: {
                    initialH: 500,
                    initialY: 0,
                    w: pdf.clientWidth,
                    maxW: pdf.style.maxWidth,
                    moved: false,
                },
                resizer: resizer,
                pdf: pdf,
                isNarrow: $(pdf).parents('tr').find('td.data').length > 0,
                maximized: false,
                resizing: false,
                field: field
            };
            $resizer.on('pointerdown', function(e) {
                if (e.originalEvent.target.classList.contains('inline-pdf-resizer')) {
                    current = pdfs[pdfId];
                    current.resizing = true;
                    if (config.debug) console.log('Resizing: ', current, e);
                    resizer.classList.add('resizing');
                    body.classList.add('resizing-v');
                    pdf.classList.add('resizing');
                    pdfs[pdfId].resize.initialH = pdf.clientHeight;
                    pdfs[pdfId].resize.initialY = e.clientY;
                }
            });
            $resizer.find('[data-action=enter-fs]').on('click', function(e) {
                if (pdfs[pdfId].resizing) return; 
                pdfs[pdfId].maximized = true;
                resizer.classList.add('maximized');
                pdfs[pdfId].maximize.initialH = pdf.clientHeight;
                pdfs[pdfId].maximize.initialY = window.scrollY;
                if (pdfs[pdfId].isNarrow) {
                    // Move to full row
                    pdfs[pdfId].maximize.w = pdf.clientWidth;
                    pdfs[pdfId].maximize.maxW = pdf.style.maxWidth;
                    pdfs[pdfId].maximize.moved = true;
                    const placeholder = document.createElement('div');
                    placeholder.style.display = 'none';
                    placeholder.dataset.placeholderForPdf = pdfId;
                    $resizer.after(placeholder);
                    const $parentTR = $resizer.parents('tr[sq_id]');
                    const colspan = $parentTR.children('td').length;
                    const $container = $(' \
                        <tr data-viewer-for-pdf="' + pdfId + '"> \
                            <td class="labelrc" colspan="' + colspan + '" class="col-12" style="padding: 2px 5px;"></td> \
                        </tr>');
                    $parentTR.after($container);
                    $container.find('td').append(pdf).append($resizer);
                    pdf.style.width = '100%';
                    pdf.style.maxWidth = '100%';
                }
                const scrollbarH = window.innerHeight - document.documentElement.clientHeight;
                const viewportH = window.innerHeight - scrollbarH;
                const extra = resizer.offsetTop - pdf.offsetTop - pdf.offsetHeight;
                const finalH = viewportH - resizer.clientHeight - extra - 2;
                pdf.style.height = finalH + 'px';
                setTimeout(() => {
                    pdf.scrollIntoView();
                }, 0); 
                $resizer.find('[data-action=exit-fs]').trigger('focus');
                if (config.debug) console.log('Maximized:', pdfs[pdfId], e);
            });
            $resizer.find('[data-action=exit-fs]').on('click', function(e) {
                pdfs[pdfId].maximized = false;
                if (pdfs[pdfId].isNarrow) {
                    // Move back to cell
                    pdf.style.width = pdfs[pdfId].maximize.w + 'px';
                    pdf.style.maxWidth = pdfs[pdfId].maximize.maxW;
                    $('[data-placeholder-for-pdf="' + pdfId + '"]').after(resizer).after(pdf).remove();
                    $('tr[data-viewer-for-pdf="' + pdfId + '"]').remove();
                }
                resizer.classList.remove('maximized');
                pdf.style.height = pdfs[pdfId].maximize.initialH + 'px';
                setTimeout(() => {
                    pdfs[pdfId].maximize.moved = false;
                    window.scroll({ top: pdfs[pdfId].maximize.initialY });
                }, 0);
                $resizer.find('[data-action=enter-fs]').trigger('focus');
                if (config.debug) console.log('Restored:', pdfs[pdfId], e);
            });
            // Add resize widget
            $(pdf).after($resizer);
            // Prevent <label> interference
            $resizer.parents('label').each(function() {
                if (!this.getAttributeNames().includes('for')) this.setAttribute('for', 'this-is-some-dummy-id-that-probably-does-not-exist');
            })
            if (config.debug) console.log('Resizer:', pdfs[pdfId]);
            initInlinePdfs();
        }
        function cleanup(el) {
            let id = el.dataset.targetPdf;
            if (typeof id !== 'string') id = false;
            if (id) {
                if (pdfs[id].field && $('input[type=hidden][name="' + pdfs[id].field + '"]').val() == '') {
                    remove(id);
                }
            }
        }
        function remove(id) {
            pdfs[id].resizer.remove();
            $('[data-placeholder-for-pdf="' + id + '"]').remove();
            $('[data-viewer-for-pdf="' + id + '"]').remove();
            delete pdfs[id];
        }
        //#endregion

        function setDebugMode(mode) {
            config.debug = mode == true
        };

        // Public interface
        return {
            addPreview: (fileAttachment) => {
                try { addPreview(fileAttachment); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            removePreview: (fileAttachment) => {
                try { removePreview(fileAttachment); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            addFileUploadPreview: (fileUpload) => {
                try { addFileUploadPreview(fileUpload); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            removeFileUploadPreview: (fileUpload) => {
                try { removeFileUploadPreview(fileUpload); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            addResizer: (pdf) => {
                try { addResizer(pdf); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            cleanup: (el) => {
                try { cleanup(el); } 
                catch (ex) { if(config.debug) console.error(ex); }
            },
            debug: (mode) => setDebugMode(mode)
        };
    })();
}

//#endregion
