<commit_before>/* ©Copyright Utrecht University (Department of Information and Computing Sciences) */

var HtmlGenerator;

(function()
{
    "use strict";

    HtmlGenerator =
    {
        addEmptyUserDefinedParameterDefinition: addEmptyUserDefinedParameterDefinition,
        addEmptyUserDefinedParameterEffect: addEmptyUserDefinedParameterEffect,
        insertPreconditions: insertPreconditions
    };

    //Get all the raw HTLM from Parts.js
    var parameterHTML = Parts.getParameterDefinitionHTML();
    var preconditionHTML = Parts.getPreconditionHTML();
    var groupPreconditionHTML = Parts.getGroupPreconditionHTML();
    var parameterEffectHTML = Parts.getParameterEffectHTML();
    var radioButtonCounter = 0;

    $(document).ready(function()
    {
        $("#preconditionsDiv").on('click', ".addPrecondition", function()
        {
            if (Parameters.atLeastOneUserDefined() || Config.atLeastOneParameter())
            {
                var container = $(this).parent().children(".groupPreconditionDiv");
                addEmptyPrecondition(container);
                var addedDiv = container.children(".precondition").last();
                focusFirstTabindexedDescendant(addedDiv);
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_test'));
            }
        });
        $("#preconditionsDiv").on('click', ".addGroupPrecondition", function()
        {
            if (Parameters.atLeastOneUserDefined() || Config.atLeastOneParameter())
            {
                var container = $(this).parent().children(".groupPreconditionDiv");
                addEmptyGroupPrecondition(container);
                container.children(".precondition").last().find('button').first().focus();
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_test'));
            }
        });

        $("#addUserDefinedParameterEffect").on('click', function()
        {
            if (Parameters.atLeastOneUserDefined())
            {
                addEmptyUserDefinedParameterEffect();
                focusFirstTabindexedDescendant($(".effect").last());
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_effect'));
            }
        });

        $("#addParameter").on('click', function()
        {
            var addedDiv = addEmptyUserDefinedParameterDefinition();
            focusFirstTabindexedDescendant(addedDiv);
            $("#paramsTableHead").removeClass("hidden");
        });

        $("#addTimeParameter").on('click', function()
        {
            var isTime = $("#params").find(".isT").length;
            var isTimeRemoved = $("#params").find(".isT.removedParameter").length;

            // if the timeParameterObject is empty, or if it is filled, but
            // the parameter in the dialog has been removed (in that case the
            // timeParameterObject has not been updated yet)
            if (Parameters.timeId === null || isTimeRemoved === isTime)
            {
                $("#params").append(parameterHTML);
                var div = $("#params").children().last();
                // div.children().children().prop('disabled', true);
                $(div).prop('id', 't');
                div.find(".name").val(i18next.t('htmlGenerator:time'));
                div.find(".parameter-type-select").val(Types.primitives.integer.name);
                div.find(".parameter-type-select").prop("disabled", "disabled");
                div.find(".parameter-initial-value-container").remove();

                focusFirstTabindexedDescendant($("#params").children().last());
                $("#paramsTableHead").removeClass("hidden");
                $("#addTimeParameter").addClass("hidden");
            }
        });

        // Event handlers for removing HTML.
        $("#preconditionsDiv").on('click', '.deleteParent', function()
        {
            var containingGroupPrecondition = $(this).parent().parent().closest(".groupprecondition");
            $(this).parent().remove();
            updateGroupPreconditionCounter(containingGroupPrecondition);
        });

        $("#userDefinedParameterEffects").on('click', '.deleteParent', function()
        {
            $(this).parent().remove();
        });
    });

    function addEmptyUserDefinedParameterDefinition()
    {
        $("#params").append(parameterHTML);
        var addedDiv = $("#params").children().last();

        var typeSelect = addedDiv.find('.parameter-type-select');
        var parent = typeSelect.parent();
        typeSelect.remove();

        var previousType;
        var onParameterTypeChange = function(newTypeName, userTypeChange)
        {
            addedDiv.addClass("changedTypeParameter");

            var replaceInitialValueContainer = function()
            {
                var initialValueContainer = addedDiv.find(".parameter-initial-value-container");
                var initialValue;
                if (previousType) initialValue = previousType.getFromDOM(initialValueContainer);
                initialValueContainer.empty();
                var type = Types.primitives[newTypeName].loadTypeFromDOM(addedDiv, initialValueContainer, 'parameter');
                type.appendControlTo(initialValueContainer);
                if (previousType) type.setInDOM(initialValueContainer, type.castFrom(previousType, initialValue));
                previousType = type;
            };

            var parameterMinContainer = addedDiv.find(".parameter-min-container");
            var parameterMaxContainer = addedDiv.find(".parameter-max-container");
            if (newTypeName === Types.primitives.integer.name)
            {
                if (!parameterMinContainer.children(Types.primitives.integer.controlName).length)
                {
                    Types.primitives[newTypeName].appendControlTo(parameterMinContainer);
                    Types.primitives[newTypeName].setInDOM(parameterMinContainer, "");
                }
                if (!parameterMaxContainer.children(Types.primitives.integer.controlName).length)
                {
                    Types.primitives[newTypeName].appendControlTo(parameterMaxContainer);
                    Types.primitives[newTypeName].setInDOM(parameterMaxContainer, "");
                }
            }
            else
            {
                parameterMinContainer.empty();
                parameterMaxContainer.empty();
            }

            if (newTypeName === Types.primitives.enumeration.name)
            {
                if (!userTypeChange)
                {
                    replaceInitialValueContainer();
                }
            }
            else
            {
                replaceInitialValueContainer();
            }
        };

        Types.appendSelectTo(parent, 'parameter-type-select', onParameterTypeChange);

        addedDiv.removeClass("changedTypeParameter");

        return addedDiv;
    }

    function addEmptyUserDefinedParameterEffect()
    {
        $("#userDefinedParameterEffects").append(parameterEffectHTML);
        var addedDiv = $("#userDefinedParameterEffects").children().last();

        insertParameters(addedDiv, Parameters.container);

        var idRefSelect = addedDiv.find(".parameter-idref-select");
        var effectDiv = addedDiv.find(".parameter-effect-container");
        var changeEffectType = function(pId)
        {
            var operatorSelect = $('<select>', { class: "parameter-effect-operator-select" });
            Parameters.container.byId[pId].type.assignmentOperators.forEach(function(op)
            {
                operatorSelect.append($('<option>', { value: op.name, text: op.uiName }));
            });
            effectDiv.append(operatorSelect);

            var controlContainer = $('<span>', { class: "parameter-effect-value-container" });
            Parameters.container.byId[pId].type.appendControlTo(controlContainer);
            effectDiv.append(controlContainer);
        };
        changeEffectType(idRefSelect.val());
        idRefSelect.on('change', function()
        {
            effectDiv.empty();
            changeEffectType($(this).val());
        });

        return addedDiv;
    }

    function insertPreconditions(precondition, divToAddTo)
    {
        var addedDiv = addEmptyGroupPrecondition(divToAddTo);
        addedDiv.children(".groupPreconditionRadioDiv").find(
            "input[value=" + precondition.type + "]").prop(
            'checked', true);

        var divToAddChildren = addedDiv.children(".groupPreconditionDiv");
        for (var i = 0; i < precondition.preconditions.length; i++)
        {
            var currentPrecondition = precondition.preconditions[i];
            if ("type" in currentPrecondition)
            {
                insertPreconditions(currentPrecondition, divToAddChildren);
            }
            else
            {
                var parameter = Config.findParameterById(currentPrecondition.idRef, currentPrecondition.characterIdRef);
                if (!parameter) parameter = Parameters.container.byId[currentPrecondition.idRef];

                addedDiv = addEmptyPrecondition(divToAddChildren);
                addedDiv.find(".parameter-idref-select").val(currentPrecondition.idRef).trigger('change');
                addedDiv.find(".character-idref-select").val(currentPrecondition.characterIdRef);
                addedDiv.find(".precondition-operator-select").val(currentPrecondition.operator);
                parameter.type.setInDOM(addedDiv.find(".precondition-value-container"), currentPrecondition.value);
            }
        }
    }

    function addEmptyPrecondition(divToAdd)
    {
        divToAdd.append(preconditionHTML);
        var addedDiv = $(divToAdd).children().last();

        insertParameters(addedDiv, Parameters.container);
        insertParameters(addedDiv, Config.container.parameters);
        insertParameters(addedDiv, Config.container.characters.parameters);
        for (var characterId in Config.container.characters.byId)
        {
            insertParameters(addedDiv, Config.container.characters.byId[characterId].parameters);
        }

        var idRefSelect = addedDiv.find(".parameter-idref-select");
        var testContainer = addedDiv.find(".precondition-test-container");
        var changeTestType = function(parameterIdRef)
        {
            var parameter = Config.findParameterById(parameterIdRef);
            if (!parameter) parameter = Parameters.container.byId[parameterIdRef];

            var characterIdRefSelect;
            if (Config.isCharacterParameter(parameterIdRef))
            {
                var inIndividualCharacter = Config.container.characters.sequence.some(function(character)
                {
                    if (parameterIdRef in Config.container.characters.byId[character.id].parameters.byId)
                    {
                        characterIdRefSelect = $('<select>', { class: "character-idref-select" });
                        characterIdRefSelect.append($('<option>', { value: character.id, text: character.name ? character.name : character.id }));
                        if (Config.container.characters.sequence.length === 1) characterIdRefSelect.hide();
                        testContainer.append(characterIdRefSelect);
                        return true;
                    }
                    return false;
                });

                if (!inIndividualCharacter)
                {
                    characterIdRefSelect = $('<select>', { class: "character-idref-select" });
                    Config.container.characters.sequence.forEach(function(character)
                    {
                        characterIdRefSelect.append($('<option>', { value: character.id, text: character.name ? character.name : character.id }));
                    });
                    if (Config.container.characters.sequence.length === 1) characterIdRefSelect.hide();
                    testContainer.append(characterIdRefSelect);
                }
            }

            var operatorSelect = $('<select>', { class: "precondition-operator-select" });
            parameter.type.relationalOperators.forEach(function(relOp)
            {
                operatorSelect.append($('<option>', { value: relOp.name, text: relOp.uiName }));
            });
            testContainer.append(operatorSelect);

            var controlContainer = $('<span>', { class: "precondition-value-container" });
            parameter.type.appendControlTo(controlContainer);
            testContainer.append(controlContainer);
        };
        changeTestType(idRefSelect.val());
        idRefSelect.on('change', function()
        {
            testContainer.empty();
            changeTestType($(this).val());
        });

        updateGroupPreconditionCounter(divToAdd.closest(".groupprecondition"));
        return addedDiv;
    }

    function addEmptyGroupPrecondition(divToAdd)
    {
        divToAdd.append(groupPreconditionHTML);
        var addedDiv = $(divToAdd).children().last();
        radioButtonCounter += 1;
        addedDiv.children(".groupPreconditionRadioDiv").find("input").each(function()
        {
            $(this).prop("name", "preconRadio" + radioButtonCounter);
        });
        updateGroupPreconditionCounter(divToAdd.closest(".groupprecondition"));
        return addedDiv;
    }

    function updateGroupPreconditionCounter(div)
    {
        var childCount = div.children(".groupPreconditionDiv").children().length;
        var states = {
            empty: childCount === 0,
            single: childCount === 1,
            multiple: childCount > 1
        };

        for (var state in states)
            if (states[state])
                div.addClass(state);
            else
                div.removeClass(state);
    }

    // Inserts all the parameters for the effects and preconditions.
    function insertParameters(div, parameters)
    {
        var select = div.find(".parameter-idref-select");
        parameters.sequence.forEach(function (parameter)
        {
            select.append($('<option>', { value: parameter.id, text: parameter.name }));
        });
    }

    // Focuses on the first descendant that has a non-negative tabindex.
    // This includes elements that are focusable by default, like <input> and <select>.
    function focusFirstTabindexedDescendant(element)
    {
        var descendants = $(element).find('*');
        for (var i = 0; i < descendants.length; i++)
        {
            var toTest = $(descendants[i]);
            if (toTest.prop('tabIndex') >= 0)
            {
                return toTest.focus();
            }
        }
        return $();
    }
})();
<commit_msg>Fixed preconditions for parameters in sections<commit_after>/* ©Copyright Utrecht University (Department of Information and Computing Sciences) */

