<commit_before>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2018  School of Business and Engineering Vaud, Comem, MEI
 * Licensed under the MIT License
 */
/**
 * @fileoverview add wegas-mcq-selector widget, exposed on Y.Wegas.MCQSelector
 * @author <NAME> <jarle.hulaas at heig-vd.ch>
 */
/*global YUI*/
YUI.add('wegas-mcq-selector', function(Y) {
    'use strict';
    var CONTENTBOX = "contentBox",
        NO_CORRESPONDENCE = "<i>Aucune question correspondante</i>",   // "<i>No relevant question found</i>",
        INVITE = "<i>Entrer vos mots-clés</i>",   // "<i>Please type in your keywords</i>",
        MESSAGE_FIELD = ".wegas-selector-message",
        MIN_INPUT_LENGTH = 2, // Shorter inputs will be ignored
        WIDGETBOX = ".wegas-selector-widget",
        SELECTORBOX = ".wegas-selector-list",
        DIALOG_CLASS = "DialogueDescriptor",
        QUESTION_CLASS = "QuestionDescriptor",
        DIALOGUE_TRANSITION = "DialogueTransition",
        QUESTION_CHOICE = "SingleResultChoiceDescriptor",
        QUESTION_CHOICE_CONDITIONAL = "ChoiceDescriptor",
        // The widget alternates between the three following states:
        SelectorStates = {
            EDITING : 1,
            PROPOSING : 2,
            SELECTED : 3
        };

    /**
     * @constructor
     * @extends Y.Wegas.Widget
     */
    var MCQSelector = Y.Base.create(
        'wegas-mcq-selector',
        Y.Widget,
        [Y.WidgetChild,
            Y.Wegas.Widget,
            Y.Wegas.Editable],
        {
            CONTENT_TEMPLATE: '<div><div class="wegas-selector-input"><input><button class="yui3-button wegas-selector-submit"></button></div><div class="wegas-selector-message"></div><div class="wegas-selector-block"><ul class="wegas-selector-list"></ul></div><div class="wegas-selector-widget"></div></div>',

            initializer: function () {
                this.handlers = [];
                this.candidates = [];
                this.selectedItem = null;
                this.currentState = SelectorStates.EDITING;
                this.tooltip = null;
                // A sanitized, lowercase copy of the input field:
                this.input = "";
                this.requireValidate = this.get('requireValidate');
                this.showDescription = this.get('showDescription');
            },

            // Tells if the given string is only made of letters and numbers
            isAlphanumeric: function(txt)
            {
                var letterNumber = /^[0-9a-zA-Z]+$/;
                return txt.match(letterNumber) != null;
            },

            // Returns 'true' if the given needle (no regex) is found in the given haystack (a string).
            // The needle will then match the beginning of a word (and not start in the middle of it).
            // It will deaccentify the haystack, so that "tete" will match "tête".
            // The needle is expected to be lowercase.
            matches: function (haystack, needle) {
                var index = -1;
                haystack = Y.Wegas.Helper.Diacritics.removeDiacritics(haystack.toLowerCase());
                needle = Y.Wegas.Helper.Diacritics.removeDiacritics(needle);
                for(;;) {
                    index = haystack.indexOf(needle, index + 1);
                    if (index < 0) {
                        return false;
                    }
                    if (index === 0 || (index > 0 && !this.isAlphanumeric(haystack.charAt(index - 1)))) {
                        return true;
                    }
                }
            },

            // Searches for a candidate with the given name and optionalId.
            // Returns null if not found.
            findCandidate: function (name, optionalId) {
                var i, length = this.candidates.length;
                for (i = 0; i < length; i++) {
                    if (this.candidates[i].name === name) {
                        // Allow both strings and numbers as ids:
                        if (optionalId && optionalId != this.candidates[i].this.get("id")) {
                            continue;
                        }
                        return this.candidates[i];
                    }
                }
                return null;
            },

            /*
            ** Scans the dialog/question folder and checks if each item is currently active and answerable
            ** before adding it to the list of candidates.
             */
            scanFolder: function () {
                var items = (this.get("variable.evaluated") ?
                    (this.get("flatten") ?
                        this.get("variable.evaluated").flatten() :
                        this.get("variable.evaluated").get("items")) :
                    []),
                    i,
                    currItem, currType,
                    length = items.length;

                if (length === 0) {
                    this.showMessage("No Dialogs or Questions could not be found inside current folder");
                    return;
                }

                for (i = 0; i < length; i++) {
                    currItem = items[i];
                    currType = currItem.get("@class");

                    if (currType === DIALOG_CLASS) {
                        if (currItem.getInstance().get("enabled")) {
                            var state = currItem.getCurrentState();
                            if (!(state instanceof Y.Wegas.persistence.DialogueState)) {
                                Y.log("State isn't a dialogue state.", 'error', 'MCQSelector');
                                return;
                            }

                            // Call to wegas-statemachine-entities, with async callback invocation.
                            // The standard entitychooser widget might propose dialogs with no fireable transitions.
                            state.getAvailableActions(Y.bind(this.addDialogueTransitions, this));
                        }
                    } else if (currType === QUESTION_CLASS) {
                        if (currItem.getInstance().get("active")) {
                            if (currItem.get("cbx")) {
                                var error = "Unable to handle checkbox-type questions (" +
                                    (currItem.get("editorTag") ? currItem.get("editorTag")+"-" : "") + I18n.t(currItem.get("label")).trim() + ")";
                                Y.log(error, "warn", 'MCQSelector')
                            } else {
                                this.addQuestion(currItem);
                            }
                        }
                    } else {
                        Y.log("Found unknown variable type inside folder : " + currType,
                            'warn', 'MCQSelector');
                        continue;
                    }
                }
            },

            // Modifier une variable définie au niveau scénariste Wegas
            affecterWegas: function(nomVariable, valeur){
                Y.Wegas.Facade.Variable.script.remoteEval("Variable.find(gameModel, \"" + nomVariable + "\").setValue(self, " + valeur + ");", {
                    on: {
                        success: Y.bind(function () {
                            //alert(nomVariable + " OK!");
                        }, this),
                        failure: Y.bind(function () {
                            alert("Erreur interne: Impossible de sauvegarder " + nomVariable + " !! \n Tenter de recommencer cet exercice en rechargeant la page (ctrl-R)");
                            throw "Impossible de sauvegarder la variable " + nomVariable;
                        }, this)
                    }
                });
            },


            submitKeywords: function() {
                if (this.input.length >= MIN_INPUT_LENGTH) {
                    this.scanFolder();
                    this.get(CONTENTBOX).one(SELECTORBOX).empty();
                    this.updateCandidatesDisplay();
                    var attempts = this.get("numberOfAttempts.evaluated"),
                        nb = 1 + attempts.getInstance().get("value");
                    this.affecterWegas(attempts.get("name"), nb);
                }
            },

            syncUI: function () {
                this.candidates = [];
                if (this.input.length < MIN_INPUT_LENGTH) {
                    this.showMessage(INVITE);
                    this.hideCandidates(SelectorStates.EDITING);
                    this.disableSubmitBtn();
                } else {
                    if (this.requireValidate) {
                        this.hideCandidates(SelectorStates.EDITING);
                        this.enableSubmitBtn();
                    } else {
                        this.submitKeywords();
                    }
                }
            },

            clearInput: function () {
                this.get(CONTENTBOX).one('input').set("value", "");
                this.input = "";
                if (this.requireValidate) {<commit_msg>Question selector: Wait for a typing pause in automatic submission mode<commit_after>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2018  School of Business and Engineering Vaud, Comem, MEI
 * Licensed under the MIT License
 */
/**
 * @fileoverview add wegas-mcq-selector widget, exposed on Y.Wegas.MCQSelector
 * @author <NAME> <jarle.hulaas at heig-vd.ch>
 */
/*global YUI*/
YUI.add('wegas-mcq-selector', function(Y) {
    'use strict';
    var CONTENTBOX = "contentBox",
        NO_CORRESPONDENCE = "<i>Aucune question correspondante</i>",   // "<i>No relevant question found</i>",
        INVITE = "<i>Entrer vos mots-clés</i>",   // "<i>Please type in your keywords</i>",
        MESSAGE_FIELD = ".wegas-selector-message",
        MIN_INPUT_LENGTH = 2, // Shorter inputs will be ignored
        WIDGETBOX = ".wegas-selector-widget",
        SELECTORBOX = ".wegas-selector-list",
        DIALOG_CLASS = "DialogueDescriptor",
        QUESTION_CLASS = "QuestionDescriptor",
        DIALOGUE_TRANSITION = "DialogueTransition",
        QUESTION_CHOICE = "SingleResultChoiceDescriptor",
        QUESTION_CHOICE_CONDITIONAL = "ChoiceDescriptor",
        // The widget alternates between the three following states:
        SelectorStates = {
            EDITING : 1,
            PROPOSING : 2,
            SELECTED : 3
        };

    /**
     * @constructor
     * @extends Y.Wegas.Widget
     */
    var MCQSelector = Y.Base.create(
        'wegas-mcq-selector',
        Y.Widget,
        [Y.WidgetChild,
            Y.Wegas.Widget,
            Y.Wegas.Editable],
        {
            CONTENT_TEMPLATE: '<div><div class="wegas-selector-input"><input><button class="yui3-button wegas-selector-submit"></button></div><div class="wegas-selector-message"></div><div class="wegas-selector-block"><ul class="wegas-selector-list"></ul></div><div class="wegas-selector-widget"></div></div>',

            initializer: function () {
                this.handlers = [];
                this.candidates = [];
                this.selectedItem = null;
                this.currentState = SelectorStates.EDITING;
                this.tooltip = null;
                // A sanitized, lowercase copy of the input field:
                this.input = "";
                this.requireValidate = this.get('requireValidate');
                this.showDescription = this.get('showDescription');
                this.attemptsVarName = this.get("numberOfAttempts.evaluated").get("name");
                this.isUpdatingCounter = false;
            },

            // Tells if the given string is only made of letters and numbers
            isAlphanumeric: function(txt)
            {
                var letterNumber = /^[0-9a-zA-Z]+$/;
                return txt.match(letterNumber) != null;
            },

            // Returns 'true' if the given needle (no regex) is found in the given haystack (a string).
            // The needle will then match the beginning of a word (and not start in the middle of it).
            // It will deaccentify the haystack, so that "tete" will match "tête".
            // The needle is expected to be lowercase.
            matches: function (haystack, needle) {
                var index = -1;
                haystack = Y.Wegas.Helper.Diacritics.removeDiacritics(haystack.toLowerCase());
                needle = Y.Wegas.Helper.Diacritics.removeDiacritics(needle);
                for(;;) {
                    index = haystack.indexOf(needle, index + 1);
                    if (index < 0) {
                        return false;
                    }
                    if (index === 0 || (index > 0 && !this.isAlphanumeric(haystack.charAt(index - 1)))) {
                        return true;
                    }
                }
            },

            // Searches for a candidate with the given name and optionalId.
            // Returns null if not found.
            findCandidate: function (name, optionalId) {
                var i, length = this.candidates.length;
                for (i = 0; i < length; i++) {
                    if (this.candidates[i].name === name) {
                        // Allow both strings and numbers as ids:
                        if (optionalId && optionalId != this.candidates[i].this.get("id")) {
                            continue;
                        }
                        return this.candidates[i];
                    }
                }
                return null;
            },

            /*
            ** Scans the dialog/question folder and checks if each item is currently active and answerable
            ** before adding it to the list of candidates.
             */
            scanFolder: function () {
                var items = (this.get("variable.evaluated") ?
                    (this.get("flatten") ?
                        this.get("variable.evaluated").flatten() :
                        this.get("variable.evaluated").get("items")) :
                    []),
                    i,
                    currItem, currType,
                    length = items.length;

                if (length === 0) {
                    this.showMessage("No Dialogs or Questions could not be found inside current folder");
                    return;
                }

                for (i = 0; i < length; i++) {
                    currItem = items[i];
                    currType = currItem.get("@class");

                    if (currType === DIALOG_CLASS) {
                        if (currItem.getInstance().get("enabled")) {
                            var state = currItem.getCurrentState();
                            if (!(state instanceof Y.Wegas.persistence.DialogueState)) {
                                Y.log("State isn't a dialogue state.", 'error', 'MCQSelector');
                                return;
                            }

                            // Call to wegas-statemachine-entities, with async callback invocation.
                            // The standard entitychooser widget might propose dialogs with no fireable transitions.
                            state.getAvailableActions(Y.bind(this.addDialogueTransitions, this));
                        }
                    } else if (currType === QUESTION_CLASS) {
                        if (currItem.getInstance().get("active")) {
                            if (currItem.get("cbx")) {
                                var error = "Unable to handle checkbox-type questions (" +
                                    (currItem.get("editorTag") ? currItem.get("editorTag")+"-" : "") + I18n.t(currItem.get("label")).trim() + ")";
                                Y.log(error, "warn", 'MCQSelector')
                            } else {
                                this.addQuestion(currItem);
                            }
                        }
                    } else {
                        Y.log("Found unknown variable type inside folder : " + currType,
                            'warn', 'MCQSelector');
                        continue;
                    }
                }
            },

            // Perform server-side incrementation of attempts counter
            incrementAttempts: function(){
                if (this.isUpdatingCounter){
                    // Skip too frequent update requests
                    return;
                }
                this.isUpdatingCounter = true;
                Y.Wegas.Facade.Variable.script.remoteEval("Variable.find(gameModel, \"" + this.attemptsVarName + "\").add(self, 1);", {
                    on: {
                        success: Y.bind(function () {
                            this.isUpdatingCounter = false;
                        }, this),
                        failure: Y.bind(function () {
                            this.isUpdatingCounter = false;
                            //throw "Cannot save variable " + nomVariable;
                        }, this)
                    }
                });
            },


            submitKeywords: function() {
                if (this.input.length >= MIN_INPUT_LENGTH) {
                    this.scanFolder();
                    this.get(CONTENTBOX).one(SELECTORBOX).empty();
                    this.updateCandidatesDisplay();
                    this.incrementAttempts();
                }
            },

            syncUI: function () {
                this.candidates = [];
                if (this.input.length < MIN_INPUT_LENGTH) {
                    this.showMessage(INVITE);
                    this.hideCandidates(SelectorStates.EDITING);
                    this.disableSubmitBtn();
                } else {
                    if (this.requireValidate) {
                        this.hideCandidates(SelectorStates.EDITING);
                        this.enableSubmitBtn();
                    } else {
                        // Wait for a typing pause of at least one second before using the search string:
                        if (this.wait) {
                            this.wait.cancel();
                        }
                        this.wait = Y.later(1000, this, function() {
                            this.wait = null;
                            this.submitKeywords();
                        });
                    }
                }
            },

            clearInput: function () {
                this.get(CONTENTBOX).one('input').set("value", "");
                this.input = "";
                if (this.requireValidate) {
{'fraction_non_alphanumeric': 0.1094136632598171, 'fraction_numerical': 0.0026896180742334587, 'mean_word_length': 1.2744066552483484, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5525663', 'n_tokens_mistral': 4784, 'n_tokens_neox': 4467, 'n_words': 1038}