Search code examples
javascriptjslint

Why am I getting "is out of scope" from JSLint for a function called from an event?


I need to reinitialize the state of some html elements from two different events (an html select change and a checkbox change), so I created a function that I placed outside the "ready" function to do that, and call it from both events.

However, JSLint flags this call to it:

$('#unitsselect').change(function() {
    unitSelected = true;

    reinitializeRecipientsAndGenerateVals();
    . . .

...as being "out of scope"; specifically, it tells me,

'reinitializeRecipientsAndGenerateVals' is out of scope.

and indicates that line as the problematic one.

reinitializeRecipientsAndGenerateVals() is indeed outside that change event handler, and also outside the "ready" function. I put it inside the ready function to see if that was the problem, but I still get that warning from JSLint with it there.

How can I reuse this function so that I don't have to move it inside both change event handlers?

Note: This has an "used out of scope" section, but not an "is out of scope"

According to this, all versions of JSLint use "{a} used outside of binding context"

UPDATE

Even when I move the function right into the change event handler, JSLint complains in the same way.

UDPATE 2

For Travis, here is the whole enchilada:

$(document).ready(function() {

    var unitSelected = false;
    var checkboxSelected = false;

    // When unit is selected, populate the data range value elements; if checkbox selected,
    // also populate email recipients and generate vals (specific day or based on a pattern)
    $('#unitsselect').change(function() {
        unitSelected = true;

        reinitializeRecipientsAndGenerateVals();

        var unitval = $('#unitsselect').val();
        // This is admittedly kludgy - copying the same ajax call multiple times, changing only
        // the report number; but a forloop attempt failed because of the asynchronous nature of
        // it all; for some reason, populatedatarangeprams() was called only once, and with a
        // rptval of 5 (when it "should have been" 1..4 instead).
        var model = JSON.stringify({ unit: unitval, report: 1 });
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitDataRangeParamsVals", "UnitReportDataRangeParams")',
            data: { unit: unitval, report: 1 },
            contentType: 'application/json',
            cache: false,
            success: function(returneddata) {
                populatedatarangeprams(1, returneddata);
            },
            error: function() {
                alert('error - returneddata.error.stringify');
            }
        });

        model = JSON.stringify({ unit: unitval, report: 2 });
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitDataRangeParamsVals", "UnitReportDataRangeParams")',
            data: { unit: unitval, report: 2 },
            contentType: 'application/json',
            cache: false,
            success: function(returneddata) {
                populatedatarangeprams(2, returneddata);
            },
            error: function() {
                alert('error - returneddata.error.stringify');
            }
        });

        model = JSON.stringify({ unit: unitval, report: 3 });
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitDataRangeParamsVals", "UnitReportDataRangeParams")',
            data: { unit: unitval, report: 3 },
            contentType: 'application/json',
            cache: false,
            success: function(returneddata) {
                populatedatarangeprams(3, returneddata);
            },
            error: function() {
                alert('error - returneddata.error.stringify');
            }
        });

        model = JSON.stringify({ unit: unitval, report: 4 });
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitDataRangeParamsVals", "UnitReportDataRangeParams")',
            data: { unit: unitval, report: 4 },
            contentType: 'application/json',
            cache: false,
            success: function(returneddata) {
                populatedatarangeprams(4, returneddata);
            },
            error: function() {
                alert('error - returneddata.error.stringify');
            }
        });

        if (!checkboxSelected) {
            return;
        }

        // from http://stackoverflow.com/questions/36870365/how-can-i-elegantize-this-verbose-jquery/
        rptval = $('[id^=ckbx_]').filter(':checked').val();
        setEmailAndGenerateValsForUnitReportPair(unitval, rptval);
    }); // $('#unitsselect').change(function ()

    // Checkbox selection has changed; if Unit has been selected, populate the email and generate vals
    $(".ckbx").change(function() {
        if (!this.checked) {
            checkboxSelected = false;
            reinitializeRecipientsAndGenerateVals();
            return;
        }

        checkboxSelected = true;
        // this unchecks all other checkboxes (from http://stackoverflow.com/questions/17785010/jquery-uncheck-other-checkbox-on-one-checked):
        $('.ckbx').not(this).prop('checked', false);
        reinitializeRecipientsAndGenerateVals();

        // If no unit is selected, vals can not be set, so exit now
        if (!unitSelected) {
            return;
        }

        var unitval = $('#unitsselect').val();
        var rptval = $(this).val();
        setEmailAndGenerateValsForUnitReportPair(unitval, rptval);
    }); // $(".ckbx").change(function ()

}); // "ready" function

    function setEmailAndGenerateValsForUnitReportPair(unitval, rptval) {
        var model = JSON.stringify({ unit: unitval, report: rptval });
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitReportPairEmailAddresses", "UnitReportPair")',
            data: { unit: unitval, report: rptval },
            contentType: 'application/json', //<= this is paired with stringify above; if comment out one, comment out both
            cache: false,
            success: function(returneddata) {
                populateemails(returneddata);
            },
            error: function() {
                alert(returneddata.error.stringify);
            }
        });

        // The above AJAX call retrieved email addr vals; the next one is for the "Generate and Email Report" section of the page
        $.ajax({
            type: 'GET',
            url: '@Url.Action("GetUnitReportPairGenerateVals", "UnitReportPairGenerateVals")',
            data: { unit: unitval, report: rptval },
            contentType: 'application/json',
            cache: false,
            success: function(returneddata) {
                populategeneratevals(returneddata);
            },
            error: function() {
                alert(returneddata.error.stringify);
            }
        });
    }

    //Adapted from Jakes's answer here: http://stackoverflow.com/questions/2204250/check-if-checkbox-is-checked-with-jquery
    function isCheckedById(id) {
        var checked = $("input[id=" + id + "]:checked").length;
        return (checked != 0);
    }

    function populateemails(trampdata) {
        // first, clear them all
        $("#email1").val('');
        $("#email2").val('');
        $("#email3").val('');
        // Now set those for which there are values
        if (trampdata.UnitReportPairEmailVals.length > 0) {
            $("#email1").val(trampdata.UnitReportPairEmailVals[0]);
        }
        if (trampdata.UnitReportPairEmailVals.length > 1) {
            $("#email2").val(trampdata.UnitReportPairEmailVals[1]);
        }
        if (trampdata.UnitReportPairEmailVals.length > 2) {
            $("#email3").val(trampdata.UnitReportPairEmailVals[2]);
        }
    }

    function populategeneratevals(generateddata) {
        // first, clear them all, if they had been set to something else
        $("#dayofmonthselect").val('1st');
        $("#ordinalselect").val('First');
        $("#dayofweekselect").val('Monday');
        $("#weekormonthselect").val('Month');

        // Now set those for which there are values
        var domOrdinalified = ordinalify(generateddata.generatevals.DayOfMonth);
        $("#dayofmonthselect").val(domOrdinalified);
        $("#ordinalselect").val(generateddata.generatevals.PatternOrdinal);
        $("#dayofweekselect").val(generateddata.generatevals.PatternDOW);
        $("#weekormonthselect").val(generateddata.generatevals.PatternInterval);

        // Now set the correct radio button
        if ($("#dayofmonthselect").val() === 0) {
            $("#groupRptGenerationAndSendingByDayOfMonth").prop("checked", true);
        } else {
            $("#groupRptGenerationAndSendingBasedOnAPattern").prop("checked", true);
        }
    }

    function ordinalify(domint) {
        if (domint === 1 || domint === 21 || domint === 31) {
            return domint + 'st';
        }
        if (domint === 2 || domint === 22) {
            return domint + 'nd';
        }
        if (domint === 3 || domint === 23) {
            return domint + 'rd';
        }
        return domint + 'th';
    }

    function populatedatarangeprams(rptval, returneddata) {
        if (rptval === 1) {
            // Produce Usage
            $("#produsagefrom").val(returneddata.datarangeparams.TimeUnitsFrom);
            $("#produsageto").val(returneddata.datarangeparams.TimeUnitsTo);
        } else if (rptval === 2) {
            // Delivery Performance
            $("#delperffrom").val(returneddata.datarangeparams.TimeUnitsFrom);
            $("#delperfto").val(returneddata.datarangeparams.TimeUnitsTo);
        } else if (rptval === 3) {
            // Price Compliance
            $("#pricecompliancefrom").val(returneddata.datarangeparams.TimeUnitsFrom);
            $("#pricecomplianceto").val(returneddata.datarangeparams.TimeUnitsTo);
        } else if (rptval === 4) {
            // Fill Rate
            $("#fillratefrom").val(returneddata.datarangeparams.TimeUnitsFrom);
            $("#fillrateto").val(returneddata.datarangeparams.TimeUnitsTo);
        }
        // TODO: Add 5 if/when Contract vs Market is added
    }

    function reinitializeRecipientsAndGenerateVals() {
        $("#email1").val('');
        $("#email2").val('');
        $("#email3").val('');

        $("#dayofmonthselect").val('1st');
        $("#ordinalselect").val('First');
        $("#dayofweekselect").val('Monday');
        $("#weekormonthselect").val('Month');
    }

Solution

  • Okay, I've tracked it down to this:

    foo(); // <- function call
    
    function foo() { // <- function declaration
    }
    

    Seem like it's an obscure way to say "Declare function before calling it!" or "Do not rely on hoisting!".

    You can fix it by moving function declaration to the top.