var HtmlGenerator;

(function()
{
    "use strict";

    HtmlGenerator =
    {
        addEmptyUserDefinedParameterDefinition: addEmptyUserDefinedParameterDefinition,
        addEmptyUserDefinedParameterEffect: addEmptyUserDefinedParameterEffect,
        insertPreconditions: insertPreconditions
    };

    //Get all the raw HTLM from Parts.js
    var parameterHTML = Parts.getParameterDefinitionHTML();
    var preconditionHTML = Parts.getPreconditionHTML();
    var groupPreconditionHTML = Parts.getGroupPreconditionHTML();
    var parameterEffectHTML = Parts.getParameterEffectHTML();
    var radioButtonCounter = 0;

    $(document).ready(function()
    {
        $("#preconditionsDiv").on('click', ".addPrecondition", function()
        {
            if (Parameters.atLeastOneUserDefined() || Config.atLeastOneParameter())
            {
                var container = $(this).parent().children(".groupPreconditionDiv");
                addEmptyPrecondition(container);
                var addedDiv = container.children(".precondition").last();
                focusFirstTabindexedDescendant(addedDiv);
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_test'));
            }
        });
        $("#preconditionsDiv").on('click', ".addGroupPrecondition", function()
        {
            if (Parameters.atLeastOneUserDefined() || Config.atLeastOneParameter())
            {
                var container = $(this).parent().children(".groupPreconditionDiv");
                addEmptyGroupPrecondition(container);
                container.children(".precondition").last().find('button').first().focus();
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_test'));
            }
        });

        $("#addUserDefinedParameterEffect").on('click', function()
        {
            if (Parameters.atLeastOneUserDefined())
            {
                addEmptyUserDefinedParameterEffect();
                focusFirstTabindexedDescendant($(".effect").last());
            }
            else
            {
                alert(i18next.t('htmlGenerator:error.no_effect'));
            }
        });

        $("#addParameter").on('click', function()
        {
            var addedDiv = addEmptyUserDefinedParameterDefinition();
            focusFirstTabindexedDescendant(addedDiv);
            $("#paramsTableHead").removeClass("hidden");
        });

        $("#addTimeParameter").on('click', function()
        {
            var isTime = $("#params").find(".isT").length;
            var isTimeRemoved = $("#params").find(".isT.removedParameter").length;

            // if the timeParameterObject is empty, or if it is filled, but
            // the parameter in the dialog has been removed (in that case the
            // timeParameterObject has not been updated yet)
            if (Parameters.timeId === null || isTimeRemoved === isTime)
            {
                $("#params").append(parameterHTML);
                var div = $("#params").children().last();
                // div.children().children().prop('disabled', true);
                $(div).prop('id', 't');
                div.find(".name").val(i18next.t('htmlGenerator:time'));
                div.find(".parameter-type-select").val(Types.primitives.integer.name);
                div.find(".parameter-type-select").prop("disabled", "disabled");
                div.find(".parameter-initial-value-container").remove();

                focusFirstTabindexedDescendant($("#params").children().last());
                $("#paramsTableHead").removeClass("hidden");
                $("#addTimeParameter").addClass("hidden");
            }
        });

        // Event handlers for removing HTML.
        $("#preconditionsDiv").on('click', '.deleteParent', function()
        {
            var containingGroupPrecondition = $(this).parent().parent().closest(".groupprecondition");
            $(this).parent().remove();
            updateGroupPreconditionCounter(containingGroupPrecondition);
        });

        $("#userDefinedParameterEffects").on('click', '.deleteParent', function()
        {
            $(this).parent().remove();
        });
    });

    function addEmptyUserDefinedParameterDefinition()
    {
        $("#params").append(parameterHTML);
        var addedDiv = $("#params").children().last();

        var typeSelect = addedDiv.find('.parameter-type-select');
        var parent = typeSelect.parent();
        typeSelect.remove();

        var previousType;
        var onParameterTypeChange = function(newTypeName, userTypeChange)
        {
            addedDiv.addClass("changedTypeParameter");

            var replaceInitialValueContainer = function()
            {
                var initialValueContainer = addedDiv.find(".parameter-initial-value-container");
                var initialValue;
                if (previousType) initialValue = previousType.getFromDOM(initialValueContainer);
                initialValueContainer.empty();
                var type = Types.primitives[newTypeName].loadTypeFromDOM(addedDiv, initialValueContainer, 'parameter');
                type.appendControlTo(initialValueContainer);
                if (previousType) type.setInDOM(initialValueContainer, type.castFrom(previousType, initialValue));
                previousType = type;
            };

            var parameterMinContainer = addedDiv.find(".parameter-min-container");
            var parameterMaxContainer = addedDiv.find(".parameter-max-container");
            if (newTypeName === Types.primitives.integer.name)
            {
                if (!parameterMinContainer.children(Types.primitives.integer.controlName).length)
                {
                    Types.primitives[newTypeName].appendControlTo(parameterMinContainer);
                    Types.primitives[newTypeName].setInDOM(parameterMinContainer, "");
                }
                if (!parameterMaxContainer.children(Types.primitives.integer.controlName).length)
                {
                    Types.primitives[newTypeName].appendControlTo(parameterMaxContainer);
                    Types.primitives[newTypeName].setInDOM(parameterMaxContainer, "");
                }
            }
            else
            {
                parameterMinContainer.empty();
                parameterMaxContainer.empty();
            }

            if (newTypeName === Types.primitives.enumeration.name)
            {
                if (!userTypeChange)
                {
                    replaceInitialValueContainer();
                }
            }
            else
            {
                replaceInitialValueContainer();
            }
        };

        Types.appendSelectTo(parent, 'parameter-type-select', onParameterTypeChange);

        addedDiv.removeClass("changedTypeParameter");

        return addedDiv;
    }

    function addEmptyUserDefinedParameterEffect()
    {
        $("#userDefinedParameterEffects").append(parameterEffectHTML);
        var addedDiv = $("#userDefinedParameterEffects").children().last();

        insertParameters(addedDiv, Parameters.container);

        var idRefSelect = addedDiv.find(".parameter-idref-select");
        var effectDiv = addedDiv.find(".parameter-effect-container");
        var changeEffectType = function(pId)
        {
            var operatorSelect = $('<select>', { class: "parameter-effect-operator-select" });
            Parameters.container.byId[pId].type.assignmentOperators.forEach(function(op)
            {
                operatorSelect.append($('<option>', { value: op.name, text: op.uiName }));
            });
            effectDiv.append(operatorSelect);

            var controlContainer = $('<span>', { class: "parameter-effect-value-container" });
            Parameters.container.byId[pId].type.appendControlTo(controlContainer);
            effectDiv.append(controlContainer);
        };
        changeEffectType(idRefSelect.val());
        idRefSelect.on('change', function()
        {
            effectDiv.empty();
            changeEffectType($(this).val());
        });

        return addedDiv;
    }

    function insertPreconditions(precondition, divToAddTo)
    {
        var addedDiv = addEmptyGroupPrecondition(divToAddTo);
        addedDiv.children(".groupPreconditionRadioDiv").find(
            "input[value=" + precondition.type + "]").prop(
            'checked', true);

        var divToAddChildren = addedDiv.children(".groupPreconditionDiv");
        for (var i = 0; i < precondition.preconditions.length; i++)
        {
            var currentPrecondition = precondition.preconditions[i];
            if ("type" in currentPrecondition)
            {
                insertPreconditions(currentPrecondition, divToAddChildren);
            }
            else
            {
                var parameter = Config.findParameterById(currentPrecondition.idRef, currentPrecondition.characterIdRef);
                if (!parameter) parameter = Parameters.container.byId[currentPrecondition.idRef];

                addedDiv = addEmptyPrecondition(divToAddChildren);
                addedDiv.find(".parameter-idref-select").val(currentPrecondition.idRef).trigger('change');
                addedDiv.find(".character-idref-select").val(currentPrecondition.characterIdRef);
                addedDiv.find(".precondition-operator-select").val(currentPrecondition.operator);
                parameter.type.setInDOM(addedDiv.find(".precondition-value-container"), currentPrecondition.value);
            }
        }
    }

    function addEmptyPrecondition(divToAdd)
    {
        divToAdd.append(preconditionHTML);
        var addedDiv = $(divToAdd).children().last();

        insertParameters(addedDiv, Parameters.container);
        insertParameters(addedDiv, Config.container.parameters);
        insertParameters(addedDiv, Config.container.characters.parameters);
        for (var characterId in Config.container.characters.byId)
        {
            insertParameters(addedDiv, Config.container.characters.byId[characterId].parameters);
        }

        var idRefSelect = addedDiv.find(".parameter-idref-select");
        var testContainer = addedDiv.find(".precondition-test-container");
        var changeTestType = function(parameterIdRef)
        {
            var parameter = Config.findParameterById(parameterIdRef);
            if (!parameter) parameter = Parameters.container.byId[parameterIdRef];

            var characterIdRefSelect;
            if (Config.isCharacterParameter(parameterIdRef))
            {
                var inIndividualCharacter = Config.container.characters.sequence.some(function(character)
                {
                    if (parameterIdRef in Config.container.characters.byId[character.id].parameters.byId)
                    {
                        characterIdRefSelect = $('<select>', { class: "character-idref-select" });
                        characterIdRefSelect.append($('<option>', { value: character.id, text: character.name ? character.name : character.id }));
                        if (Config.container.characters.sequence.length === 1) characterIdRefSelect.hide();
                        testContainer.append(characterIdRefSelect);
                        return true;
                    }
                    return false;
                });

                if (!inIndividualCharacter)
                {
                    characterIdRefSelect = $('<select>', { class: "character-idref-select" });
                    Config.container.characters.sequence.forEach(function(character)
                    {
                        characterIdRefSelect.append($('<option>', { value: character.id, text: character.name ? character.name : character.id }));
                    });
                    if (Config.container.characters.sequence.length === 1) characterIdRefSelect.hide();
                    testContainer.append(characterIdRefSelect);
                }
            }

            var operatorSelect = $('<select>', { class: "precondition-operator-select" });
            parameter.type.relationalOperators.forEach(function(relOp)
            {
                operatorSelect.append($('<option>', { value: relOp.name, text: relOp.uiName }));
            });
            testContainer.append(operatorSelect);

            var controlContainer = $('<span>', { class: "precondition-value-container" });
            parameter.type.appendControlTo(controlContainer);
            testContainer.append(controlContainer);
        };
        changeTestType(idRefSelect.val());
        idRefSelect.on('change', function()
        {
            testContainer.empty();
            changeTestType($(this).val());
        });

        updateGroupPreconditionCounter(divToAdd.closest(".groupprecondition"));
        return addedDiv;
    }

    function addEmptyGroupPrecondition(divToAdd)
    {
        divToAdd.append(groupPreconditionHTML);
        var addedDiv = $(divToAdd).children().last();
        radioButtonCounter += 1;
        addedDiv.children(".groupPreconditionRadioDiv").find("input").each(function()
        {
            $(this).prop("name", "preconRadio" + radioButtonCounter);
        });
        updateGroupPreconditionCounter(divToAdd.closest(".groupprecondition"));
        return addedDiv;
    }

    function updateGroupPreconditionCounter(div)
    {
        var childCount = div.children(".groupPreconditionDiv").children().length;
        var states = {
            empty: childCount === 0,
            single: childCount === 1,
            multiple: childCount > 1
        };

        for (var state in states)
            if (states[state])
                div.addClass(state);
            else
                div.removeClass(state);
    }

    // Inserts all the parameters for the effects and preconditions.
    function insertParameters(div, parameters)
    {
        var select = div.find(".parameter-idref-select");

        var appendParameter = function(parameterItem)
        {
            if (parameterItem.kind && parameterItem.kind !== "parameter")
            {
                parameterItem.sequence.forEach(appendParameter);
            }
            else
            {
                select.append($('<option>', { value: parameterItem.id, text: parameterItem.name }));
            }
        };
        parameters.sequence.forEach(appendParameter);
    }

    // Focuses on the first descendant that has a non-negative tabindex.
    // This includes elements that are focusable by default, like <input> and <select>.
    function focusFirstTabindexedDescendant(element)
    {
        var descendants = $(element).find('*');
        for (var i = 0; i < descendants.length; i++)
        {
            var toTest = $(descendants[i]);
            if (toTest.prop('tabIndex') >= 0)
            {
                return toTest.focus();
            }
        }
        return $();
    }
})();
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const config = require(`../config/${process.env.NODE_ENV || 'production'}`);
var querystring = require('querystring');
const bb = require('bluebird');
var request = bb.promisifyAll(require('request'));
var db = require('../lib/db')(config.database);
var trackutils = require('../lib/trackUtils')(db);

