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

var Metadata;

(function()
{
    "use strict";

    Metadata =
    {
        container: {},
        reset: reset,
        dialog: dialog,
        formatScenarioName: formatScenarioName,
        addOrExtendAuthor: addOrExtendAuthor
    };

    $(document).ready(function()
    {
        $("#editMetadata").on('click', dialog);
        reset();
    });

    // Resets the metadata to the default
    function reset()
    {
        Metadata.container =
        {
            name: "",
            version: 0,
            difficulty: "medium",
            description: Config.container.settings.description.type.defaultValue,
            authors: [],
            propertyValues: Config.getNewDefaultPropertyValues(['independent'])
        };

        if (Config.container.settings.languages.sequence.length > 0)
        {
            Metadata.container.language = Config.container.settings.languages.sequence[0];
        }
    }

    function dialog()
    {
        var metadataDialog = $('
', { id: "metadata" });

        var metadataContainer = $('
');

        var generalHeader = $('
').append($('', { colspan: 2 }).append($('
', { text: i18next.t('metadata:general') })));
        metadataContainer.append(generalHeader);

        var generalContainer = $('
');
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioName", text: i18next.t('metadata:scenario_name') })))
            .append($('').append($('<input>', { type: 'text', id: "scenarioName" }))));
        if (Config.container.settings.languages.sequence.length > 0)
        {
            var scenarioLanguageSelect = $('<select>', { id: "scenarioLanguage" });
            Config.container.settings.languages.sequence.forEach(function(language)
            {
                scenarioLanguageSelect.append($('<option>', { value: language.code, text: language.name }));
            });
            var scenarioLanguageContainer = $('
');
            scenarioLanguageContainer.append($('').append($('', { for: "scenarioLanguage", text: i18next.t('metadata:language') + ':' })));
            scenarioLanguageContainer.append($('').append(scenarioLanguageSelect));
            generalContainer.append(scenarioLanguageContainer);
        }
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioDifficulty", text: i18next.t('metadata:difficulty.translation') })))
            .append($('').append($('<select>', { id: "scenarioDifficulty" } )
                .append($('<option>', { value: "very_easy", text: i18next.t('metadata:difficulty.very_easy') }))
                .append($('<option>', { value: "easy", text: i18next.t('metadata:difficulty.easy') }))
                .append($('<option>', { value: "medium", text: i18next.t('metadata:difficulty.medium') }))
                .append($('<option>', { value: "difficult", text: i18next.t('metadata:difficulty.hard') }))
                .append($('<option>', { value: "very_difficult", text: i18next.t('metadata:difficulty.very_hard') })))));
        var scenarioDescription = $('<textarea>', { id: "scenarioDescription", class: "description" } );
        scenarioDescription.attr('maxlength', Config.container.settings.description.type.maxLength);
        if (Config.container.settings.description.type.markdown) Utils.attachMarkdownTooltip(scenarioDescription);
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioDescription", text: i18next.t('common:description') })))
            .append($('').append(scenarioDescription)));
        metadataContainer.append(generalContainer);

        var authorsHeader = $('
').append($('').append($('
', { text: i18next.t('metadata:authors') })));
        metadataContainer.append(authorsHeader);

        var authorsContainer = $('
').append($('
')
            .append($('')).append($('
', { text: i18next.t('common:name') }))
            .append($('')).append($('
', { text: i18next.t('metadata:email') }))
            .append($('')).append($('
', { text: i18next.t('metadata:start_date') }))
            .append($('')).append($('
', { text: i18next.t('metadata:end_date') })));
        metadataContainer.append($('
').append($('
').append($('', { colspan: 2 }).append(authorsContainer))));

        metadataContainer.append($('
'));
        metadataContainer.append($('
', { id: "meta-property-values" }));

        metadataContainer.append($('
').append($('', { colspan: 2 }).append($('
', { text: i18next.t('common:characters') }))));
        metadataContainer.append($('
', { id: "meta-character-property-values" }).append($('
').append($('', { colspan: 2 , id: "character-tabs" }))));

        metadataDialog.append(metadataContainer);

        metadataDialog.dialog(
        {
            title: i18next.t('metadata:title'),
            height: Constants.heightMetaScreen,
            width: Constants.widthMetaScreen,
            modal: true,
            buttons: [
            {
                text: i18next.t('common:confirm'),
                click: function()
                {
                    save();
                    $(this).dialog('close');
                }
            },
            {
                text: i18next.t('common:cancel'),
                click: function()
                {
                    $(this).dialog('close');
                }
            }],
            close: function()
            {
                $("#main").focus();
                metadataDialog.remove();
            }
        });

        // Show the stored values for the metadata.
        $("#scenarioName").val(Metadata.container.name);
        if (Metadata.container.language) $("#scenarioLanguage").val(Metadata.container.language.code).trigger('change');
        $("#scenarioDifficulty").val(Metadata.container.difficulty);
        scenarioDescription.val(Metadata.container.description);

        authorsContainer.find("tr").not("tr:first").remove();
        if (Metadata.container.authors.length > 0)
        {
            authorsHeader.show();
            authorsContainer.show();
            Metadata.container.authors.forEach(function(author)
            {
                var authorContainer = $('
');
                authorContainer.append($('', { text: author.name }));
                authorContainer.append($('', { text: author.email ? author.email : "" }));
                authorContainer.append($('', { text: author.startDate }));
                authorContainer.append($('', { text: author.endDate ? author.endDate : "" }));
                authorsContainer.append(authorContainer);
            });
        }
        else
        {
            authorsHeader.hide();
            authorsContainer.hide();
        }

        var anyPropertyShown = false;
        var hStartLevel = 3;

        var propertyValuesEl = $('#meta-property-values');
        var showPropertyItem = function (propertyItem, hLevel, tableBody, idPrefix)
        {
            if (propertyItem.scopes.statementScope !== 'independent') return;
            if (propertyItem.kind === 'section')
            {
                var sectionTable = $('
');

                var sectionTableHeader = $('
').append($('').append($('<h' + hLevel + '>', { text: propertyItem.name })));
                sectionTable.append(sectionTableHeader);

                var sectionTableBody = $('
');
                sectionTable.append(sectionTableBody);

                var sectionContainer = $('
').append(sectionTable);
                if (hLevel !== hStartLevel) sectionContainer.addClass("subsection");
                tableBody.append($('
').append($('').append(sectionContainer)));

                propertyItem.sequence.forEach(function (subItem)
                {
                     showPropertyItem(subItem, hLevel + 1, sectionTableBody, idPrefix);
                });
            }
            else
            {
                var propertyHeader = $('');
                var controlHtmlId = idPrefix + '-' + propertyItem.id;
                var controlFirst = propertyItem.type.labelControlOrder === Types.labelControlOrders.singleLineContainerLabel ||
                                   propertyItem.type.labelControlOrder === Types.labelControlOrders.twoLineContainerLabel;
                propertyHeader.append($('', { text: propertyItem.name + (controlFirst ? '' : ':'), 'for': controlHtmlId }));

                var propertyData = $('', { id: idPrefix + '-container-' + propertyItem.id });
                propertyItem.type.appendControlTo(propertyData, controlHtmlId);

                var propertyRow = $('
');
                var additionalPropertyRow;
                switch (propertyItem.type.labelControlOrder)
                {
                    case Types.labelControlOrders.singleLineLabelContainer:
                        propertyRow.append(propertyHeader);
                        propertyRow.append(propertyData);
                        break;
                    case Types.labelControlOrders.singleLineContainerLabel:
                        propertyRow.append(propertyHeader);
                        propertyRow.prepend(propertyData);
                        break;
                    case Types.labelControlOrders.container:
                        propertyData.prop('colspan', "2");
                        propertyRow.append(propertyData);
                        break;
                    case Types.labelControlOrders.twoLineLabelContainer:
                        propertyRow.append(propertyHeader);
                        additionalPropertyRow = $('
').append(propertyData);
                        break;
                    case Types.labelControlOrders.twoLineContainerLabel:
                        additionalPropertyRow = propertyRow.append(propertyHeader);
                        propertyRow = $('
').append(propertyData);
                        break;
                    default:
                        console.error("Not implemented");
                        break;
                }
                tableBody.append(propertyRow);
                if (additionalPropertyRow) tableBody.append(additionalPropertyRow);

                anyPropertyShown = true;
            }
        };
        Config.container.properties.sequence.forEach(function (propertyItem)
        {
            showPropertyItem(propertyItem, hStartLevel, propertyValuesEl, propertyValuesEl.attr('id'));
        });
        propertyValuesEl.toggle(anyPropertyShown);

        anyPropertyShown = false;

        var characterPropertyValuesEl = $('#meta-character-property-values');
        var characterTabs = $("#character-tabs");
        var characterList = Config.container.characters.sequence.length > 1 ? $('
') : $('
');
        characterTabs.append(characterList);

        Config.container.characters.sequence.forEach(function (character)
        {
            var characterTabId = characterPropertyValuesEl.attr('id') + '-' + character.id;

            var characterHeaderText = character.name ? character.name : character.id;

            if (Config.container.characters.sequence.length > 1)
            {
                // Make a character tab with a link to the div it contains
                var li = $('
').append($('', { href:'#' + characterTabId, value: character.id, text: characterHeaderText }));
                characterList.append(li);
            }
            else
            {
                var characterHeader = $('
', { text: characterHeaderText });
                characterList.append(characterHeader);
            }

            var characterTab = $('
', { id: characterTabId });
            characterTabs.append(characterTab);

            Config.container.characters.properties.sequence.forEach(function(propertyItem)
            {
                showPropertyItem(propertyItem, hStartLevel, characterTab, characterTabId);
            });

            Config.container.characters.byId[character.id].properties.sequence.forEach(function(propertyItem)
            {
                showPropertyItem(propertyItem, hStartLevel, characterTab, characterTabId);
            });
        });

        if (Config.container.characters.sequence.length > 1)
        {
            characterTabs.tabs(
            {
                active: 0,
                collapsible: true
            });
        }
        characterPropertyValuesEl.toggle(anyPropertyShown);

        var setPropertyInDOM = function(propertyValues, propertyContainerId, property)
        {
            if (property.scopes.statementScope !== "independent") return;
            property.type.setInDOM($(propertyContainerId + '-' + Utils.escapeSelector(property.id)), propertyValues[property.id]);
        };
        var propertyId, characterId, property;
        for (propertyId in Config.container.properties.byId)
        {
            property = Config.container.properties.byId[propertyId];
            setPropertyInDOM(Metadata.container.propertyValues.characterIndependent, "#meta-property-values-container", property);
        }
        for (propertyId in Config.container.characters.properties.byId)
        {
            for (characterId in Config.container.characters.byId)
            {
                property = Config.container.characters.properties.byId[propertyId];
                setPropertyInDOM(Metadata.container.propertyValues.perCharacter[characterId], "#meta-character-property-values-" + Utils.escapeSelector(characterId) + "-container", property);
            }
        }
        for (characterId in Config.container.characters.byId)
        {
            for (propertyId in Config.container.characters.byId[characterId].properties.byId)
            {
                property = Config.container.characters.byId[characterId].properties.byId[propertyId];
                setPropertyInDOM(Metadata.container.propertyValues.perCharacter[characterId], "#meta-character-property-values-" + Utils.escapeSelector(characterId) + "-container", property);
            }
        }
    }

    function save()
    {
        Main.unsavedChanges = true;

        var previouslySelectedElement = Main.selectedElement;
        Main.selectElement(null);

        // Save all values in the dialog to the metaObject
        Metadata.container.name = formatScenarioName($("#scenarioName").val());
        $('#scenarioNameTab .scenarioName').text(Metadata.container.name);
        var languageCode = $("#scenarioLanguage").val();
        if (languageCode) Metadata.container.language = Config.container.settings.languages.byCode[languageCode];
        Metadata.container.difficulty = $("#scenarioDifficulty").val();
        Metadata.container.description = $("#scenarioDescription").val();

        var propertyId, characterId, property;
        for (propertyId in Config.container.properties.byId)
        {
            property = Config.container.properties.byId[propertyId];
            if (property.scopes.statementScope !== "independent") continue;
            Metadata.container.propertyValues.characterIndependent[property.id] =
                property.type.getFromDOM($("#meta-property-values-container-" + property.id));
        }
        for (propertyId in Config.container.characters.properties.byId)
        {
            for (characterId in Config.container.characters.byId)
            {
                property = Config.container.characters.properties.byId[propertyId];
                if (property.scopes.statementScope !== "independent") continue;
                Metadata.container.propertyValues.perCharacter[characterId][property.id] =
                    property.type.getFromDOM($("#meta-character-property-values-" + characterId + "-container-" + property.id));
            }
        }
        for (characterId in Config.container.characters.byId)
        {
            for (propertyId in Config.container.characters.byId[characterId].properties.byId)
            {
                property = Config.container.characters.byId[characterId].properties.byId[propertyId];
                if (property.scopes.statementScope !== "independent") continue;
                Metadata.container.propertyValues.perCharacter[characterId][property.id] =
                    property.type.getFromDOM($("#meta-character-property-values-" + characterId + "-container-" + property.id));
            }
        }

        Main.selectElement(previouslySelectedElement);
    }

    function formatScenarioName(scenarioName)
    {
        scenarioName = scenarioName.trim().substr(0, 35);
        if (scenarioName !== "")
        {
            return scenarioName;
        }
        else
        {
            return Metadata.container.name;
        }
    }

    function addOrExtendAuthor(name, email, date, setEndDateToDate)
    {
        var found = false;
        Metadata.container.authors.forEach(function(existingAuthor)
        {
            if (existingAuthor.name === name)
            {
                if (email) existingAuthor.email = email;
                if (setEndDateToDate) existingAuthor.endDate = date;
                found = true;
            }
        });

        if (!found)
        {
            var author = {};
            author.name = name;
            if (email) author.email = email;
            author.startDate = date;
            if (setEndDateToDate) author.endDate = date;

            Metadata.container.authors.push(author);
        }
    }
})();
<commit_msg>Fixed author headers<commit_after>/* ©Copyright Utrecht University (Department of Information and Computing Sciences) */

var Metadata;

(function()
{
    "use strict";

    Metadata =
    {
        container: {},
        reset: reset,
        dialog: dialog,
        formatScenarioName: formatScenarioName,
        addOrExtendAuthor: addOrExtendAuthor
    };

    $(document).ready(function()
    {
        $("#editMetadata").on('click', dialog);
        reset();
    });

    // Resets the metadata to the default
    function reset()
    {
        Metadata.container =
        {
            name: "",
            version: 0,
            difficulty: "medium",
            description: Config.container.settings.description.type.defaultValue,
            authors: [],
            propertyValues: Config.getNewDefaultPropertyValues(['independent'])
        };

        if (Config.container.settings.languages.sequence.length > 0)
        {
            Metadata.container.language = Config.container.settings.languages.sequence[0];
        }
    }

    function dialog()
    {
        var metadataDialog = $('
', { id: "metadata" });

        var metadataContainer = $('
');

        var generalHeader = $('
').append($('', { colspan: 2 }).append($('
', { text: i18next.t('metadata:general') })));
        metadataContainer.append(generalHeader);

        var generalContainer = $('
');
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioName", text: i18next.t('metadata:scenario_name') })))
            .append($('').append($('<input>', { type: 'text', id: "scenarioName" }))));
        if (Config.container.settings.languages.sequence.length > 0)
        {
            var scenarioLanguageSelect = $('<select>', { id: "scenarioLanguage" });
            Config.container.settings.languages.sequence.forEach(function(language)
            {
                scenarioLanguageSelect.append($('<option>', { value: language.code, text: language.name }));
            });
            var scenarioLanguageContainer = $('
');
            scenarioLanguageContainer.append($('').append($('', { for: "scenarioLanguage", text: i18next.t('metadata:language') + ':' })));
            scenarioLanguageContainer.append($('').append(scenarioLanguageSelect));
            generalContainer.append(scenarioLanguageContainer);
        }
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioDifficulty", text: i18next.t('metadata:difficulty.translation') })))
            .append($('').append($('<select>', { id: "scenarioDifficulty" } )
                .append($('<option>', { value: "very_easy", text: i18next.t('metadata:difficulty.very_easy') }))
                .append($('<option>', { value: "easy", text: i18next.t('metadata:difficulty.easy') }))
                .append($('<option>', { value: "medium", text: i18next.t('metadata:difficulty.medium') }))
                .append($('<option>', { value: "difficult", text: i18next.t('metadata:difficulty.hard') }))
                .append($('<option>', { value: "very_difficult", text: i18next.t('metadata:difficulty.very_hard') })))));
        var scenarioDescription = $('<textarea>', { id: "scenarioDescription", class: "description" } );
        scenarioDescription.attr('maxlength', Config.container.settings.description.type.maxLength);
        if (Config.container.settings.description.type.markdown) Utils.attachMarkdownTooltip(scenarioDescription);
        generalContainer.append($('
')
            .append($('').append($('', { for: "scenarioDescription", text: i18next.t('common:description') })))
            .append($('').append(scenarioDescription)));
        metadataContainer.append(generalContainer);

        var authorsHeader = $('
').append($('').append($('
', { text: i18next.t('metadata:authors') })));
        metadataContainer.append(authorsHeader);

        var authorsContainer = $('
').append($('
')
            .append($('').append($('
', { text: i18next.t('common:name') })))
            .append($('').append($('
', { text: i18next.t('metadata:email') })))
            .append($('').append($('
', { text: i18next.t('metadata:start_date') })))
            .append($('').append($('
', { text: i18next.t('metadata:end_date') }))));
        metadataContainer.append($('
').append($('
').append($('', { colspan: 2 }).append(authorsContainer))));

        metadataContainer.append($('
'));
        metadataContainer.append($('
', { id: "meta-property-values" }));

        metadataContainer.append($('
').append($('', { colspan: 2 }).append($('
', { text: i18next.t('common:characters') }))));
        metadataContainer.append($('
', { id: "meta-character-property-values" }).append($('
').append($('', { colspan: 2 , id: "character-tabs" }))));

        metadataDialog.append(metadataContainer);

        metadataDialog.dialog(
        {
            title: i18next.t('metadata:title'),
            height: Constants.heightMetaScreen,
            width: Constants.widthMetaScreen,
            modal: true,
            buttons: [
            {
                text: i18next.t('common:confirm'),
                click: function()
                {
                    save();
                    $(this).dialog('close');
                }
            },
            {
                text: i18next.t('common:cancel'),
                click: function()
                {
                    $(this).dialog('close');
                }
            }],
            close: function()
            {
                $("#main").focus();
                metadataDialog.remove();
            }
        });

        // Show the stored values for the metadata.
        $("#scenarioName").val(Metadata.container.name);
        if (Metadata.container.language) $("#scenarioLanguage").val(Metadata.container.language.code).trigger('change');
        $("#scenarioDifficulty").val(Metadata.container.difficulty);
        scenarioDescription.val(Metadata.container.description);

        authorsContainer.find("tr").not("tr:first").remove();
        if (Metadata.container.authors.length > 0)
        {
            authorsHeader.show();
            authorsContainer.show();
            Metadata.container.authors.forEach(function(author)
            {
                var authorContainer = $('
');
                authorContainer.append($('', { text: author.name }));
                authorContainer.append($('', { text: author.email ? author.email : "" }));
                authorContainer.append($('', { text: author.startDate }));
                authorContainer.append($('', { text: author.endDate ? author.endDate : "" }));
                authorsContainer.append(authorContainer);
            });
        }
        else
        {
            authorsHeader.hide();
            authorsContainer.hide();
        }

        var anyPropertyShown = false;
        var hStartLevel = 3;

        var propertyValuesEl = $('#meta-property-values');
        var showPropertyItem = function (propertyItem, hLevel, tableBody, idPrefix)
        {
            if (propertyItem.scopes.statementScope !== 'independent') return;
            if (propertyItem.kind === 'section')
            {
                var sectionTable = $('
');

                var sectionTableHeader = $('
').append($('').append($('<h' + hLevel + '>', { text: propertyItem.name })));
                sectionTable.append(sectionTableHeader);

                var sectionTableBody = $('
');
                sectionTable.append(sectionTableBody);

                var sectionContainer = $('
').append(sectionTable);
                if (hLevel !== hStartLevel) sectionContainer.addClass("subsection");
                tableBody.append($('
').append($('').append(sectionContainer)));

                propertyItem.sequence.forEach(function (subItem)
                {
                     showPropertyItem(subItem, hLevel + 1, sectionTableBody, idPrefix);
                });
            }
            else
            {
                var propertyHeader = $('');
                var controlHtmlId = idPrefix + '-' + propertyItem.id;
                var controlFirst = propertyItem.type.labelControlOrder === Types.labelControlOrders.singleLineContainerLabel ||
                                   propertyItem.type.labelControlOrder === Types.labelControlOrders.twoLineContainerLabel;
                propertyHeader.append($('', { text: propertyItem.name + (controlFirst ? '' : ':'), 'for': controlHtmlId }));

                var propertyData = $('', { id: idPrefix + '-container-' + propertyItem.id });
                propertyItem.type.appendControlTo(propertyData, controlHtmlId);

                var propertyRow = $('
');
                var additionalPropertyRow;
                switch (propertyItem.type.labelControlOrder)
                {
                    case Types.labelControlOrders.singleLineLabelContainer:
                        propertyRow.append(propertyHeader);
                        propertyRow.append(propertyData);
                        break;
                    case Types.labelControlOrders.singleLineContainerLabel:
                        propertyRow.append(propertyHeader);
                        propertyRow.prepend(propertyData);
                        break;
                    case Types.labelControlOrders.container:
                        propertyData.prop('colspan', "2");
                        propertyRow.append(propertyData);
                        break;
                    case Types.labelControlOrders.twoLineLabelContainer:
                        propertyRow.append(propertyHeader);
                        additionalPropertyRow = $('
').append(propertyData);
                        break;
                    case Types.labelControlOrders.twoLineContainerLabel:
                        additionalPropertyRow = propertyRow.append(propertyHeader);
                        propertyRow = $('
').append(propertyData);
                        break;
                    default:
                        console.error("Not implemented");
                        break;
                }
                tableBody.append(propertyRow);
                if (additionalPropertyRow) tableBody.append(additionalPropertyRow);

                anyPropertyShown = true;
            }
        };
        Config.container.properties.sequence.forEach(function (propertyItem)
        {
            showPropertyItem(propertyItem, hStartLevel, propertyValuesEl, propertyValuesEl.attr('id'));
        });
        propertyValuesEl.toggle(anyPropertyShown);

        anyPropertyShown = false;

        var characterPropertyValuesEl = $('#meta-character-property-values');
        var characterTabs = $("#character-tabs");
        var characterList = Config.container.characters.sequence.length > 1 ? $('
') : $('
');
        characterTabs.append(characterList);

        Config.container.characters.sequence.forEach(function (character)
        {
            var characterTabId = characterPropertyValuesEl.attr('id') + '-' + character.id;

            var characterHeaderText = character.name ? character.name : character.id;

            if (Config.container.characters.sequence.length > 1)
            {
                // Make a character tab with a link to the div it contains
                var li = $('
').append($('', { href:'#' + characterTabId, value: character.id, text: characterHeaderText }));
                characterList.append(li);
            }
            else
            {
                var characterHeader = $('
', { text: characterHeaderText });
                characterList.append(characterHeader);
            }

            var characterTab = $('
', { id: characterTabId });
            characterTabs.append(characterTab);

            Config.container.characters.properties.sequence.forEach(function(propertyItem)
            {
                showPropertyItem(propertyItem, hStartLevel, characterTab, characterTabId);
            });

            Config.container.characters.byId[character.id].properties.sequence.forEach(function(propertyItem)
            {
                showPropertyItem(propertyItem, hStartLevel, characterTab, characterTabId);
            });
        });

        if (Config.container.characters.sequence.length > 1)
        {
            characterTabs.tabs(
            {
                active: 0,
                collapsible: true
            });
        }
        characterPropertyValuesEl.toggle(anyPropertyShown);

        var setPropertyInDOM = function(propertyValues, propertyContainerId, property)
        {
            if (property.scopes.statementScope !== "independent") return;
            property.type.setInDOM($(propertyContainerId + '-' + Utils.escapeSelector(property.id)), propertyValues[property.id]);
        };
        var propertyId, characterId, property;
        for (propertyId in Config.container.properties.byId)
        {
            property = Config.container.properties.byId[propertyId];
            setPropertyInDOM(Metadata.container.propertyValues.characterIndependent, "#meta-property-values-container", property);
        }
        for (propertyId in Config.container.characters.properties.byId)
        {
            for (characterId in Config.container.characters.byId)
            {
                property = Config.container.characters.properties.byId[propertyId];
                setPropertyInDOM(Metadata.container.propertyValues.perCharacter[characterId], "#meta-character-property-values-" + Utils.escapeSelector(characterId) + "-container", property);
            }
        }
        for (characterId in Config.container.characters.byId)
        {
            for (propertyId in Config.container.characters.byId[characterId].properties.byId)
            {
                property = Config.container.characters.byId[characterId].properties.byId[propertyId];
                setPropertyInDOM(Metadata.container.propertyValues.perCharacter[characterId], "#meta-character-property-values-" + Utils.escapeSelector(characterId) + "-container", property);
            }
        }
    }

    function save()
    {
        Main.unsavedChanges = true;

        var previouslySelectedElement = Main.selectedElement;
        Main.selectElement(null);

        // Save all values in the dialog to the metaObject
        Metadata.container.name = formatScenarioName($("#scenarioName").val());
        $('#scenarioNameTab .scenarioName').text(Metadata.container.name);
        var languageCode = $("#scenarioLanguage").val();
        if (languageCode) Metadata.container.language = Config.container.settings.languages.byCode[languageCode];
        Metadata.container.difficulty = $("#scenarioDifficulty").val();
        Metadata.container.description = $("#scenarioDescription").val();

        var propertyId, characterId, property;
        for (propertyId in Config.container.properties.byId)
        {
            property = Config.container.properties.byId[propertyId];
            if (property.scopes.statementScope !== "independent") continue;
            Metadata.container.propertyValues.characterIndependent[property.id] =
                property.type.getFromDOM($("#meta-property-values-container-" + property.id));
        }
        for (propertyId in Config.container.characters.properties.byId)
        {
            for (characterId in Config.container.characters.byId)
            {
                property = Config.container.characters.properties.byId[propertyId];
                if (property.scopes.statementScope !== "independent") continue;
                Metadata.container.propertyValues.perCharacter[characterId][property.id] =
                    property.type.getFromDOM($("#meta-character-property-values-" + characterId + "-container-" + property.id));
            }
        }
        for (characterId in Config.container.characters.byId)
        {
            for (propertyId in Config.container.characters.byId[characterId].properties.byId)
            {
                property = Config.container.characters.byId[characterId].properties.byId[propertyId];
                if (property.scopes.statementScope !== "independent") continue;
                Metadata.container.propertyValues.perCharacter[characterId][property.id] =
                    property.type.getFromDOM($("#meta-character-property-values-" + characterId + "-container-" + property.id));
            }
        }

        Main.selectElement(previouslySelectedElement);
    }

    function formatScenarioName(scenarioName)
    {
        scenarioName = scenarioName.trim().substr(0, 35);
        if (scenarioName !== "")
        {
            return scenarioName;
        }
        else
        {
            return Metadata.container.name;
        }
    }

    function addOrExtendAuthor(name, email, date, setEndDateToDate)
    {
        var found = false;
        Metadata.container.authors.forEach(function(existingAuthor)
        {
            if (existingAuthor.name === name)
            {
                if (email) existingAuthor.email = email;
                if (setEndDateToDate) existingAuthor.endDate = date;
                found = true;
            }
        });

        if (!found)
        {
            var author = {};
            author.name = name;
            if (email) author.email = email;
            author.startDate = date;
            if (setEndDateToDate) author.endDate = date;

            Metadata.container.authors.push(author);
        }
    }
})();
<|endoftext|>


<commit_before>import {find, split, trim, toLower} from 'bundstreg';
import {attr, tag, matches} from 'hoardom';
import ROLES from '../../data/aria/roles';
import ELEMENTS from '../../data/aria/elements';

/**
 * Get the semantic role of an element.
 *
 * @see https://www.w3.org/TR/html-aria/
 *
 * @param {Element} element
 * @param {Boolean} [implicit=true]
 * @return {String}
 */
export default function role(element, implicit = true) {
  let role = trim(attr(element, 'role'));

  if (!role && implicit) {
    const feature = tag(element);

    implicit = find(ELEMENTS[feature], implicit => {
      const {include, exclude} = implicit;

      if (include && !matches(element, include)) {
        return false;
      }

      if (exclude && matches(element, exclude)) {
        return false;
      }

      return true;
    });

    if (implicit) {
      role = implicit.role;
    }
  }

  const roles = split(toLower(role), /\s+/);
  const first = find(roles, role => role in ROLES);

  return first ? first : null;
}
<commit_msg>Make `role()` lazy<commit_after>import {find, split, trim, toLower} from 'bundstreg';
import {attr, tag, matches} from 'hoardom';
import ROLES from '../../data/aria/roles';
import ELEMENTS from '../../data/aria/elements';

const ROLE = Symbol('role');

/**
 * Get the semantic role of an element.
 *
 * @see https://www.w3.org/TR/html-aria/
 *
 * @param {Element} element
 * @param {Boolean} [implicit=true]
 * @return {String}
 */
export default function role(element, implicit = true) {
  return element.lazy(ROLE,
    () => {
      let role = trim(attr(element, 'role'));

      if (!role && implicit) {
        const feature = tag(element);

        implicit = find(ELEMENTS[feature], implicit => {
          const {include, exclude} = implicit;

          if (include && !matches(element, include)) {
            return false;
          }

          if (exclude && matches(element, exclude)) {
            return false;
          }

          return true;
        });

        if (implicit) {
          role = implicit.role;
        }
      }

      const roles = split(toLower(role), /\s+/);
      const first = find(roles, role => role in ROLES);

      return first ? first : null;
    }
  );
}
<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
var config = require('./protractor.conf.js').config;

chai.use(chaiAsPromised);
var expect = chai.expect;

describe('ripple client', function() {
  var ptor;

  beforeEach(function() {
    ptor = protractor.getInstance();
  });

  describe('bootstrap', function() {

    beforeEach(function() {
      ptor.get('#');
    });

    it('should automatically redirect to /register when location hash/fragment is empty', function() {
      ptor.getCurrentUrl()
        .then(function(url) {
          expect(url).to.contain('/register');
        });
    });

  });

  describe('login', function() {

    beforeEach(function() {
      ptor.get('#/login');
    });

    it('should render login when user navigates to /login', function() {
      expect($("form[name='loginForm']").getText()).to.exist;
    });

  });
});
<commit_msg>[TEST] e2e: Test user login<commit_after>'use strict';

// Chai is a BDD / TDD assertion library
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
var config = require('./protractor.conf.js').config;

chai.use(chaiAsPromised);
var expect = chai.expect;

describe('ripple client', function() {
  var ptor;

  beforeEach(function() {
    ptor = protractor.getInstance();
  });

  describe('bootstrap', function() {

    beforeEach(function() {
      ptor.get('#');
    });

    it('should automatically redirect to /register when location hash/fragment is empty', function() {
      ptor.getCurrentUrl()
        .then(function(url) {
          expect(url).to.contain('/register');
        });
    });

  });

  describe('login', function() {

    beforeEach(function() {
      ptor.get('#/login');
    });

    it('should render login when user navigates to /login', function() {
      expect($("form[name='loginForm']").getText()).to.exist;
    });

    it('should login the test user', function() {
      // Fill the form
      $(".auth-form-wrapper #login_username").sendKeys(config.username);
      $(".auth-form-wrapper #login_password").sendKeys(config.password);
      $(".auth-form-wrapper button").click();

      // Check if it takes to the balance page (success login)
      ptor.getCurrentUrl()
        .then(function(url) {
          expect(url).to.contain('/balance');
        });
    });

  });
});
<|endoftext|>


<commit_before>import App from "../app";
import { pubsub } from "../helpers/subscriptionManager.js";
import uuid from "uuid";

App.on("createTargetingContact", ({ id, targetClass }) => {
  App.systems.find(s => s.id === id).createTarget(targetClass);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("targetTargetingContact", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  system.targetTarget(targetId);
  pubsub.publish("notify", {
    id: uuid.v4(),
    simulatorId: system.simulatorId,
    type: "Targeting",
    station: "Core",
    title: `Targeted`,
    body: "",
    color: "info"
  });
  App.handleEvent(
    {
      simulatorId: system.simulatorId,
      title: `Targeted`,
      component: "TargetingCore",
      body: null,
      color: "info"
    },
    "addCoreFeed"
  );
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("untargetTargetingContact", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  system.untargetTarget(targetId);
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("targetSystem", ({ id, targetId, system }) => {
  App.systems.find(s => s.id === id).targetSystem(targetId, system);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("removeTarget", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  const contact = system.contacts.find(c => c.id === targetId);
  const classId = contact.class;
  // Send a sensors update too
  const sensors = App.systems.find(
    s =>
      s.simulatorId === system.simulatorId &&
      s.class === "Sensors" &&
      s.domain === "external"
  );

  system.destroyTarget(targetId);

  // Remove the contact from Sensors
  if (sensors.contacts.find(c => c.id === classId)) {
    sensors.destroyContact({ id: classId });
    pubsub.publish("sensorContactUpdate", sensors);

    // We should also remove the class, if there are none left
    if (!system.contacts.find(c => c.class === classId && c.id !== targetId)) {
      setTimeout(() => {
        App.handleEvent({ id, classId }, "removeTargetClass");
      }, 500);
    }
  }

  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("addTargetClass", ({ id, classInput }) => {
  App.systems.find(s => s.id === id).addTargetClass(classInput);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("removeTargetClass", ({ id, classId }) => {
  const system = App.systems.find(s => s.id === id);
  system.removeTargetClass(classId);
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("updateTargetClass", ({ id, classInput }) => {
  App.systems.find(s => s.id === id).updateTargetClass(classInput);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("setTargetClassCount", ({ id, classId, count }) => {
  App.systems.find(s => s.id === id).setTargetClassCount(classId, count);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on(
  "setTargetingCalculatedTarget",
  ({ id, coordinates, simulatorId, contactId }) => {
    if (coordinates.z === 0) coordinates.z = Math.random();
    App.systems
      .find(
        s =>
          s.id === id ||
          (s.simulatorId === simulatorId && s.type === "Targeting")
      )
      .setCalculatedTarget(coordinates, contactId);
    pubsub.publish(
      "targetingUpdate",
      App.systems.filter(s => s.type === "Targeting")
    );
  }
);
App.on("setTargetingEnteredTarget", ({ id, simulatorId, coordinates }) => {
  App.systems
    .find(
      s =>
        s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
    )
    .setEnteredTarget(coordinates);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("setCoordinateTargeting", ({ id, which }) => {
  App.systems.find(s => s.id === id).setCoordinateTargeting(which);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("clearAllTargetingContacts", ({ id, simulatorId }) => {
  const system = App.systems.find(
    s =>
      s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
  );
  system.contacts = [];
  system.classes = [];
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("setTargetingRange", ({ id, range }) => {
  const system = App.systems.find(s => s.id === id);
  system.setRange(range);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("setTargetingClasses", ({ id, simulatorId, classInput }) => {
  const system = App.systems.find(
    s =>
      s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
  );
  classInput.forEach(c => system.addTargetClass(c));
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
<commit_msg>Fix a bug<commit_after>import App from "../app";
import { pubsub } from "../helpers/subscriptionManager.js";
import uuid from "uuid";

App.on("createTargetingContact", ({ id, targetClass }) => {
  App.systems.find(s => s.id === id).createTarget(targetClass);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("targetTargetingContact", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  system.targetTarget(targetId);
  pubsub.publish("notify", {
    id: uuid.v4(),
    simulatorId: system.simulatorId,
    type: "Targeting",
    station: "Core",
    title: `Targeted`,
    body: "",
    color: "info"
  });
  App.handleEvent(
    {
      simulatorId: system.simulatorId,
      title: `Targeted`,
      component: "TargetingCore",
      body: null,
      color: "info"
    },
    "addCoreFeed"
  );
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("untargetTargetingContact", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  system.untargetTarget(targetId);
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("targetSystem", ({ id, targetId, system }) => {
  App.systems.find(s => s.id === id).targetSystem(targetId, system);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("removeTarget", ({ id, targetId }) => {
  const system = App.systems.find(s => s.id === id);
  const contact = system.contacts.find(c => c.id === targetId);
  const classId = contact.class;
  // Send a sensors update too
  const sensors = App.systems.find(
    s =>
      s.simulatorId === system.simulatorId &&
      s.class === "Sensors" &&
      s.domain === "external"
  );

  system.destroyTarget(targetId);

  // Remove the contact from Sensors
  if (sensors.contacts.find(c => c.id === classId)) {
    sensors.destroyContact({ id: classId });
    pubsub.publish("sensorContactUpdate", sensors);

    // We should also remove the class, if there are none left
    if (!system.contacts.find(c => c.class === classId && c.id !== targetId)) {
      setTimeout(() => {
        App.handleEvent({ id, classId }, "removeTargetClass");
      }, 500);
    }
  }

  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("addTargetClass", ({ id, classInput }) => {
  const sys = App.systems.find(s => s.id === id);
  sys && sys.addTargetClass(classInput);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("removeTargetClass", ({ id, classId }) => {
  const system = App.systems.find(s => s.id === id);
  system.removeTargetClass(classId);
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("updateTargetClass", ({ id, classInput }) => {
  App.systems.find(s => s.id === id).updateTargetClass(classInput);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("setTargetClassCount", ({ id, classId, count }) => {
  App.systems.find(s => s.id === id).setTargetClassCount(classId, count);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on(
  "setTargetingCalculatedTarget",
  ({ id, coordinates, simulatorId, contactId }) => {
    if (coordinates.z === 0) coordinates.z = Math.random();
    App.systems
      .find(
        s =>
          s.id === id ||
          (s.simulatorId === simulatorId && s.type === "Targeting")
      )
      .setCalculatedTarget(coordinates, contactId);
    pubsub.publish(
      "targetingUpdate",
      App.systems.filter(s => s.type === "Targeting")
    );
  }
);
App.on("setTargetingEnteredTarget", ({ id, simulatorId, coordinates }) => {
  App.systems
    .find(
      s =>
        s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
    )
    .setEnteredTarget(coordinates);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
App.on("setCoordinateTargeting", ({ id, which }) => {
  App.systems.find(s => s.id === id).setCoordinateTargeting(which);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("clearAllTargetingContacts", ({ id, simulatorId }) => {
  const system = App.systems.find(
    s =>
      s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
  );
  system.contacts = [];
  system.classes = [];
  // Send a sensors update too
  const sensors = App.systems.find(
    s => s.simulatorId === system.simulatorId && s.class === "Sensors"
  );
  pubsub.publish("sensorContactUpdate", sensors);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("setTargetingRange", ({ id, range }) => {
  const system = App.systems.find(s => s.id === id);
  system.setRange(range);
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});

App.on("setTargetingClasses", ({ id, simulatorId, classInput }) => {
  const system = App.systems.find(
    s =>
      s.id === id || (s.simulatorId === simulatorId && s.type === "Targeting")
  );
  classInput.forEach(c => system.addTargetClass(c));
  pubsub.publish(
    "targetingUpdate",
    App.systems.filter(s => s.type === "Targeting")
  );
});
<|endoftext|>


<commit_before>const _ = require('lodash');
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const unminifiedPlugin = require('unminified-webpack-plugin');
const preset = require('./preset/webpack.config');

if (process.env.NODE_ENV === 'production') {
  Promise.resolve()
    .then(packAndLog(preset))
    .then(packAndLog(production(distEntries)))
    .then(renameUIExports)
    .then(packAndLog(production(packageEntry)))
    .then(packAndLog(development(testsEntry)))
    .catch(function failure (err) {
      console.error(err);
    });
} else {
  module.exports = [
    development(distEntries),
    development(packageEntry),
  ];

  module.exports.devServer = {
    contentBase: 'examples/',
    inline: true,
  };
}

function common (tx) {
  return tx({
    resolve: {
      extensions: ['', '.webpack.js', '.web.js', '.js', '.jsx']
    },
    module: {
      loaders: [{
        test: /\.(js|jsx)$/,
        loader: 'babel',
        exclude: [
          /(node_modules)/,                         // Skip node modules
        ]
      }, {
        test: /\.json$/,
        loader: 'json'
      }]
    },
    debug: true,
    externals: [
      /^babel.+$/,
    ]
  });
}

function development (tx) {
  return tx(common(function devTx (config) {
    config.devtool = 'eval-source-map';
    config.plugins = [];
    return config;
  }));
}

function production (tx) {
  return tx(common(function productionTx (config) {
    config.devtool = 'source-map';

    config.plugins = [
      new webpack.optimize.DedupePlugin(),
      new webpack.optimize.OccurrenceOrderPlugin(),
      new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }),
      new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }),
      new webpack.optimize.UglifyJsPlugin({
        compress: { warnings: false, screw_ie8: true },
        sourceMap: true,
      }),
      new unminifiedPlugin()
    ];

    return config;
  }));
}

function packageEntry (cfg) {
  cfg.name = 'packages';

  cfg.entry = {
    'inferno-starter': './src/packages/inferno-starter.js',
  };

  cfg.output = {
    path: path.join(__dirname, 'dist'),
    filename: '[name].min.js',
    sourceMapFilename: '[name].min.map',
    library: 'relm',
    libraryTarget: 'umd'
  };

  return cfg;
}

function testsEntry (cfg) {
  cfg.name = 'tests';

  cfg.entry = {
    'inferno-starter.e2e': './src/packages/__tests__/inferno-starter.e2e.js',
  };

  cfg.output = {
    path: path.join(__dirname, 'dist/__tests__'),
    filename: '[name].js',
  };

  cfg.externals = [
    '../inferno-starter'
  ];

  return cfg;
}

function distEntries (cfg) {
  cfg.name = 'dist';

  cfg.entry = Object.assign.apply(Object, [
    {
      create: './src/create.js',
      list: './src/list.js',
      router: './src/router.js',
    },
    // readDir('./src/plugins', '.js', function readPlugins (entries, filename) {
    //   entries[filename] = `./src/plugins/${filename}.js`;
    //   return entries;
    // }),
    readDir('./src/ui', '.js', function readUI (entries, filename) {
      entries[`ui/${filename}`] = `./src/ui/${filename}.js`;
      return entries;
    }),
  ]);

  cfg.plugins.push(
    new webpack.optimize.CommonsChunkPlugin({
      name: 'create',
      minChunks (module, count) {
        if (!module.resource) return false;
        if (module.resource.indexOf('lodash') > -1 && count > 1) return true;
        return false;
      }
    })
  );

  cfg.output = {
    path: path.join(__dirname, 'dist'),
    filename: '[name].min.js',
    sourceMapFilename: '[name].min.map',
    library: ['relm', '[name]'],
    libraryTarget: 'umd'
  };

  // lib.externals = [
  //   /lodash/,
  // ];

  return cfg;
}

/*
 * Helpers
 */

function readDir (dir, ext, f) {
  return _.reduce(fs.readdirSync(dir), function checkExtensionFirst (entries, filename) {
    if (filename.indexOf(ext) === -1) return entries;
    return f(entries, filename.replace(ext, ''));
  }, {});
}

function pack (config) {
  return new Promise(function exec (resolve, reject) {
    const compiler = webpack(config);
    compiler.run(function callback (err, stats) {
      if (err) return reject(err);
      const jsonStats = stats.toJson();
      if (jsonStats.errors.length > 0) return reject(jsonStats.errors);
      return resolve(stats);
    });
  });
}

function log (stats) {
  console.log(stats.toString('minimal'));
  return stats;
}

function packAndLog (config) {
  return () => pack(config).then(log);
}

function stringReplace (filename, source, replacement) {
  return new Promise(function exec (resolve, reject) {
    fs.readFile(filename, 'utf8', function fileRead (readError, data) {
      if (readError) return reject(readError);

      const result = data.replace(source, replacement);

      return fs.writeFile(filename, result, 'utf8', function fileWritten (writeError) {
        if (writeError) return reject(writeError);
        return resolve();
      });
    });
  });
}

// This is a workaround for webpack's umd export
//
// We want weback to compile ui files to dist/ui sub directory but want the exports
// named relm.Button instead of relm[ui/Button]. This cannot be acheived through
// separate webpack configs as we still want to apply CommonsChunkPlugin across
// the board (ex: so lodash modules used inside ui components can be extracted to core)
//
// The naming only affects the global export; commonjs usage is normal i.e. require('relm/ui/Button')
//
// So for now, this method reads all files in dist/ui and replaces the export names
function renameUIExports () {
  return Promise.resolve().then(function exec () {
    const dir = path.join(__dirname, 'dist/ui');
    const files = fs.readdirSync(dir).map(file => path.join(dir, file));

    const jsRegex = /\["ui\//g;             // replace occurrences of the ui prefix ["ui/
    const sourceMapRegex = /\[\\"ui\//g;    // sourcemap has quotation escaped [\"ui

    function replacementPromise (promises, filename) {
      if (filename.endsWith('.js')) return promises.concat(stringReplace(filename, jsRegex, '["'));
      if (filename.endsWith('.map')) return promises.concat(stringReplace(filename, sourceMapRegex, '[\\"'));
      return promises;
    }

    return Promise.all(_.reduce(files, replacementPromise, [])).then(function done (replacements) {
      console.log(`Renamed ${replacements.length} exports in dist/ui from relm[ui/Component] to relm[Component]`);
    });
  });
}
<commit_msg>Add build step to copy preset to dist folder<commit_after>const _ = require('lodash');
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const unminifiedPlugin = require('unminified-webpack-plugin');
const preset = require('./preset/webpack.config');

if (process.env.NODE_ENV === 'production') {
  Promise.resolve()
    .then(packAndLog(preset))
      .then(copyPresetToDist)
    .then(packAndLog(production(distEntries)))
      .then(renameUIExports)
    .then(packAndLog(production(packageEntry)))
    .then(packAndLog(development(testsEntry)))
    .catch(function failure (err) {
      console.error(err);
    });
} else {
  module.exports = [
    development(distEntries),
    development(packageEntry),
  ];

  module.exports.devServer = {
    contentBase: 'examples/',
    inline: true,
  };
}

function common (tx) {
  return tx({
    resolve: {
      extensions: ['', '.webpack.js', '.web.js', '.js', '.jsx']
    },
    module: {
      loaders: [{
        test: /\.(js|jsx)$/,
        loader: 'babel',
        exclude: [
          /(node_modules)/,                         // Skip node modules
        ]
      }, {
        test: /\.json$/,
        loader: 'json'
      }]
    },
    debug: true,
    externals: [
      /^babel.+$/,
    ]
  });
}

function development (tx) {
  return tx(common(function devTx (config) {
    config.devtool = 'eval-source-map';
    config.plugins = [];
    return config;
  }));
}

function production (tx) {
  return tx(common(function productionTx (config) {
    config.devtool = 'source-map';

    config.plugins = [
      new webpack.optimize.DedupePlugin(),
      new webpack.optimize.OccurrenceOrderPlugin(),
      new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }),
      new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }),
      new webpack.optimize.UglifyJsPlugin({
        compress: { warnings: false, screw_ie8: true },
        sourceMap: true,
      }),
      new unminifiedPlugin()
    ];

    return config;
  }));
}

function packageEntry (cfg) {
  cfg.name = 'packages';

  cfg.entry = {
    'inferno-starter': './src/packages/inferno-starter.js',
  };

  cfg.output = {
    path: path.join(__dirname, 'dist'),
    filename: '[name].min.js',
    sourceMapFilename: '[name].min.map',
    library: 'relm',
    libraryTarget: 'umd'
  };

  return cfg;
}

function testsEntry (cfg) {
  cfg.name = 'tests';

  cfg.entry = {
    'inferno-starter.e2e': './src/packages/__tests__/inferno-starter.e2e.js',
  };

  cfg.output = {
    path: path.join(__dirname, 'dist/__tests__'),
    filename: '[name].js',
  };

  cfg.externals = [
    '../inferno-starter'
  ];

  return cfg;
}

function distEntries (cfg) {
  cfg.name = 'dist';

  cfg.entry = Object.assign.apply(Object, [
    {
      create: './src/create.js',
      list: './src/list.js',
      router: './src/router.js',
    },
    readDir('./src/plugins', '.js', function readPlugins (entries, filename) {
      entries[filename] = `./src/plugins/${filename}.js`;
      return entries;
    }),
    readDir('./src/ui', '.js', function readUI (entries, filename) {
      entries[`ui/${filename}`] = `./src/ui/${filename}.js`;
      return entries;
    }),
  ]);

  cfg.plugins.push(
    new webpack.optimize.CommonsChunkPlugin({
      name: 'create',
      minChunks (module, count) {
        if (!module.resource) return false;
        if (module.resource.indexOf('lodash') > -1 && count > 1) return true;
        return false;
      }
    })
  );

  cfg.output = {
    path: path.join(__dirname, 'dist'),
    filename: '[name].min.js',
    sourceMapFilename: '[name].min.map',
    library: ['relm', '[name]'],
    libraryTarget: 'umd'
  };

  // lib.externals = [
  //   /lodash/,
  // ];

  return cfg;
}

/*
 * Helpers
 */

function readDir (dir, ext, f) {
  return _.reduce(fs.readdirSync(dir), function checkExtensionFirst (entries, filename) {
    if (filename.indexOf(ext) === -1) return entries;
    return f(entries, filename.replace(ext, ''));
  }, {});
}

function pack (config) {
  return new Promise(function exec (resolve, reject) {
    const compiler = webpack(config);
    compiler.run(function callback (err, stats) {
      if (err) return reject(err);
      const jsonStats = stats.toJson();
      if (jsonStats.errors.length > 0) return reject(jsonStats.errors);
      return resolve(stats);
    });
  });
}

function log (stats) {
  console.log(stats.toString('minimal'));
  return stats;
}

function packAndLog (config) {
  return () => pack(config).then(log);
}

function stringReplace (filename, source, replacement) {
  return new Promise(function exec (resolve, reject) {
    fs.readFile(filename, 'utf8', function fileRead (readError, data) {
      if (readError) return reject(readError);

      const result = data.replace(source, replacement);

      return fs.writeFile(filename, result, 'utf8', function fileWritten (writeError) {
        if (writeError) return reject(writeError);
        return resolve();
      });
    });
  });
}

// This is a workaround for webpack's umd export
//
// We want weback to compile ui files to dist/ui sub directory but want the exports
// named relm.Button instead of relm[ui/Button]. This cannot be acheived through
// separate webpack configs as we still want to apply CommonsChunkPlugin across
// the board (ex: so lodash modules used inside ui components can be extracted to core)
//
// The naming only affects the global export; commonjs usage is normal i.e. require('relm/ui/Button')
//
// So for now, this method reads all files in dist/ui and replaces the export names
function renameUIExports () {
  return Promise.resolve().then(function exec () {
    const dir = path.join(__dirname, 'dist/ui');
    const files = fs.readdirSync(dir).map(file => path.join(dir, file));

    const jsRegex = /\["ui\//g;             // replace occurrences of the ui prefix ["ui/
    const sourceMapRegex = /\[\\"ui\//g;    // sourcemap has quotation escaped [\"ui

    function replacementPromise (promises, filename) {
      if (filename.endsWith('.js')) return promises.concat(stringReplace(filename, jsRegex, '["'));
      if (filename.endsWith('.map')) return promises.concat(stringReplace(filename, sourceMapRegex, '[\\"'));
      return promises;
    }

    return Promise.all(_.reduce(files, replacementPromise, [])).then(function done (replacements) {
      console.log(`Renamed ${replacements.length} exports in dist/ui from relm[ui/Component] to relm[Component]`);
    });
  });
}

function copyFile (source, target) {
  return new Promise(function exec (resolve, reject) {
    const readStream = fs.createReadStream(source);
    readStream.on('error', reject);
    const writeStream = fs.createWriteStream(target);
    writeStream.on('error', reject);
    writeStream.on('finish', resolve);
    readStream.pipe(writeStream);
  });
}

function copyPresetToDist () {
  const source = path.join(__dirname, 'preset/preset.bundle.js');
  const target = path.join(__dirname, 'dist/preset.js');
  return copyFile(source, target);
}
<|endoftext|>


<commit_before>var jsdom = require('jsdom');
var Path = require('path');
var URL = require('url');
var readfile = require('fs').readFileSync;

exports.compile = function(str, opts) {
	// use opts.features, opts.scripts ?
	var window = jsdom.jsdom(str, null, {
		features: {
			FetchExternalResources: false,				// loaded depending on script[notemplate] attribute
			ProcessExternalResources: false,			// same
			MutationEvents: false,								// not needed
			QuerySelector: true										// jquip doesn't provide querySelector/querySelectorAll compatibility, Sizzle does.
		}
	}).createWindow();

	// core jQuery : selector, manipulation, traversal
	// use real jQuery when it becomes modular.
	// jquip needs some patches to run inside jsdom (mainly because node.style.key is not supported by cssom)
	run(window, require.resolve('jquip-jsdom'));

	// <script> tags can have attribute notemplate = server | client | both
	// default value is client
	// server value : scripts are fetched (if they have src attribute), and discarded
	// client value : script are not fetched
	// both : scripts are fetched (if they have src attribute )and not discarded
	var public = Path.join('.', opts.public || 'public');
	var scripts = window.$('script');
	for (var i=0, len = scripts.length; i < len; i++) {
		var script = scripts[i];
		var att = script.attributes.notemplate;
		if (!att) continue; // default is notemplate="client"
		att = att.value;
		script.attributes.removeNamedItem('notemplate'); // make sure attribute is removed
		if (att != "server" && att != "both") continue; // any other value is "client"
		var src = script.attributes.src;
		if (!src && script.textContent) window.run(script.textContent); // html5 runs script content only when src is not set
		if (att == "server") window.$(script).remove(); // remove script tag
		if (!src) continue;
		// load file and run it
		src = src.value;
		// try to find javascript file
		var url = URL.parse(src);
		if (url.hostname) {
			console.error("express-notemplate doesn't allow loading external URL -- ping author to do it", script);
			continue;
		}
		var path = Path.join(public, url.pathname);
		if (!Path.existsSync(path)) {
			console.error("express-notemplate doesn't find script.src file", path);
			continue;
		}
		run(window, path);
	}

	return function(data) {
		// just return the template if no merge functions
		if (!window._merge && !window.merge) return str;
		if (window._merge) window._merge(data);
		if (window.merge) window.merge(data);
		// outputs doctype because of jsdom bug
		return window.document.doctype.toString() + "\n" + window.document.outerHTML;
	};
};

function run(window, path) {
	window.run(readfile(path).toString());
}
<commit_msg>render('index', {fragment:'selector'})<commit_after>var jsdom = require('jsdom');
var Path = require('path');
var URL = require('url');
var readfile = require('fs').readFileSync;

exports.compile = function(str, opts) {
	// use opts.features, opts.scripts ?
	opts = opts || {};
	var window = jsdom.jsdom(str, null, {
		features: {
			FetchExternalResources: false,				// loaded depending on script[notemplate] attribute
			ProcessExternalResources: false,			// same
			MutationEvents: false,								// not needed
			QuerySelector: true										// jquip doesn't provide querySelector/querySelectorAll compatibility, Sizzle does.
		}
	}).createWindow();

	// core jQuery : selector, manipulation, traversal
	// use real jQuery when it becomes modular.
	// jquip needs some patches to run inside jsdom (mainly because node.style.key is not supported by cssom)
	run(window, require.resolve('jquip-jsdom'));

	// <script> tags can have attribute notemplate = server | client | both
	// default value is client
	// server value : scripts are fetched (if they have src attribute), and discarded
	// client value : script are not fetched
	// both : scripts are fetched (if they have src attribute )and not discarded
	var public = Path.join('.', opts.public || 'public');
	var scripts = window.$('script');
	for (var i=0, len = scripts.length; i < len; i++) {
		var script = scripts[i];
		var att = script.attributes.notemplate;
		if (!att) continue; // default is notemplate="client"
		att = att.value;
		script.attributes.removeNamedItem('notemplate'); // make sure attribute is removed
		if (att != "server" && att != "both") continue; // any other value is "client"
		var src = script.attributes.src;
		if (!src && script.textContent) window.run(script.textContent); // html5 runs script content only when src is not set
		if (att == "server") window.$(script).remove(); // remove script tag
		if (!src) continue;
		// load file and run it
		src = src.value;
		// try to find javascript file
		var url = URL.parse(src);
		if (url.hostname) {
			console.error("express-notemplate doesn't allow loading external URL -- ping author to do it", script);
			continue;
		}
		var path = Path.join(public, url.pathname);
		if (!Path.existsSync(path)) {
			console.error("express-notemplate doesn't find script.src file", path);
			continue;
		}
		run(window, path);
	}

	return function(data) {
		// just return the template if no merge functions
		if (!window._merge && !window.merge) return str;
		if (window._merge) window._merge(data);
		if (window.merge) window.merge(data);
		// output selected nodes
		if (opts.fragment) return outer(window.$(opts.fragment));
		// outputs doctype because of jsdom bug
		return window.document.doctype.toString() + "\n" + window.document.outerHTML;
	};
};

function run(window, path) {
	window.run(readfile(path).toString());
}
function outer($nodes) {
	var ret = '';
	$nodes.each(function() {
		ret += this.outerHTML;
	});
	return ret;
}
<|endoftext|>


<commit_before>/**
 * Return thunkified versions of the provided functions bound to the specified
 * context and with the provided initial args.
 */
function thunkifyAll(fns, thisArg, args) {
  fns = fns || [];
  return fns.map(function(fn) {
    return function(done) {
      var newArgs = args || [];
      if (arguments.length) newArgs.push.apply(newArgs, arguments);
      newArgs.push(done);
      try {
        fn.apply(thisArg, newArgs);
      } catch (err) {
        done(err);
      }
    };
  });
}

module.exports = thunkifyAll;
<commit_msg>Don't pass along arguments<commit_after>/**
 * Return thunkified versions of the provided functions bound to the specified
 * context and with the provided initial args.
 */
function thunkifyAll(fns, thisArg, args) {
  fns = fns || [];
  return fns.map(function(fn) {
    return function(done) {
      var newArgs = args ? Array.prototype.slice.call(args, 0) : [];
      newArgs.push(done);
      try {
        fn.apply(thisArg, newArgs);
      } catch (err) {
        done(err);
      }
    };
  });
}

module.exports = thunkifyAll;
<|endoftext|>


<commit_before>process.env.NODE_ENV = 'testing'

var should = require('should');
var Nightmare = require('nightmare');

// Set different ports so we can run tests while dev server is running
process.env.API_PORT = 3001
process.env.WEB_PORT = 8081

// Web tests
require('../../build/dev-server')
require('../../api/server');

console.log("~~~~ Webpack & API servers up, starting e2e tests ~~~~")

function newNightmare() {
  return new Nightmare(
    {
      show: false
    }
  )
}
var testTimeout = 20000
var port = 8080
var url = `http://localhost:${port}`;

describe('Start page', function () {
  this.timeout(testTimeout); // Set timeout to 15 seconds, instead of the original 2 seconds

  it('should show login form when loaded', function (done) {
    newNightmare()
      .goto(url)
      .evaluate(function () {
        return document.querySelectorAll('div.home').length;
      })
      .run(function (err, result) {
        result.should.equal(1);
        done();
      });
  });
});

describe('Game page', function () {
  this.timeout(testTimeout); // Set timeout to 15 seconds, instead of the original 2 seconds

  it("should contain the 'List of Players'", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')
      .evaluate(function () {
        return document.querySelectorAll('div.game')[0].innerHTML;
      })
      .run(function (err, result) {
        result.should.containEql("List of players");
        done();
      });
  });

  it("should contain the current user's name", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')
      .evaluate(function () {
        return document.querySelectorAll('div.game')[0].innerHTML;
      })
      .end()
      .then(function (result) {
        result.should.containEql(username);
        done();
      });
  });

  it("should return to home page when quit button is pressed", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')

      // TODO http://stackoverflow.com/questions/41907654/how-to-assert-at-multiple-points-during-execution-of-a-nightmare-test
      // .evaluate(function () {
      //   return document.querySelectorAll('#write-btn').length;
      // })
      // .then(function (result) {
      //   // No 'write' buttons found
      //   result.should.eql(0);
      // })
      .click('#quit-btn')
      .wait('div.home')
      .evaluate(function () {
        return document.querySelectorAll('#write-btn').length;
      })
      .then(function (result) {
        // After quitting, we should have a write button
        result.should.eql(1);
        done();
      });
  });
});<commit_msg>Fix tests<commit_after>process.env.NODE_ENV = 'testing'

var should = require('should');
var Nightmare = require('nightmare');

// Set different ports so we can run tests while dev server is running
process.env.WEB_PORT = 8081
// TODO - this is still the standard socket.io port, not sure how to change it on the client
// http://stackoverflow.com/questions/41908504/vue-how-to-use-a-different-socket-io-port-in-tests
process.env.API_PORT = 3000

// Web tests
require('../../build/dev-server')
require('../../api/server');

console.log("~~~~ Webpack & API servers up, starting e2e tests ~~~~")

function newNightmare() {
  return new Nightmare(
    {
      show: false
    }
  )
}
var testTimeout = 20000
var url = `http://localhost:${process.env.WEB_PORT}`;

describe('Start page', function () {
  this.timeout(testTimeout); // Set timeout to 15 seconds, instead of the original 2 seconds

  it('should show login form when loaded', function (done) {
    newNightmare()
      .goto(url)
      .evaluate(function () {
        return document.querySelectorAll('div.home').length;
      })
      .run(function (err, result) {
        result.should.equal(1);
        done();
      });
  });
});

describe('Game page', function () {
  this.timeout(testTimeout); // Set timeout to 15 seconds, instead of the original 2 seconds

  it("should contain the 'List of Players'", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')
      .evaluate(function () {
        return document.querySelectorAll('div.game')[0].innerHTML;
      })
      .run(function (err, result) {
        result.should.containEql("List of players");
        done();
      });
  });

  it("should contain the current user's name", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')
      .evaluate(function () {
        return document.querySelectorAll('div.game')[0].innerHTML;
      })
      .end()
      .then(function (result) {
        result.should.containEql(username);
        done();
      });
  });

  it("should return to home page when quit button is pressed", function (done) {
    var username = 'sinbad';
    newNightmare()
      .goto(url)
      .type('#choosenickname', username)
      .click('#write-btn')
      .wait('div.game')

      // TODO http://stackoverflow.com/questions/41907654/how-to-assert-at-multiple-points-during-execution-of-a-nightmare-test
      // .evaluate(function () {
      //   return document.querySelectorAll('#write-btn').length;
      // })
      // .then(function (result) {
      //   // No 'write' buttons found
      //   result.should.eql(0);
      // })
      .click('#quit-btn')
      .wait('div.home')
      .evaluate(function () {
        return document.querySelectorAll('#write-btn').length;
      })
      .then(function (result) {
        // After quitting, we should have a write button
        result.should.eql(1);
        done();
      });
  });
});<|endoftext|>


<commit_before>(function(root, undefined) {

	// Variablizing the strings for consistency
	// and to avoid harmful dot-notation look-ups with
	// javascript keywords
	var sNull      = 'Null',
		sUndefined = 'Undefined',
		sInfinity  = 'Infinity',
		sDate      = 'Date',
		sNaN       = 'NaN',
		sNumber    = 'Number',
		sString    = 'String',
		sObject    = 'Object',
		sArray     = 'Array',
		sRegExp    = 'RegExp',
		sBoolean   = 'Boolean',
		sFunction  = 'Function',
		sElement   = 'Element';

	// Utilizing the non-standard (but available in modern browsers) Global Object names
	// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
	// Provide a polyfill for items without names
	(function() {
		var globalObjects = [
				sDate,
				sNumber,
				sString,
				sObject,
				sArray,
				sRegExp,
				sBoolean,
				sFunction,
				sElement
			],
			idx = globalObjects.length,
			globalObject;
		while (idx--) {
			globalObject = globalObjects[idx];
			if (!root[globalObject].name) {
				root[globalObject].name = globalObject;
			}
		}
	}());

	/**
	 * Possible values
	 * @type {Object}
	 */
	var _types = {};
	_types[sNull]      = 0;
	_types[sUndefined] = 1;
	_types[sInfinity]  = 2;
	_types[sDate]      = 3;
	_types[sNaN]       = 4;
	_types[sNumber]    = 5;
	_types[sString]    = 6;
	_types[sObject]    = 7;
	_types[sArray]     = 8;
	_types[sRegExp]    = 9;
	_types[sBoolean]   = 10;
	_types[sFunction]  = 11;
	_types[sElement]   = 12;

	/**
	 * Cached reference to Object.prototype.toString
	 * for type checking
	 * @type {Function}
	 */
	var _toString = (function(toString) {
			return function(obj) {
				return toString.call(obj);
			};
		}(({}).toString)),

		_noopArr = [],

		/**
		 * Type checks
		 */
		_checkMap = (function(map) {

			var types = [
					// Only mapping items that need to be mapped.
					// Items not in this list are doing faster
					// (non-string) checks
					//
					// k = key, v = value
					{ k: sDate,     v: _types[sDate]     },
					{ k: sNumber,   v: _types[sNumber]   },
					{ k: sString,   v: _types[sString]   },
					{ k: sObject,   v: _types[sObject]   },
					{ k: sArray,    v: _types[sArray]    },
					{ k: sRegExp,   v: _types[sRegExp]   },
					{ k: sFunction, v: _types[sFunction] }
				],
				idx = types.length;
			while (idx--) {
				map['[object ' + types[idx].k + ']'] = types[idx].v;
			}

			return map;

		}({})),

		/**
		 * Changes arguments to an array
		 * @param  {Arguments} arraylike
		 * @return {Array}
		 */
		_slice = (function(slice) {
			return function(arraylike) {
				return slice.call(arraylike);
			};
		}([].slice));

	// Reference: https://gist.github.com/dhm116/1790197
	if (!('bind' in Function.prototype)) {
		Function.prototype.bind = function(owner) {
			var self = this;

			if (arguments.length <= 1) {
				return function() {
					return self.apply(owner, arguments);
				};
			}

			var args = _slice(arguments);
			return function() {
				return self.apply(owner, arguments.length === 0 ? args : args.concat(_slice(arguments)));
			};
		};
	}

	var _getConfigurationType = function(val) {
		if (val === null) { return _types[sNull]; }
		if (val === undefined) { return _types[sUndefined]; }

		// we have something, but don't know what
		if (val.name === undefined) {
			if (val !== +val) { return _types[sNaN]; } // NaN check
			return _types[sInfinity]; // Infinity check
		}

		return _types[val.name];
	};

	var _getParameterType = function(val) {
		if (val === null) { return _types[sNull]; }
		if (val === undefined) { return _types[sUndefined]; }
		if (val === true || val === false) { return _types[sBoolean]; }
		if (val && val.nodeType === 1) { return _types[sElement]; } // Element check from Underscore

		var typeString = _toString(val);
		if (_checkMap[typeString] === _types[sNumber]) {
			if (val !== +val) { return _types[sNaN]; } // NaN check
			if (!isFinite(val)) { return _types[sInfinity]; } // Finite check
			return _types[sNumber]; // definitely a number
		}

		return _checkMap[typeString];
	};

	var _convertConfigurationTypes = function(args) {
		var parameters = [],
			idx = 0, length = args.length,
			configItem;
		for (; idx < length; idx++) {
			configItem = args[idx];
			parameters.push(
				(configItem instanceof Custom) ? configItem : _getConfigurationType(configItem)
			);
		}
		return parameters;
	};

	var _convertParametersTypes = function(args) {
		var parameters = [],
			idx = 0, length = args.length;
		for (; idx < length; idx++) {
			parameters.push(_getParameterType(args[idx]));
		}
		return parameters;
	};

	var _doesMapMatchArgsTypes = function(map, argTypes, args) {
		var mapLength = map.length,
			argLength = argTypes.length;

		if (mapLength === 0 && argLength === 0) { return true; }
		if (mapLength !== argLength) { return false; }

		var idx = 0,
			mapItem;
		for (; idx < argLength; idx++) {
			mapItem = map[idx];

			if (mapItem instanceof Custom) {
				if (!mapItem.check(args[idx])) {
					return false;
				}
			}

			if (argTypes[idx] !== mapItem) {
				return false;
			}
		}

		return true;
	};

	var _getArgumentMatch = function(mappings, args) {
		mappings = mappings || [];

		var argTypes = _convertParametersTypes(args),
			idx = 0, length = mappings.length;
		for (; idx < length; idx++) {
			if (_doesMapMatchArgsTypes(mappings[idx].params, argTypes, args)) {
				return mappings[idx];
			}
		}

		return null;
	};

	var _getLengthMatch = function(mappings, args) {
		mappings = mappings || [];

		var argLength = args.length,
			idx = 0, length = mappings.length;
		for (; idx < length; idx++) {
			if (mappings[idx].length === argLength) {
				return mappings[idx];
			}
		}

		return null;
	};

	var _matchAny = function(args, val) {
		var type = _getParameterType(val),
			idx = args.length,
			mapItem;

		while (idx--) {
			mapItem = args[idx];

			if (mapItem instanceof Custom) {
				if (mapItem.check(val)) {
					return true;
				} else {
					continue;
				}
			}

			if (args[idx] === type) {
				return true;
			}
		}

		return false;
	};

	/**
	 * Custom type that validates a value
	 * @constructor
	 * @param {Function} check
	 */
	var Custom = function(check) {
		this.check = check;
	};

	var O = {
		wild: new Custom(function() {
			return true;
		}),
		truthy: new Custom(function(val) {
			return (!!val) === true;
		}),
		falsy: new Custom(function(val) {
			return (!!val) === false;
		}),
		any: function() {
			var args = _convertConfigurationTypes(arguments);
			return new Custom(function(val) {
				return _matchAny(args, val);
			});
		},
		except: function() {
			var args = _convertConfigurationTypes(arguments);
			return new Custom(function(val) {
				return !_matchAny(args, val);
			});
		}
	};

	/**
	 * @constructor
	 */
	var Overload = function() {
		if (!(this instanceof Overload)) { return new Overload(); }

		/**
		 * Methods mapped to argument types
		 * Lazily instanciated
		 * @type {Array}
		 */
		// this._argMaps;

		/**
		 * Methods mapped to argument lengths
		 * Lazily instanciated
		 * @type {Array}
		 */
		// this._lenMaps;

		/**
		 * A fallback function if none
		 * of the criteria match on a call
		 * @type {Function}
		 */
		// this._f;
	};

	Overload.defineType = function(name, check) {
		var custom = new Custom(check);
		return (O[name] = custom);
	};

	Overload.prototype = {

		/** @constructor */
		constructor: Overload,

		args: function() {
			var self = this,
				args = arguments;

			return {
				use: function(method) {
					var argMappings = self._argMaps || (self._argMaps = []);
					argMappings.push({
						params: _convertConfigurationTypes(args),
						method: method
					});
					return self;
				}
			};
		},

		length: function(num) {
			var self = this;
			return {
				use: function(method) {
					var lengthMappings = self._lenMaps || (self._lenMaps = []);
					lengthMappings.push({
						length: (num === undefined) ? method.length : num,
						method: method
					});
					return self;
				}
			};
		},

		err: function() {
			throw 'Overload - exception: No methods matched';
		},

		fallback: function(method) {
			this._f = method;
			return this;
		},

		call: function() {
			var args = _slice(arguments);
			return this._call(args.shift(), args);
		},

		apply: function(context, args) {
			args = (args && args.callee) ? _slice(args) : args;
			return this._call(context, args);
		},

		bind: function(context) {
			var self = this;
			return function() {
				return self._call(context, arguments);
			}.bind(context);
		},

		expose: function() {
			var self = this;
			return function() {
				return self._call(this, arguments);
			};
		},

		_call: function(context, args) {
			args = args || _noopArr;

			// Any argument match, of course, already matches
			// the length match, so this should be done first
			var argMatch = _getArgumentMatch(this._argMaps, args);
			if (argMatch) {
				return argMatch.method.apply(context, args);
			}

			// Check for a length match
			var lengthMatch = _getLengthMatch(this._lenMaps, args);
			if (lengthMatch) {
				return lengthMatch.method.apply(context, args);
			}

			// Check for a fallback
			if (this._f) {
				return this._f.apply(context, args);
			}

			// Error
			return this.err(args);
		}
	};

	root.Overload = Overload;
	root.O = O;

}(this));<commit_msg>Remove superfilous parens<commit_after>(function(root, undefined) {

	// Variablizing the strings for consistency
	// and to avoid harmful dot-notation look-ups with
	// javascript keywords
	var sNull      = 'Null',
		sUndefined = 'Undefined',
		sInfinity  = 'Infinity',
		sDate      = 'Date',
		sNaN       = 'NaN',
		sNumber    = 'Number',
		sString    = 'String',
		sObject    = 'Object',
		sArray     = 'Array',
		sRegExp    = 'RegExp',
		sBoolean   = 'Boolean',
		sFunction  = 'Function',
		sElement   = 'Element';

	// Utilizing the non-standard (but available in modern browsers) Global Object names
	// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
	// Provide a polyfill for items without names
	(function() {
		var globalObjects = [
				sDate,
				sNumber,
				sString,
				sObject,
				sArray,
				sRegExp,
				sBoolean,
				sFunction,
				sElement
			],
			idx = globalObjects.length,
			globalObject;
		while (idx--) {
			globalObject = globalObjects[idx];
			if (!root[globalObject].name) {
				root[globalObject].name = globalObject;
			}
		}
	}());

	/**
	 * Possible values
	 * @type {Object}
	 */
	var _types = {};
	_types[sNull]      = 0;
	_types[sUndefined] = 1;
	_types[sInfinity]  = 2;
	_types[sDate]      = 3;
	_types[sNaN]       = 4;
	_types[sNumber]    = 5;
	_types[sString]    = 6;
	_types[sObject]    = 7;
	_types[sArray]     = 8;
	_types[sRegExp]    = 9;
	_types[sBoolean]   = 10;
	_types[sFunction]  = 11;
	_types[sElement]   = 12;

	/**
	 * Cached reference to Object.prototype.toString
	 * for type checking
	 * @type {Function}
	 */
	var _toString = (function(toString) {
			return function(obj) {
				return toString.call(obj);
			};
		}(({}).toString)),

		_noopArr = [],

		/**
		 * Type checks
		 */
		_checkMap = (function(map) {

			var types = [
					// Only mapping items that need to be mapped.
					// Items not in this list are doing faster
					// (non-string) checks
					//
					// k = key, v = value
					{ k: sDate,     v: _types[sDate]     },
					{ k: sNumber,   v: _types[sNumber]   },
					{ k: sString,   v: _types[sString]   },
					{ k: sObject,   v: _types[sObject]   },
					{ k: sArray,    v: _types[sArray]    },
					{ k: sRegExp,   v: _types[sRegExp]   },
					{ k: sFunction, v: _types[sFunction] }
				],
				idx = types.length;
			while (idx--) {
				map['[object ' + types[idx].k + ']'] = types[idx].v;
			}

			return map;

		}({})),

		/**
		 * Changes arguments to an array
		 * @param  {Arguments} arraylike
		 * @return {Array}
		 */
		_slice = (function(slice) {
			return function(arraylike) {
				return slice.call(arraylike);
			};
		}([].slice));

	// Reference: https://gist.github.com/dhm116/1790197
	if (!('bind' in Function.prototype)) {
		Function.prototype.bind = function(owner) {
			var self = this;

			if (arguments.length <= 1) {
				return function() {
					return self.apply(owner, arguments);
				};
			}

			var args = _slice(arguments);
			return function() {
				return self.apply(owner, arguments.length === 0 ? args : args.concat(_slice(arguments)));
			};
		};
	}

	var _getConfigurationType = function(val) {
		if (val === null) { return _types[sNull]; }
		if (val === undefined) { return _types[sUndefined]; }

		// we have something, but don't know what
		if (val.name === undefined) {
			if (val !== +val) { return _types[sNaN]; } // NaN check
			return _types[sInfinity]; // Infinity check
		}

		return _types[val.name];
	};

	var _getParameterType = function(val) {
		if (val === null) { return _types[sNull]; }
		if (val === undefined) { return _types[sUndefined]; }
		if (val === true || val === false) { return _types[sBoolean]; }
		if (val && val.nodeType === 1) { return _types[sElement]; } // Element check from Underscore

		var typeString = _toString(val);
		if (_checkMap[typeString] === _types[sNumber]) {
			if (val !== +val) { return _types[sNaN]; } // NaN check
			if (!isFinite(val)) { return _types[sInfinity]; } // Finite check
			return _types[sNumber]; // definitely a number
		}

		return _checkMap[typeString];
	};

	var _convertConfigurationTypes = function(args) {
		var parameters = [],
			idx = 0, length = args.length,
			configItem;
		for (; idx < length; idx++) {
			configItem = args[idx];
			parameters.push(
				(configItem instanceof Custom) ? configItem : _getConfigurationType(configItem)
			);
		}
		return parameters;
	};

	var _convertParametersTypes = function(args) {
		var parameters = [],
			idx = 0, length = args.length;
		for (; idx < length; idx++) {
			parameters.push(_getParameterType(args[idx]));
		}
		return parameters;
	};

	var _doesMapMatchArgsTypes = function(map, argTypes, args) {
		var mapLength = map.length,
			argLength = argTypes.length;

		if (mapLength === 0 && argLength === 0) { return true; }
		if (mapLength !== argLength) { return false; }

		var idx = 0,
			mapItem;
		for (; idx < argLength; idx++) {
			mapItem = map[idx];

			if (mapItem instanceof Custom) {
				if (!mapItem.check(args[idx])) {
					return false;
				}
			}

			if (argTypes[idx] !== mapItem) {
				return false;
			}
		}

		return true;
	};

	var _getArgumentMatch = function(mappings, args) {
		mappings = mappings || [];

		var argTypes = _convertParametersTypes(args),
			idx = 0, length = mappings.length;
		for (; idx < length; idx++) {
			if (_doesMapMatchArgsTypes(mappings[idx].params, argTypes, args)) {
				return mappings[idx];
			}
		}

		return null;
	};

	var _getLengthMatch = function(mappings, args) {
		mappings = mappings || [];

		var argLength = args.length,
			idx = 0, length = mappings.length;
		for (; idx < length; idx++) {
			if (mappings[idx].length === argLength) {
				return mappings[idx];
			}
		}

		return null;
	};

	var _matchAny = function(args, val) {
		var type = _getParameterType(val),
			idx = args.length,
			mapItem;

		while (idx--) {
			mapItem = args[idx];

			if (mapItem instanceof Custom) {
				if (mapItem.check(val)) {
					return true;
				} else {
					continue;
				}
			}

			if (args[idx] === type) {
				return true;
			}
		}

		return false;
	};

	/**
	 * Custom type that validates a value
	 * @constructor
	 * @param {Function} check
	 */
	var Custom = function(check) {
		this.check = check;
	};

	var O = {
		wild: new Custom(function() {
			return true;
		}),
		truthy: new Custom(function(val) {
			return !!val === true;
		}),
		falsy: new Custom(function(val) {
			return !!val === false;
		}),
		any: function() {
			var args = _convertConfigurationTypes(arguments);
			return new Custom(function(val) {
				return _matchAny(args, val);
			});
		},
		except: function() {
			var args = _convertConfigurationTypes(arguments);
			return new Custom(function(val) {
				return !_matchAny(args, val);
			});
		}
	};

	/**
	 * @constructor
	 */
	var Overload = function() {
		if (!(this instanceof Overload)) { return new Overload(); }

		/**
		 * Methods mapped to argument types
		 * Lazily instanciated
		 * @type {Array}
		 */
		// this._argMaps;

		/**
		 * Methods mapped to argument lengths
		 * Lazily instanciated
		 * @type {Array}
		 */
		// this._lenMaps;

		/**
		 * A fallback function if none
		 * of the criteria match on a call
		 * @type {Function}
		 */
		// this._f;
	};

	Overload.defineType = function(name, check) {
		var custom = new Custom(check);
		return (O[name] = custom);
	};

	Overload.prototype = {

		/** @constructor */
		constructor: Overload,

		args: function() {
			var self = this,
				args = arguments;

			return {
				use: function(method) {
					var argMappings = self._argMaps || (self._argMaps = []);
					argMappings.push({
						params: _convertConfigurationTypes(args),
						method: method
					});
					return self;
				}
			};
		},

		length: function(num) {
			var self = this;
			return {
				use: function(method) {
					var lengthMappings = self._lenMaps || (self._lenMaps = []);
					lengthMappings.push({
						length: (num === undefined) ? method.length : num,
						method: method
					});
					return self;
				}
			};
		},

		err: function() {
			throw 'Overload - exception: No methods matched';
		},

		fallback: function(method) {
			this._f = method;
			return this;
		},

		call: function() {
			var args = _slice(arguments);
			return this._call(args.shift(), args);
		},

		apply: function(context, args) {
			args = (args && args.callee) ? _slice(args) : args;
			return this._call(context, args);
		},

		bind: function(context) {
			var self = this;
			return function() {
				return self._call(context, arguments);
			}.bind(context);
		},

		expose: function() {
			var self = this;
			return function() {
				return self._call(this, arguments);
			};
		},

		_call: function(context, args) {
			args = args || _noopArr;

			// Any argument match, of course, already matches
			// the length match, so this should be done first
			var argMatch = _getArgumentMatch(this._argMaps, args);
			if (argMatch) {
				return argMatch.method.apply(context, args);
			}

			// Check for a length match
			var lengthMatch = _getLengthMatch(this._lenMaps, args);
			if (lengthMatch) {
				return lengthMatch.method.apply(context, args);
			}

			// Check for a fallback
			if (this._f) {
				return this._f.apply(context, args);
			}

			// Error
			return this.err(args);
		}
	};

	root.Overload = Overload;
	root.O = O;

}(this));<|endoftext|>