var nutrients_helper = function(text, reply) {
  reply({text: "Link your account and log foods directly onto your Track foodlog. Once linked, begin adding foods to your log by messaging me what you ate."});
  reply({
    attachment: {
      type: 'template',
      payload: {
        template_type: 'generic',
        elements: [{title: "login", buttons: [{type: "account_link", url: "https://www.nutritionix.com/messenger-bot/authorize"}]}]
      }
    }
  })

  var data = {'query': text};

  return request.postAsync({
    headers: {
      'x-app-id': config.threeScale.appId,
      'x-app-key': config.threeScale.appKey
    },
    uri: 'https://trackapi.nutritionix.com/v2/natural/nutrients',
    json: data
  })
    .then(res => {
      let text;
      let body = res.body;
      if (res.statusCode === 404) {
        text = 'Could not match foods';
        return reply({text});
      }
      else if (res.statusCode > 200) {
        throw new Error(`status code: ${res.statusCode}`);
      } else {

        let elements = [];
        let cals = _.reduce(body.foods, (a, b) => a + b.nf_calories, 0);
        text = `That is about ${cals} total calories. Click the link below for more nutrition details.`;
        reply({text})
        _.forEach(body.foods, function(food) {
          let nat_q = food.serving_qty + ' ' + food.serving_unit + ' ' + food.food_name
          if (food.food_name.includes(food.serving_unit)) {
            nat_q = food.serving_qty + ' ' + food.food_name;
          }

          let element = {
            title: food.food_name,
            subtitle: food.serving_qty + ' ' + food.serving_unit,
            image_url: food.photo.thumb,
            buttons: [{type: 'web_url', title: 'View Details', url: `https://www.nutritionix.com/natural-demo?q=${nat_q}`}]
          };
          elements.push(element);
        })

        return reply({
          attachment: {
            type: 'template',
            payload: {
              template_type: 'generic',
              elements: elements
            }
          }
        })
      }
    })
    .catch(err => {
      console.error(err);
    });
}

var log_helper = function(text, user_jwt, reply) {
  var data = {'query': text};

  return request.postAsync({
    headers: {
      'x-app-id': config.threeScale.appId,
      'x-app-key': config.threeScale.appKey,
      'x-user-jwt': user_jwt
    },
    uri: 'https://trackapi.nutritionix.com/v2/natural/add',
    json: data
  })
    .then(res => {
      let text;
      let body = res.body;
      if (res.statusCode === 404) {
        text = 'Could not identify foods to add.';
        return reply({text});
      }
      else if (res.statusCode > 200) {
        throw new Error(`status code: ${res.statusCode}`);
      } else {
        let elements = [];
        let cals = _.reduce(body.foods, (a, b) => a + b.nf_calories, 0);
        text = `You have added a total of ${cals} calories to your food log. Click the links below to view your foodlog.`;
        reply({text})
        _.forEach(body.foods, function(food) {
          let nat_q = food.serving_qty + ' ' + food.serving_unit + ' ' + food.food_name
          if (food.food_name.includes(food.serving_unit)) {
            nat_q = food.serving_qty + ' ' + food.food_name;
          }

          let element = {
            title: food.food_name,
            subtitle: food.serving_qty + ' ' + food.serving_unit,
            image_url: food.photo.thumb,
            buttons: [{type: 'web_url', title: 'View log', url: `https://www.nutritionix.com/dashboard`}]
          };
          elements.push(element);
        })

        return reply({
          attachment: {
            type: 'template',
            payload: {
              template_type: 'generic',
              elements: elements
            }
          }
        })
      }
    })
}

var process_message = function(payload, reply) {
  var trackAccount = trackutils.getTokenByScopedId(payload.sender.id)
    .then(credentials => {
      if (!credentials) {
        return nutrients_helper(payload.message.text, reply);
      } else {
        return log_helper(payload.message.text, credentials['x-user-jwt'],reply);
      }
    })

};

var welcome = function(reply) {
  reply({text: "Please link your account. Once linked you will be able to add foods to your foodlog by messaging me what you ate. You are still able to retrieve nutritional information for foods without linking."})
  .then(() => {
    return reply({
      attachment: {
        type: 'template',
        payload: {
          template_type: 'generic',
          elements: [{title: "login", buttons: [{type: "account_link", url: "https://www.nutritionix.com/messenger-bot/authorize"}]}]
        }
      }
    })
  })
};

var confirmLink = function(reply) {
  console.log('conflink');
  return reply({text: 'You have succesffully linked. What have you eaten today?'})
}

module.exports = {
  process_message,
  welcome
};
<commit_msg>fix bugs<commit_after>'use strict';

const _ = require('lodash');
const config = require(`../config/${process.env.NODE_ENV || 'production'}`);
var querystring = require('querystring');
const bb = require('bluebird');
var request = bb.promisifyAll(require('request'));
var db = require('../lib/db')(config.database);
var trackutils = require('../lib/trackUtils')(db);

var nutrients_helper = function(text, reply) {
  reply({text: "Link your account and log foods directly onto your Track foodlog. Once linked, begin adding foods to your log by messaging me what you ate."});
  reply({
    attachment: {
      type: 'template',
      payload: {
        template_type: 'generic',
        elements: [{title: "login", buttons: [{type: "account_link", url: "https://www.nutritionix.com/messenger-bot/authorize"}]}]
      }
    }
  })

  var data = {'query': text};

  return request.postAsync({
    headers: {
      'x-app-id': config.threeScale.appId,
      'x-app-key': config.threeScale.appKey
    },
    uri: 'https://trackapi.nutritionix.com/v2/natural/nutrients',
    json: data
  })
    .then(res => {
      let text;
      let body = res.body;
      if (res.statusCode === 404) {
        text = 'Could not match foods';
        return reply({text});
      }
      else if (res.statusCode > 200) {
        throw new Error(`status code: ${res.statusCode}`);
      } else {

        let elements = [];
        let cals = _.reduce(body.foods, (a, b) => a + b.nf_calories, 0);
        text = `That is about ${cals} total calories. Click the link below for more nutrition details.`;
        reply({text})
        _.forEach(body.foods, function(food) {
          let nat_q = food.serving_qty + ' ' + food.serving_unit + ' ' + food.food_name
          if (food.food_name.includes(food.serving_unit)) {
            nat_q = food.serving_qty + ' ' + food.food_name;
          }

          let element = {
            title: food.food_name,
            subtitle: food.serving_qty + ' ' + food.serving_unit,
            image_url: food.photo.thumb,
            buttons: [{type: 'web_url', title: 'View Details', url: `https://www.nutritionix.com/natural-demo?q=${nat_q}`}]
          };
          elements.push(element);
        })

        return reply({
          attachment: {
            type: 'template',
            payload: {
              template_type: 'generic',
              elements: elements
            }
          }
        })
      }
    })
    .catch(err => {
      console.error(err);
    });
}

var log_helper = function(text, user_jwt, reply) {
  var data = {'query': text};

  return request.postAsync({
    headers: {
      'x-app-id': config.threeScale.appId,
      'x-app-key': config.threeScale.appKey,
      'x-user-jwt': user_jwt
    },
    uri: 'https://trackapi.nutritionix.com/v2/natural/add',
    json: data
  })
    .then(res => {
      let text;
      let body = res.body;
      if (res.statusCode === 404) {
        text = 'Could not identify foods to add.';
        return reply({text});
      }
      else if (res.statusCode > 201) {
        throw new Error(`status code: ${res.statusCode}`);
      } else {
        let elements = [];
        let cals = _.reduce(body.foods, (a, b) => a + b.nf_calories, 0);
        text = `You have added a total of ${cals} calories to your food log. Click the links below to view your foodlog.`;
        reply({text})
        _.forEach(body.foods, function(food) {
          let nat_q = food.serving_qty + ' ' + food.serving_unit + ' ' + food.food_name
          if (food.food_name.includes(food.serving_unit)) {
            nat_q = food.serving_qty + ' ' + food.food_name;
          }

          let element = {
            title: food.food_name,
            subtitle: food.serving_qty + ' ' + food.serving_unit,
            image_url: food.photo.thumb,
            buttons: [{type: 'web_url', title: 'View log', url: `https://www.nutritionix.com/dashboard`}]
          };
          elements.push(element);
        })

        return reply({
          attachment: {
            type: 'template',
            payload: {
              template_type: 'generic',
              elements: elements
            }
          }
        })
      }
    })
}

var process_message = function(payload, reply) {
  var trackAccount = trackutils.getTokenByScopedId(payload.sender.id)
    .then(credentials => {
      if (!credentials) {
        return nutrients_helper(payload.message.text, reply);
      } else {
        return log_helper(payload.message.text, credentials['x-user-jwt'],reply);
      }
    })

};

var welcome = function(reply) {
  reply({text: "Please link your account. Once linked you will be able to add foods to your foodlog by messaging me what you ate. You are still able to retrieve nutritional information for foods without linking."})
  .then(() => {
    return reply({
      attachment: {
        type: 'template',
        payload: {
          template_type: 'generic',
          elements: [{title: "login", buttons: [{type: "account_link", url: "https://www.nutritionix.com/messenger-bot/authorize"}]}]
        }
      }
    })
  })
};

var confirmLink = function(reply) {
  console.log('conflink');
  return reply({text: 'You have succesffully linked. What have you eaten today?'})
}

module.exports = {
  process_message,
  welcome,
  confirmLink
};
<|endoftext|>


<commit_before>define('ContainerView', require('app').View.extend({
  events: {
    'mouseenter header': 'showSettings',
    'mouseenter .column ul': 'hideSettings',
    'mouseleave': 'hideSettings'
  },
  initialize: function () {
    this.model.on('update', this.render, this);
  },
  render: function () {
    var self = this;
    // Push to the end of the execution stack
    setTimeout(function () {
      self.offset = self.$el.find('.settings-form').height();
    }, 0);
    return this;
  },
  hideSettings: function () {
    this.el.style[this.translateProp] = 'translate3d(0px, -' + this.offset + 'px, 0px)';
  },
  showSettings: function () {
    this.el.style[this.translateProp] = 'translate3d(0px, 0px, 0px)';
  },
  translateProp: (function () {
    var div = document.createElement('div'),
        prefixes = ['transform', 'mozTransform', 'webkitTransform'],
        i = 0;

    for (; i < prefixes.length; i++) {
      if (div.style[prefixes[i]] !== undefined) {
        return prefixes[i];
      }
    }
  }())
}));
<commit_msg>Always show the settings pane when no project has been picked<commit_after>define('ContainerView', require('app').View.extend({
  events: {
    'mouseenter header': 'showSettings',
    'mouseenter .column ul': 'hideSettings',
    'mouseleave': 'hideSettings'
  },
  initialize: function () {
    this.model.on('update', this.render, this);
  },
  render: function () {
    var self = this;
    // Push to the end of the execution stack
    setTimeout(function () {
      self.offset = self.$el.find('.settings-form').height();
    }, 0);
    return this;
  },
  hideSettings: function () {
    if (require('currentProject')()) {
      this.el.style[this.translateProp] = 'translate3d(0px, -' + this.offset + 'px, 0px)';
    }
  },
  showSettings: function () {
    this.el.style[this.translateProp] = 'translate3d(0px, 0px, 0px)';
  },
  translateProp: (function () {
    var div = document.createElement('div'),
        prefixes = ['transform', 'mozTransform', 'webkitTransform'],
        i = 0;

    for (; i < prefixes.length; i++) {
      if (div.style[prefixes[i]] !== undefined) {
        return prefixes[i];
      }
    }
  }())
}));
<|endoftext|>


<commit_before>/**
 * Namespace: Util.OSM
 */
OpenLayers.Util.OSM = {};

/**
 * Constant: MISSING_TILE_URL
 * {String} URL of image to display for missing tiles
 */
OpenLayers.Util.OSM.MISSING_TILE_URL = "http://openstreetmap.org/openlayers/img/404.png";

/**
 * Property: originalOnImageLoadError
 * {Function} Original onImageLoadError function.
 */
OpenLayers.Util.OSM.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;

/**
 * Function: onImageLoadError
 */
OpenLayers.Util.onImageLoadError = function() {
    if (this.src.match(/^http:\/\/[abc]\.[a-z]+\.openstreetmap\.org\//)) {
        this.src = OpenLayers.Util.OSM.MISSING_TILE_URL;
    } else if (this.src.match(/^http:\/\/[def]\.tah\.openstreetmap\.org\//)) {
        // do nothing - this layer is transparent
    } else {
        OpenLayers.Util.OSM.originalOnImageLoadError;
    }
};

/**
 * @requires OpenLayers/Layer/TMS.js
 *
 * Class: OpenLayers.Layer.OSM
 *
 * Inherits from:
 *  - <OpenLayers.Layer.TMS>
 */
OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.TMS, {
    /**
     * Constructor: OpenLayers.Layer.OSM
     *
     * Parameters:
     * name - {String}
     * url - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, url, options) {
        options = OpenLayers.Util.extend({
            attribution: "Data by OpenStreetMap",
            maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
            maxResolution: 156543.0339,
            units: "m",
            projection: "EPSG:900913",
            transitionEffect: "resize"
        }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.TMS.prototype.initialize.apply(this, newArguments);
    },

    /**
     * Method: getUrl
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {String} A string with the layer's url and parameters and also the
     *          passed-in bounds and appropriate tile size specified as
     *          parameters
     */
    getURL: function (bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();
        var limit = Math.pow(2, z);

        if (y < 0 || y >= limit)
        {
            return OpenLayers.Util.OSM.MISSING_TILE_URL;
        }
        else
        {
            x = ((x % limit) + limit) % limit;

            var url = this.url;
            var path = z + "/" + x + "/" + y + ".png";

            if (url instanceof Array)
            {
                url = this.selectUrl(path, url);
            }

            return url + path;
        }
    },

    CLASS_NAME: "OpenLayers.Layer.OSM"
});

/**
 * Class: OpenLayers.Layer.OSM.Mapnik
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Mapnik
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.tile.openstreetmap.org/",
            "http://b.tile.openstreetmap.org/",
            "http://c.tile.openstreetmap.org/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 19 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Mapnik"
});

/**
 * Class: OpenLayers.Layer.OSM.Osmarender
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Osmarender = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Osmarender
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.tah.openstreetmap.org/Tiles/tile/",
            "http://b.tah.openstreetmap.org/Tiles/tile/",
            "http://c.tah.openstreetmap.org/Tiles/tile/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 18 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Osmarender"
});

/**
 * Class: OpenLayers.Layer.OSM.CycleMap
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.CycleMap = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.CycleMap
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.andy.sandbox.cloudmade.com/tiles/cycle/",
            "http://b.andy.sandbox.cloudmade.com/tiles/cycle/",
            "http://c.andy.sandbox.cloudmade.com/tiles/cycle/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 19 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.CycleMap"
});

/**
 * Class: OpenLayers.Layer.OSM.Maplint
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Maplint = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Maplint
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://d.tah.openstreetmap.org/Tiles/maplint/",
            "http://e.tah.openstreetmap.org/Tiles/maplint/",
            "http://f.tah.openstreetmap.org/Tiles/maplint/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 18, isBaseLayer: false, visibility: false }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Maplint"
});
<commit_msg>Include CC-BY-SA in attribution.<commit_after>/**
 * Namespace: Util.OSM
 */
OpenLayers.Util.OSM = {};

/**
 * Constant: MISSING_TILE_URL
 * {String} URL of image to display for missing tiles
 */
OpenLayers.Util.OSM.MISSING_TILE_URL = "http://openstreetmap.org/openlayers/img/404.png";

/**
 * Property: originalOnImageLoadError
 * {Function} Original onImageLoadError function.
 */
OpenLayers.Util.OSM.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;

/**
 * Function: onImageLoadError
 */
OpenLayers.Util.onImageLoadError = function() {
    if (this.src.match(/^http:\/\/[abc]\.[a-z]+\.openstreetmap\.org\//)) {
        this.src = OpenLayers.Util.OSM.MISSING_TILE_URL;
    } else if (this.src.match(/^http:\/\/[def]\.tah\.openstreetmap\.org\//)) {
        // do nothing - this layer is transparent
    } else {
        OpenLayers.Util.OSM.originalOnImageLoadError;
    }
};

/**
 * @requires OpenLayers/Layer/TMS.js
 *
 * Class: OpenLayers.Layer.OSM
 *
 * Inherits from:
 *  - <OpenLayers.Layer.TMS>
 */
OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.TMS, {
    /**
     * Constructor: OpenLayers.Layer.OSM
     *
     * Parameters:
     * name - {String}
     * url - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, url, options) {
        options = OpenLayers.Util.extend({
            attribution: "Data by OpenStreetMap under CC-BY-SA",
            maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
            maxResolution: 156543.0339,
            units: "m",
            projection: "EPSG:900913",
            transitionEffect: "resize"
        }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.TMS.prototype.initialize.apply(this, newArguments);
    },

    /**
     * Method: getUrl
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {String} A string with the layer's url and parameters and also the
     *          passed-in bounds and appropriate tile size specified as
     *          parameters
     */
    getURL: function (bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();
        var limit = Math.pow(2, z);

        if (y < 0 || y >= limit)
        {
            return OpenLayers.Util.OSM.MISSING_TILE_URL;
        }
        else
        {
            x = ((x % limit) + limit) % limit;

            var url = this.url;
            var path = z + "/" + x + "/" + y + ".png";

            if (url instanceof Array)
            {
                url = this.selectUrl(path, url);
            }

            return url + path;
        }
    },

    CLASS_NAME: "OpenLayers.Layer.OSM"
});

/**
 * Class: OpenLayers.Layer.OSM.Mapnik
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Mapnik
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.tile.openstreetmap.org/",
            "http://b.tile.openstreetmap.org/",
            "http://c.tile.openstreetmap.org/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 19 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Mapnik"
});

/**
 * Class: OpenLayers.Layer.OSM.Osmarender
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Osmarender = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Osmarender
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.tah.openstreetmap.org/Tiles/tile/",
            "http://b.tah.openstreetmap.org/Tiles/tile/",
            "http://c.tah.openstreetmap.org/Tiles/tile/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 18 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Osmarender"
});

/**
 * Class: OpenLayers.Layer.OSM.CycleMap
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.CycleMap = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.CycleMap
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://a.andy.sandbox.cloudmade.com/tiles/cycle/",
            "http://b.andy.sandbox.cloudmade.com/tiles/cycle/",
            "http://c.andy.sandbox.cloudmade.com/tiles/cycle/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 19 }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.CycleMap"
});

/**
 * Class: OpenLayers.Layer.OSM.Maplint
 *
 * Inherits from:
 *  - <OpenLayers.Layer.OSM>
 */
OpenLayers.Layer.OSM.Maplint = OpenLayers.Class(OpenLayers.Layer.OSM, {
    /**
     * Constructor: OpenLayers.Layer.OSM.Maplint
     *
     * Parameters:
     * name - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {
        var url = [
            "http://d.tah.openstreetmap.org/Tiles/maplint/",
            "http://e.tah.openstreetmap.org/Tiles/maplint/",
            "http://f.tah.openstreetmap.org/Tiles/maplint/"
        ];
        options = OpenLayers.Util.extend({ numZoomLevels: 18, isBaseLayer: false, visibility: false }, options);
        var newArguments = [name, url, options];
        OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
    },

    CLASS_NAME: "OpenLayers.Layer.OSM.Maplint"
});
<|endoftext|>


<commit_before>$.get('candidate-template.mst', function(template) {
  Mustache.parse(template);   // optional, speeds up future uses
  window.candidate_template = template;
});

$(document).ready(function() {
  $('.cand-choice').click(function(e) {
    e.preventDefault();
    var $this = $(this);
    var $candidateModal = $('#candidateModal');
    var reg_no = $this.data('reg-no');
    var candidate_ballot_name = $this.text().replace(/\([DRLGIN]\)/, "").trim();
    var url = "http://services2.arcgis.com/tuFQUQg1xd48W6M5/arcgis/rest/services/HI_2014_primary_candidates/FeatureServer/1/query";
    //var params = { where: "Ballot_name='" + candidate_ballot_name + "'", outFields: '*', f: 'pjson' };
    var params = { where: "CC_Reg_No='" + reg_no + "'", outFields: '*', f: 'pjson' };
    $.get(url, params, function(data) {
      //console.log("got data " + data);
      //console.log("got id: " + JSON.parse(data).features[0].attributes.Candidate_ID);
      // TODO: Can do error checking here? For empty-ish data?
      var features;
      try {
        features = parse_server_response(data);
      } catch(err) {
        console.err("Received err: " + err);
      }

      var rendered = Mustache.render(window.candidate_template, features);

      $candidateModal.find('.modal-title').text(features.name_party);
      $candidateModal.find('.modal-body').html(rendered);
      $candidateModal.modal();
    });
  });

});

function parse_server_response(json_str) {
  var features = JSON.parse(json_str).features;
  if (features.length == 0) {
    throw "Invalid response - Unable to load candidate";
  }
  var data = features[0].attributes;
  // Fix email links
  if(data.cand_email) data.cand_email = "mailto:" + data.cand_email;

  var candidate_links = {
    cand_website_url: "Candidate Website",
    cand_twitter_url: "Twitter",
    cand_facebook_url: "Facebook",
    cand_linked: "LinkedIn",
    cand_email: "Email"
  };
  data.candidate_links = prepare_links(candidate_links, data);

  var campaign_finance_links = {
    csc_standard_url: "Standard Reports",
    csc_special_url: "Special Reports"
  };
  data.campaign_finance_links = prepare_links(campaign_finance_links, data);

  var other_links = {
      gov_website_url: "Official government webpage",
      lwv_website_url: "League of Women Voters of Hawaii",
      ftm_website_url: "FollowTheMoney",
      pvs_website_url: "Project Vote Smart",
      bp_website_url: "Ballotpedia",
      wp_website_url: "Wikipedia",
      cb_website_url: "Civil Beat Q&A"
  };
  if (data.office === "Governor") {
    other_links.fec_url = "FEC";
  }
  data.other_links = prepare_links(other_links, data);

  return data;
}

function prepare_links(links, data) {
  response = [];
  $.each(links, function(key,val) {
    if (data[key].length == 0) {
      response.push({url: '', text: val + ' - Unknown'});
    } else {
      response.push({url: data[key], text: val});
    }
    delete data[key];
  });
  return response;
}
<commit_msg>Pull using candidate id<commit_after>$.get('candidate-template.mst', function(template) {
  Mustache.parse(template);   // optional, speeds up future uses
  window.candidate_template = template;
});

$(document).ready(function() {
  $('.cand-choice').click(function(e) {
    e.preventDefault();
    var $this = $(this);
    var $candidateModal = $('#candidateModal');
    var cand_id = $this.data('cand-id');
    var url = "http://services2.arcgis.com/tuFQUQg1xd48W6M5/arcgis/rest/services/HI_2014_primary_candidates/FeatureServer/1/query";
    var params = { where: "Candidate_ID='" + cand_id + "'", outFields: '*', f: 'pjson' };
    $.get(url, params, function(data) {
      //console.log("got data " + data);
      //console.log("got id: " + JSON.parse(data).features[0].attributes.Candidate_ID);
      // TODO: Can do error checking here? For empty-ish data?
      var features;
      try {
        features = parse_server_response(data);
      } catch(err) {
        console.err("Received err: " + err);
      }

      var rendered = Mustache.render(window.candidate_template, features);

      $candidateModal.find('.modal-title').text(features.name_party);
      $candidateModal.find('.modal-body').html(rendered);
      $candidateModal.modal();
    });
  });

});

function parse_server_response(json_str) {
  var features = JSON.parse(json_str).features;
  if (features.length == 0) {
    throw "Invalid response - Unable to load candidate";
  }
  var data = features[0].attributes;
  // Fix email links
  if(data.cand_email) data.cand_email = "mailto:" + data.cand_email;

  var candidate_links = {
    cand_website_url: "Candidate Website",
    cand_twitter_url: "Twitter",
    cand_facebook_url: "Facebook",
    cand_linked: "LinkedIn",
    cand_email: "Email"
  };
  data.candidate_links = prepare_links(candidate_links, data);

  var campaign_finance_links = {
    csc_standard_url: "Standard Reports",
    csc_special_url: "Special Reports"
  };
  data.campaign_finance_links = prepare_links(campaign_finance_links, data);

  var other_links = {
      gov_website_url: "Official government webpage",
      lwv_website_url: "League of Women Voters of Hawaii",
      ftm_website_url: "FollowTheMoney",
      pvs_website_url: "Project Vote Smart",
      bp_website_url: "Ballotpedia",
      wp_website_url: "Wikipedia",
      cb_website_url: "Civil Beat Q&A"
  };
  if (data.office === "Governor") {
    other_links.fec_url = "FEC";
  }
  data.other_links = prepare_links(other_links, data);

  return data;
}

function prepare_links(links, data) {
  response = [];
  $.each(links, function(key,val) {
    if (data[key].length == 0) {
      response.push({url: '', text: val + ' - Unknown'});
    } else {
      response.push({url: data[key], text: val});
    }
    delete data[key];
  });
  return response;
}
<|endoftext|>


<commit_before>var Class = require( '/scripts/libraries/ptclass' ),
  Abstract = require( 'ui/Abstract' ),
  List;

/**
 * A representation of a stateful list
 * @class List
 * @constructor
 * @param {HTMLElement} element The HTML element containing this component
 * @param {Object} settings Configuration properties for this instance
 */
List =  Class.create( Abstract, ( function () {

  //RETURN METHODS OBJECT
  return {
    /**
     * PTClass constructor
     * @method initialize
     * @public
     * @param {Object} $super Pointer to superclass constructor
     * @param {Object} $element JQuery object for the element wrapped by the component
     * @param {Object} settings Configuration settings
     */
    initialize: function ( $super, $element, settings ){

      // PRIVATE CONSTANTS
      var NEXT = 'next',
          PREVIOUS = 'previous',
          VERTICAL = 'vertical',
          HORIZONTAL = 'horizontal',

      // PRIVATE INSTANCE PROPERTIES
      /**
       * Instance of Button
       * @property Button
       * @type Object
       * @private
       */
      List = this,

      /**
       * Default configuration values
       * @property defaults
       * @type Object
       * @private
       * @final
       */
      defaults = {
         /**
          * The CSS class that designates a selected list item
          * @property selectFlag
          * @default 'selected'
          * @type String
          * @final
         */
         selectFlag: 'selected',

         /**
          * Denotes the behavior of left, right, top and down arrow keys.
          * @property direction
          * @default 'horizontal'
          * @type String
          * @final
         */
         direction: HORIZONTAL
      },
      /**
       * JQuery collection of like items in the list
       * @property $items
       * @type Object
       * @private
       */
      $items,

      /**
       * Handles the keyup event and looks for keycodes 37, 38, 39 and 40.  These correspond to left, up, right and down
       * arrows.  Left and up correspond to action "previous" and right and next correspond to "next".
       * @method handleKeyup
       * @private
       * @param {Event} event An event object
       * @param {Object} item An object or a number
       * @return {Void}
       */
      handleKeyup = function(event, item) {
        var keyCode = event.keyCode,
            item = $(event.target);

        // A "vertical" list direction means that the up and down arrow keys work
        if (settings.direction === VERTICAL) {
          switch (keyCode) {
            case 38:
              List.trigger(PREVIOUS, item);
              break;
            case 40:
              List.trigger(NEXT, item);
              break;
            default:
            // Fall thru
          }
        } else {
          // By default, list direction is "horizontal" and left and right arrows work
          switch (keyCode) {
            case 37:
              List.trigger(PREVIOUS, item);
              break;
            case 39:
              List.trigger(NEXT, item);
              break;
            default:
            // Fall thru
          }
        }
      };

      // MIX THE DEFAULTS INTO THE SETTINGS VALUES
      _.defaults( settings, defaults );

      // CALL THE PARENT'S CONSTRUCTOR
      $super( $element, settings );

      //Scan for items from the provided selector, or default to the children of the container.
      if ( settings.items ) {
        if( typeof settings.items === 'string' ) {
          $items =  $element.children( settings.items ).children();
        } else {
          $items = settings.items.children();
        }
      } else {
        $items = $element.children();
      }

      // PRIVATE METHODS
      // n/a

      // PRIVILEGED METHODS

      /**
       * Append a new item to $element
       * @method append
       * @public
       * @param {Array} A jQuery Collection of $items to append
       * @return {Object} List
       */
      List.append = function( $item ) {
        $items.parent().append( $item );
        return List;
      };

      /**
       * Removes an item from $element
       * @method remove
       * @public
       * @param {Array} A jQuery Collection of $items to remove
       * @return {Object} List
       */
      List.remove = function( $item ) {
        $( $item, $items ).remove();
        return List;
      };
      /**
       * Select an item in the list
       * @method select
       * @public
       * @param {Integer|Object} item The index of the item to select, or a JQuery instance of the item.
       * @return {Object} List
       */
      List.select = function( item ) {
        var $item;

        if( typeof item === 'number' || typeof item === 'string') {
          $item = $items.eq( item );
        } else {
          $item = item;
        }

        if( $item.hasClass( settings.selectFlag ) === false ) {
          $items.filter( '.' + settings.selectFlag ).removeClass( settings.selectFlag );
          this.trigger( 'selected', [$item.addClass( settings.selectFlag ), this.index()] );
        }

        return List;
      };

      /**
       * Selects the next item in the list.
       * @method next
       * @public
       * @return {Object} List
       */
      List.next = function() {
        var increment = 0;
        if(  List.hasNext() ) {
          increment = 1;
        } else {
           List.trigger( 'max' );
        }
        List.select( $items.eq( List.index() + increment ) );

        return List;
      };

      /**
       * Selects the previous item in the list.
       * @method previous
       * @public
       * @return {Object} List
       */
      List.previous = function() {
        var decrement = 0;

        if( List.hasPrevious() ) {
          decrement = 1;
        } else {
           List.trigger( 'min' );
        }

        List.select( $items.eq( this.index() - decrement ) );
        return this;
      };

      /**
       * Selects the last item in the list.
       * @method last
       * @public
       * @return {Object} List
       */
      List.last = function() {
        List.select( $items.eq( List.size() - 1 ) );
        return List;
      };

      /**
       * Selects the first item in the list.
       * @method first
       * @public
       * @return {Object} List
       */
      List.first = function() {
        List.select( 0 );
        return List;
      };

      /**
       * Determines if there are any higher-index items in the list.
       * @method hasNext
       * @public
       * @return {Boolean} true if not at the last item in the list
       */
      List.hasNext = function() {
        return ( List.index() < List.size() - 1 );
      };

      /**
       * Determines if there are any lower-index items in the list.
       * @method hasPrevious
       * @public
       * @return {Boolean} true if not at the first item in the list
       */
      List.hasPrevious = function() {
        return ( List.index() > 0 );
      };

      /**
       * Returns the index of the selecte item.
       * @method index
       * @public
       * @return {Number} The index of the selected item
       */
      List.index = function() {
        var ndx = -1;
        _.each( $items, function( item, index ) {
          var $item = $( item );
          if( $item.hasClass( settings.selectFlag ) ) {
            ndx = index;
            return;
          }
        } );
        return ndx;
      };

      /**
       * Returns the currently-selected item.
       * @method current
       * @public
       * @return {Object} JQuery object-reference to the selected item
       */
      List.current = function() {
        return $items.eq( List.index() );
      };

      /**
       * Returns the array of list items.
       * @method items
       * @public
       * @return {Array} The array of list items
       */
      List.items = function() {
        return $items;
      };

      /**
       * Returns the number of items in the list.
       * @method size
       * @public
       * @return {Number} The number of items in the list
       */
      List.size = function() {
        return $items.length;
      };

      // EVENT BINDINGS
      $element.on( 'select', function( event, item ) {
        event.stopPropagation();
        List.select( item );
      } );
      $element.on( NEXT, function( event, item ) {
        event.stopPropagation();
        List.next();
      } );
      $element.on( PREVIOUS, function( event, item ) {
        event.stopPropagation();
        List.previous();
      } );
      $element.on( 'first', function( event, item ) {
        event.stopPropagation();
        List.first();
      } );
      $element.on( 'last', function( event, item ) {
        event.stopPropagation();
        List.last();
      } );
      $element.on('keyup', handleKeyup);

    }
  };

}() ));

//Export to CommonJS Loader
if( module && module.exports ) {
  module.exports = List;
}
<commit_msg>Updated ally support.  Set focus on the selected item.  Support for Home and End keys.  Home takes you to the first and End takes you to the last element.<commit_after>var Class = require( '/scripts/libraries/ptclass' ),
  Abstract = require( 'ui/Abstract' ),
  List;

/**
 * A representation of a stateful list
 * @class List
 * @constructor
 * @param {HTMLElement} element The HTML element containing this component
 * @param {Object} settings Configuration properties for this instance
 */
List =  Class.create( Abstract, ( function () {

  //RETURN METHODS OBJECT
  return {
    /**
     * PTClass constructor
     * @method initialize
     * @public
     * @param {Object} $super Pointer to superclass constructor
     * @param {Object} $element JQuery object for the element wrapped by the component
     * @param {Object} settings Configuration settings
     */
    initialize: function ( $super, $element, settings ){

      // PRIVATE CONSTANTS
      var NEXT = 'next',
          LAST = 'last',
          FIRST = 'first',
          PREVIOUS = 'previous',
          VERTICAL = 'vertical',
          HORIZONTAL = 'horizontal',

      // PRIVATE INSTANCE PROPERTIES
      /**
       * Instance of Button
       * @property Button
       * @type Object
       * @private
       */
      List = this,

      /**
       * Default configuration values
       * @property defaults
       * @type Object
       * @private
       * @final
       */
      defaults = {
         /**
          * The CSS class that designates a selected list item
          * @property selectFlag
          * @default 'selected'
          * @type String
          * @final
         */
         selectFlag: 'selected',

         /**
          * Denotes the behavior of left, right, top and down arrow keys.
          * @property direction
          * @default 'horizontal'
          * @type String
          * @final
         */
         direction: HORIZONTAL
      },
      /**
       * JQuery collection of like items in the list
       * @property $items
       * @type Object
       * @private
       */
      $items,

      /**
       * Handles the keyup event and looks for keycodes 37, 38, 39 and 40.  These correspond to left, up, right and down
       * arrows.  Left and up correspond to action "previous" and right and next correspond to "next".
       * @method handleKeyup
       * @private
       * @param {Event} event An event object
       * @param {Object} item An object or a number
       * @return {Void}
       */
      handleKeyup = function(event, item) {
        var keyCode = event.keyCode,
            item = $(event.target);

        // A "vertical" list direction means that the up and down arrow keys work
        if (settings.direction === VERTICAL) {
          switch (keyCode) {
            case 38: // Up arrow
              List.trigger(PREVIOUS, item);
              break;
            case 40: // Down arrow
              List.trigger(NEXT, item);
            default:
              break;
          }
        } else {
          // By default, list direction is "horizontal" and left and right arrows work
          switch (keyCode) {
            case 37: // Left arrow
              List.trigger(PREVIOUS, item);
              break;
            case 39: // Right arrow
              List.trigger(NEXT, item);
            default:
              break;
          }
        }

        switch (keyCode) {
          case 36: // Home key
            List.trigger(FIRST, item);
            break;
          case 35: // Last key
            List.trigger(LAST, item);
          default:
            break;
        }
      };

      // MIX THE DEFAULTS INTO THE SETTINGS VALUES
      _.defaults( settings, defaults );

      // CALL THE PARENT'S CONSTRUCTOR
      $super( $element, settings );

      //Scan for items from the provided selector, or default to the children of the container.
      if ( settings.items ) {
        if( typeof settings.items === 'string' ) {
          $items =  $element.children( settings.items ).children();
        } else {
          $items = settings.items.children();
        }
      } else {
        $items = $element.children();
      }

      // PRIVATE METHODS
      // n/a

      // PRIVILEGED METHODS

      /**
       * Append a new item to $element
       * @method append
       * @public
       * @param {Array} A jQuery Collection of $items to append
       * @return {Object} List
       */
      List.append = function( $item ) {
        $items.parent().append( $item );
        return List;
      };

      /**
       * Removes an item from $element
       * @method remove
       * @public
       * @param {Array} A jQuery Collection of $items to remove
       * @return {Object} List
       */
      List.remove = function( $item ) {
        $( $item, $items ).remove();
        return List;
      };
      /**
       * Select an item in the list
       * @method select
       * @public
       * @param {Integer|Object} item The index of the item to select, or a JQuery instance of the item.
       * @return {Object} List
       */
      List.select = function( item ) {
        var $item;

        if( typeof item === 'number' || typeof item === 'string') {
          $item = $items.eq( item );
        } else {
          $item = item;
        }

        if( $item.hasClass( settings.selectFlag ) === false ) {
          $items.filter( '.' + settings.selectFlag ).removeClass( settings.selectFlag );
          this.trigger( 'selected', [$item.addClass( settings.selectFlag ), this.index()] );
        }

        // Set focus to the item that you've selected
        // We do this for ally
        $item.attr("tabindex", "-1").focus();

        return List;
      };

      /**
       * Selects the next item in the list.
       * @method next
       * @public
       * @return {Object} List
       */
      List.next = function() {
        var increment = 0;
        if(  List.hasNext() ) {
          increment = 1;
        } else {
           List.trigger( 'max' );
        }
        List.select( $items.eq( List.index() + increment ) );

        return List;
      };

      /**
       * Selects the previous item in the list.
       * @method previous
       * @public
       * @return {Object} List
       */
      List.previous = function() {
        var decrement = 0;

        if( List.hasPrevious() ) {
          decrement = 1;
        } else {
           List.trigger( 'min' );
        }

        List.select( $items.eq( this.index() - decrement ) );
        return this;
      };

      /**
       * Selects the last item in the list.
       * @method last
       * @public
       * @return {Object} List
       */
      List.last = function() {
        List.select( $items.eq( List.size() - 1 ) );
        return List;
      };

      /**
       * Selects the first item in the list.
       * @method first
       * @public
       * @return {Object} List
       */
      List.first = function() {
        List.select( 0 );
        return List;
      };

      /**
       * Determines if there are any higher-index items in the list.
       * @method hasNext
       * @public
       * @return {Boolean} true if not at the last item in the list
       */
      List.hasNext = function() {
        return ( List.index() < List.size() - 1 );
      };

      /**
       * Determines if there are any lower-index items in the list.
       * @method hasPrevious
       * @public
       * @return {Boolean} true if not at the first item in the list
       */
      List.hasPrevious = function() {
        return ( List.index() > 0 );
      };

      /**
       * Returns the index of the selecte item.
       * @method index
       * @public
       * @return {Number} The index of the selected item
       */
      List.index = function() {
        var ndx = -1;
        _.each( $items, function( item, index ) {
          var $item = $( item );
          if( $item.hasClass( settings.selectFlag ) ) {
            ndx = index;
            return;
          }
        } );
        return ndx;
      };

      /**
       * Returns the currently-selected item.
       * @method current
       * @public
       * @return {Object} JQuery object-reference to the selected item
       */
      List.current = function() {
        return $items.eq( List.index() );
      };

      /**
       * Returns the array of list items.
       * @method items
       * @public
       * @return {Array} The array of list items
       */
      List.items = function() {
        return $items;
      };

      /**
       * Returns the number of items in the list.
       * @method size
       * @public
       * @return {Number} The number of items in the list
       */
      List.size = function() {
        return $items.length;
      };

      // EVENT BINDINGS
      $element.on( 'select', function( event, item ) {
        event.stopPropagation();
        List.select( item );
      } );
      $element.on( NEXT, function( event, item ) {
        event.stopPropagation();
        List.next();
      } );
      $element.on( PREVIOUS, function( event, item ) {
        event.stopPropagation();
        List.previous();
      } );
      $element.on( FIRST, function( event, item ) {
        event.stopPropagation();
        List.first();
      } );
      $element.on( LAST, function( event, item ) {
        event.stopPropagation();
        List.last();
      } );
      $element.on('keyup', handleKeyup);

    }
  };

}() ));

//Export to CommonJS Loader
if( module && module.exports ) {
  module.exports = List;
}
<|endoftext|>


<commit_before>let SizeLimitError = require('size-limit/size-limit-error')
let { existsSync } = require('fs')
let { promisify } = require('util')
let { join } = require('path')
let readFile = promisify(require('fs').readFile)
let rimraf = promisify(require('rimraf'))

let [webpack] = require('../')
let [file] = require('../../file')

const ROOT_CONFIG = join(__dirname, '..', '..', '.size-limit.json')
const DIST = join(process.cwd(), 'dist')

function fixture (name) {
  return join(__dirname, 'fixtures', name)
}

async function run (config) {
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    await file.step60(config, config.checks[0])
    await webpack.step61(config, config.checks[0])
  } finally {
    await webpack.finally(config, config.checks[0])
  }
}

afterEach(async () => {
  await rimraf(DIST)
  jest.clearAllMocks()
})

it('uses webpack to make bundle', async () => {
  let config = {
    checks: [
      { path: [fixture('big.js')] }
    ]
  }
  await run(config)
  expect(config).toEqual({
    checks: [
      {
        path: [fixture('big.js')],
        webpackOutput: config.checks[0].webpackOutput,
        webpackConfig: config.checks[0].webpackConfig,
        bundles: [join(config.checks[0].webpackOutput, 'index.js')],
        size: 2827
      }
    ]
  })
  expect(config.checks[0].webpackOutput).toContain('size-limit-')
  expect(typeof config.checks[0].webpackConfig).toEqual('object')
  expect(existsSync(config.checks[0].webpackOutput)).toBeFalsy()
})

it('supports non-JS require', async () => {
  let config = {
    checks: [
      { path: [fixture('nonjs.js')] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toBeGreaterThan(1600)
  expect(config.checks[0].size).toBeLessThan(1700)
})

it('supports ignore', async () => {
  let config = {
    checks: [
      { path: fixture('big.js'), ignore: ['redux'] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(27)
})

it('supports custom webpack config', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js') }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(1840)
})

it('supports custom entry', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js'), entry: ['small'] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(688)
})

it('throws error on unknown entry', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js'), entry: ['unknown'] }
    ]
  }
  let err
  try {
    await run(config)
  } catch (e) {
    err = e
  }
  expect(err).toEqual(new SizeLimitError('unknownEntry', 'unknown'))
  expect(existsSync(config.checks[0].webpackOutput)).toBeFalsy()
})

it('allows to disable webpack', async () => {
  let config = {
    checks: [
      { path: [fixture('big.js')], webpack: false }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(55)
})

it('allows to disable gzip', async () => {
  let config = {
    checks: [
      { path: [fixture('small.js')], gzip: false }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(36)
})

it('throws on missed file plugin', async () => {
  let config = {
    checks: [
      { path: [fixture('small.js')] }
    ]
  }
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    let err
    try {
      await webpack.step61(config, config.checks[0])
    } catch (e) {
      err = e
    }
    expect(err).toEqual(new SizeLimitError('missedPlugin', 'file'))
  } finally {
    await webpack.finally(config, config.checks[0])
  }
})

it('supports --why', async () => {
  jest.spyOn(console, 'log').mockImplementation(() => true)
  let config = {
    project: 'superProject',
    why: true,
    checks: [
      { path: [fixture('big.js')] }
    ]
  }
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    let reportFile = join(config.checks[0].webpackOutput, 'report.html')
    let reportHTML = await readFile(reportFile)
    expect(reportHTML.toString()).toContain('superProject')
  } finally {
    await webpack.finally(config, config.checks[0])
  }
})

it('supports --saveBundle', async () => {
  let config = {
    saveBundle: DIST,
    checks: [
      { path: [fixture('small.js')] }
    ]
  }
  await run(config)
  expect(existsSync(join(DIST, 'index.js'))).toBeTruthy()
})

it('throws on webpack error', async () => {
  let config = {
    checks: [
      { path: [fixture('unknown.js')] }
    ]
  }
  let err
  try {
    await run(config)
  } catch (e) {
    err = e
  }
  expect(err.message).toContain('unknown.js')
})
<commit_msg>Update test<commit_after>let SizeLimitError = require('size-limit/size-limit-error')
let { existsSync } = require('fs')
let { promisify } = require('util')
let { join } = require('path')
let readFile = promisify(require('fs').readFile)
let rimraf = promisify(require('rimraf'))

let [webpack] = require('../')
let [file] = require('../../file')

const ROOT_CONFIG = join(__dirname, '..', '..', '.size-limit.json')
const DIST = join(process.cwd(), 'dist')

function fixture (name) {
  return join(__dirname, 'fixtures', name)
}

async function run (config) {
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    await file.step60(config, config.checks[0])
    await webpack.step61(config, config.checks[0])
  } finally {
    await webpack.finally(config, config.checks[0])
  }
}

afterEach(async () => {
  await rimraf(DIST)
  jest.clearAllMocks()
})

it('uses webpack to make bundle', async () => {
  let config = {
    checks: [
      { path: [fixture('big.js')] }
    ]
  }
  await run(config)
  expect(config).toEqual({
    checks: [
      {
        path: [fixture('big.js')],
        webpackOutput: config.checks[0].webpackOutput,
        webpackConfig: config.checks[0].webpackConfig,
        bundles: [join(config.checks[0].webpackOutput, 'index.js')],
        size: 2832
      }
    ]
  })
  expect(config.checks[0].webpackOutput).toContain('size-limit-')
  expect(typeof config.checks[0].webpackConfig).toEqual('object')
  expect(existsSync(config.checks[0].webpackOutput)).toBeFalsy()
})

it('supports non-JS require', async () => {
  let config = {
    checks: [
      { path: [fixture('nonjs.js')] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toBeGreaterThan(1600)
  expect(config.checks[0].size).toBeLessThan(1700)
})

it('supports ignore', async () => {
  let config = {
    checks: [
      { path: fixture('big.js'), ignore: ['redux'] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(27)
})

it('supports custom webpack config', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js') }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(1840)
})

it('supports custom entry', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js'), entry: ['small'] }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(688)
})

it('throws error on unknown entry', async () => {
  let config = {
    configPath: ROOT_CONFIG,
    checks: [
      { config: fixture('webpack.config.js'), entry: ['unknown'] }
    ]
  }
  let err
  try {
    await run(config)
  } catch (e) {
    err = e
  }
  expect(err).toEqual(new SizeLimitError('unknownEntry', 'unknown'))
  expect(existsSync(config.checks[0].webpackOutput)).toBeFalsy()
})

it('allows to disable webpack', async () => {
  let config = {
    checks: [
      { path: [fixture('big.js')], webpack: false }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(55)
})

it('allows to disable gzip', async () => {
  let config = {
    checks: [
      { path: [fixture('small.js')], gzip: false }
    ]
  }
  await run(config)
  expect(config.checks[0].size).toEqual(36)
})

it('throws on missed file plugin', async () => {
  let config = {
    checks: [
      { path: [fixture('small.js')] }
    ]
  }
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    let err
    try {
      await webpack.step61(config, config.checks[0])
    } catch (e) {
      err = e
    }
    expect(err).toEqual(new SizeLimitError('missedPlugin', 'file'))
  } finally {
    await webpack.finally(config, config.checks[0])
  }
})

it('supports --why', async () => {
  jest.spyOn(console, 'log').mockImplementation(() => true)
  let config = {
    project: 'superProject',
    why: true,
    checks: [
      { path: [fixture('big.js')] }
    ]
  }
  try {
    await webpack.step20(config, config.checks[0])
    await webpack.step40(config, config.checks[0])
    let reportFile = join(config.checks[0].webpackOutput, 'report.html')
    let reportHTML = await readFile(reportFile)
    expect(reportHTML.toString()).toContain('superProject')
  } finally {
    await webpack.finally(config, config.checks[0])
  }
})

it('supports --saveBundle', async () => {
  let config = {
    saveBundle: DIST,
    checks: [
      { path: [fixture('small.js')] }
    ]
  }
  await run(config)
  expect(existsSync(join(DIST, 'index.js'))).toBeTruthy()
})

it('throws on webpack error', async () => {
  let config = {
    checks: [
      { path: [fixture('unknown.js')] }
    ]
  }
  let err
  try {
    await run(config)
  } catch (e) {
    err = e
  }
  expect(err.message).toContain('unknown.js')
})
<|endoftext|>


<commit_before>//Based on https://github.com/arasatasaygin/is.js/blob/master/is.js
require('../polyfill/index');

var userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '',
    vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '',
    appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';

//Browsers
var isChrome = /chrome|chromium/i.test(userAgent) && /google inc/.test(vendor),
    isFirefox = /firefox/i.test(userAgent),
    isIE = /msie/i.test(userAgent) || "ActiveXObject" in window,
    isOpera = /^Opera\//.test(userAgent) || /\x20OPR\//.test(userAgent),
    isSafari = /safari/i.test(userAgent) && /apple computer/i.test(vendor);

//Devices && OS
var isIphone = /iphone/i.test(userAgent),
    isIpad = /ipad/i.test(userAgent),
    isIpod = /ipod/i.test(userAgent),
    isAndroid = /android/i.test(userAgent),
    isAndroidPhone = /android/i.test(userAgent) && /mobile/i.test(userAgent),
    isAndroidTablet = /android/i.test(userAgent) && !/mobile/i.test(userAgent),
    isLinux = /linux/i.test(appVersion),
    isMac = /mac/i.test(appVersion),
    isWindow = /win/i.test(appVersion),
    isWindowPhone = isWindow && /phone/i.test(userAgent),
    isWindowTablet = isWindow && !isWindowPhone && /touch/i.test(userAgent),
    isMobile = isIphone || isIpod|| isAndroidPhone || isWindowPhone,
    isTablet = isIpad || isAndroidTablet || isWindowTablet,
    isDesktop = !isMobile && !isTablet,
    isTouchDevice = 'ontouchstart' in window ||'DocumentTouch' in window && document instanceof DocumentTouch,
    isCocoon = !!navigator.isCocoonJS,
    isNodeWebkit = typeof process === "object" && process.title === "node" && typeof global === "object",
    isEjecta = !!window.ejecta,
    isCrosswalk = /Crosswalk/.test(userAgent),
    isCordova = !!window.cordova,
    isElectron = process && process.versions && (process.versions['electron'] || process.versions['atom-shell']);

var hasVibrate = !!navigator.vibrate && (isMobile || isTablet),
    hasMouseWheel = 'onwheel' in window || 'onmousewheel' in window || 'MouseScrollEvent' in window,
    hasAccelerometer = 'DeviceMotionEvent' in window;

//FullScreen
var div = document.createElement('div');
var fullScreenRequest = div.requestFullscreen || div.webkitRequestFullScreen || div.msRequestFullScreen || div.mozRequestFullScreen,
    fullScreenCancel = document.cancelFullScreen || document.exitFullScreen || document.webkitCancelFullScreen || document.msCancelFullScreen || document.mozCancelFullScreen,
    hasFullScreen = !!(fullScreenRequest && fullScreenCancel);

//TODO: Check audio support, fullScreensupport, etc...

var Device = module.exports = {
    isChrome : isChrome,
    isFirefox : isFirefox,
    isIE : isIE,
    isOpera : isOpera,
    isSafari : isSafari,
    isIphone : isIphone,
    isIpad : isIpad,
    isIpod : isIpod,
    isAndroid : isAndroid,
    isAndroidPhone : isAndroidPhone,
    isAndroidTablet : isAndroidTablet,
    isLinux : isLinux,
    isMac : isMac,
    isWindow : isWindow,
    isWindowPhone : isWindowPhone,
    isWindowTablet : isWindowTablet,
    isMobile : isMobile,
    isTablet : isTablet,
    isDesktop : isDesktop,
    isTouchDevice : isTouchDevice,
    isCocoon : isCocoon,
    isNodeWebkit : isNodeWebkit,
    isEjecta : isEjecta,
    isCordova : isCordova,
    isCrosswalk : isCrosswalk,
    isElectron : isElectron,
    isAtomShell : isElectron, //TODO: Remove soon, when atom-shell (version) is deprecated

    //isOnline : navigator.onLine,
    hasVibrate : hasVibrate,
    hasMouseWheel : hasMouseWheel,
    hasFullScreen : hasFullScreen,
    hasAccelerometer : hasAccelerometer,

    fullScreenRequest : fullScreenRequest ? fullScreenRequest.name : undefined,
    fullScreenCancel : fullScreenCancel ? fullScreenCancel.name : undefined,

    getMouseWheelEvent : function(){
        if(!hasMouseWheel)return;
        var evt;
        if('onwheel' in window){
            evt = 'wheel';
        }else if('onmousewheel' in window){
            evt = 'mousewheel';
        }else if('MouseScrollEvent' in window){
            evt = 'DOMMouseScroll';
        }

        return evt;
    },

    vibrate : function(value){
        if(hasVibrate){
            navigator.vibrate(value);
        }
    }
};

Object.defineProperties(Device, {
    isOnline: {
        get : function() {
            return window.navigator.onLine;
        }
    }
});<commit_msg>minor fix<commit_after>//Based on https://github.com/arasatasaygin/is.js/blob/master/is.js
require('../polyfill/index');

var userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '',
    vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '',
    appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';

//Browsers
var isChrome = /chrome|chromium/i.test(userAgent) && /google inc/.test(vendor),
    isFirefox = /firefox/i.test(userAgent),
    isIE = /msie/i.test(userAgent) || "ActiveXObject" in window,
    isOpera = /^Opera\//.test(userAgent) || /\x20OPR\//.test(userAgent),
    isSafari = /safari/i.test(userAgent) && /apple computer/i.test(vendor);

//Devices && OS
var isIphone = /iphone/i.test(userAgent),
    isIpad = /ipad/i.test(userAgent),
    isIpod = /ipod/i.test(userAgent),
    isAndroid = /android/i.test(userAgent),
    isAndroidPhone = /android/i.test(userAgent) && /mobile/i.test(userAgent),
    isAndroidTablet = /android/i.test(userAgent) && !/mobile/i.test(userAgent),
    isLinux = /linux/i.test(appVersion),
    isMac = /mac/i.test(appVersion),
    isWindow = /win/i.test(appVersion),
    isWindowPhone = isWindow && /phone/i.test(userAgent),
    isWindowTablet = isWindow && !isWindowPhone && /touch/i.test(userAgent),
    isMobile = isIphone || isIpod|| isAndroidPhone || isWindowPhone,
    isTablet = isIpad || isAndroidTablet || isWindowTablet,
    isDesktop = !isMobile && !isTablet,
    isTouchDevice = 'ontouchstart' in window ||'DocumentTouch' in window && document instanceof DocumentTouch,
    isCocoon = !!navigator.isCocoonJS,
    isNodeWebkit = !!(typeof process === "object" && process.title === "node" && typeof global === "object"),
    isEjecta = !!window.ejecta,
    isCrosswalk = /Crosswalk/.test(userAgent),
    isCordova = !!window.cordova,
    isElectron = !!(process && process.versions && (process.versions.electron || process.versions['atom-shell']));

var hasVibrate = !!navigator.vibrate && (isMobile || isTablet),
    hasMouseWheel = 'onwheel' in window || 'onmousewheel' in window || 'MouseScrollEvent' in window,
    hasAccelerometer = 'DeviceMotionEvent' in window;

//FullScreen
var div = document.createElement('div');
var fullScreenRequest = div.requestFullscreen || div.webkitRequestFullScreen || div.msRequestFullScreen || div.mozRequestFullScreen,
    fullScreenCancel = document.cancelFullScreen || document.exitFullScreen || document.webkitCancelFullScreen || document.msCancelFullScreen || document.mozCancelFullScreen,
    hasFullScreen = !!(fullScreenRequest && fullScreenCancel);

//TODO: Check audio support, fullScreensupport, etc...

var Device = module.exports = {
    isChrome : isChrome,
    isFirefox : isFirefox,
    isIE : isIE,
    isOpera : isOpera,
    isSafari : isSafari,
    isIphone : isIphone,
    isIpad : isIpad,
    isIpod : isIpod,
    isAndroid : isAndroid,
    isAndroidPhone : isAndroidPhone,
    isAndroidTablet : isAndroidTablet,
    isLinux : isLinux,
    isMac : isMac,
    isWindow : isWindow,
    isWindowPhone : isWindowPhone,
    isWindowTablet : isWindowTablet,
    isMobile : isMobile,
    isTablet : isTablet,
    isDesktop : isDesktop,
    isTouchDevice : isTouchDevice,
    isCocoon : isCocoon,
    isNodeWebkit : isNodeWebkit,
    isEjecta : isEjecta,
    isCordova : isCordova,
    isCrosswalk : isCrosswalk,
    isElectron : isElectron,
    isAtomShell : isElectron, //TODO: Remove soon, when atom-shell (version) is deprecated

    //isOnline : navigator.onLine,
    hasVibrate : hasVibrate,
    hasMouseWheel : hasMouseWheel,
    hasFullScreen : hasFullScreen,
    hasAccelerometer : hasAccelerometer,

    fullScreenRequest : fullScreenRequest ? fullScreenRequest.name : undefined,
    fullScreenCancel : fullScreenCancel ? fullScreenCancel.name : undefined,

    getMouseWheelEvent : function(){
        if(!hasMouseWheel)return;
        var evt;
        if('onwheel' in window){
            evt = 'wheel';
        }else if('onmousewheel' in window){
            evt = 'mousewheel';
        }else if('MouseScrollEvent' in window){
            evt = 'DOMMouseScroll';
        }

        return evt;
    },

    vibrate : function(value){
        if(hasVibrate){
            navigator.vibrate(value);
        }
    }
};

Object.defineProperties(Device, {
    isOnline: {
        get : function() {
            return window.navigator.onLine;
        }
    }
});<|endoftext|>