<commit_before>var input_pill = (function () {

var exports = {};

exports.random_id = function () {
    return Math.random().toString(16);
};

exports.create = function (opts) {
    // a dictionary of the key codes that are associated with each key
    // to make if/else more human readable.
    var KEY = {
        ENTER: 13,
        BACKSPACE: 8,
        LEFT_ARROW: 37,
        RIGHT_ARROW: 39,
        COMMA: 188,
    };

    if (!opts.container) {
        blueslip.error('Pill needs container.');
        return;
    }

    if (!opts.create_item_from_text) {
        blueslip.error('Pill needs create_item_from_text');
        return;
    }

    if (!opts.get_text_from_item) {
        blueslip.error('Pill needs get_text_from_item');
        return;
    }

    // a stateful object of this `pill_container` instance.
    // all unique instance information is stored in here.
    var store = {
        pills: [],
        $parent: opts.container,
        $input: opts.container.find(".input"),
        create_item_from_text: opts.create_item_from_text,
        get_text_from_item: opts.get_text_from_item,
    };

    // a dictionary of internal functions. Some of these are exposed as well,
    // and nothing in here should be assumed to be private (due to the passing)
    // of the `this` arg in the `Function.prototype.bind` use in the prototype.
    var funcs = {
        // return the value of the contenteditable input form.
        value: function (input_elem) {
            return input_elem.innerText.trim();
        },

        // clear the value of the input form.
        clear: function (input_elem) {
            input_elem.innerText = "";
        },

        clear_text: function () {
            store.$input.text("");
        },

        create_item: function (text) {
            var existing_items = funcs.items();
            var item = store.create_item_from_text(text, existing_items);

            if (!item || !item.display_value) {
                store.$input.addClass("shake");
                return;
            }

            if (typeof store.onPillCreate === "function") {
                store.onPillCreate();
            }

            return item;
        },

        // This is generally called by typeahead logic, where we have all
        // the data we need (as opposed to, say, just a user-typed email).
        appendValidatedData: function (item) {
            var id = exports.random_id();

            if (!item.display_value) {
                blueslip.error('no display_value returned');
                return;
            }

            var payload = {
                id: id,
                item: item,
            };

            store.pills.push(payload);

            var has_image = item.img_src !== undefined;

            var opts = {
                id: payload.id,
                display_value: item.display_value,
                has_image: has_image,
            };

            if (has_image) {
                opts.img_src = item.img_src;
            }

            var pill_html = templates.render('input_pill', opts);
            payload.$element = $(pill_html);
            store.$input.before(payload.$element);
        },

        // this appends a pill to the end of the container but before the
        // input block.
        appendPill: function (value) {
            if (value.length === 0) {
                return;
            }
            if (value.match(",")) {
                funcs.insertManyPills(value);
                return false;
            }

            var payload = this.create_item(value);
            // if the pill object is undefined, then it means the pill was
            // rejected so we should return out of this.
            if (!payload) {
                return false;
            }

            this.appendValidatedData(payload);
        },

        // this searches given a particlar pill ID for it, removes the node
        // from the DOM, removes it from the array and returns it.
        // this would generally be used for DOM-provoked actions, such as a user
        // clicking on a pill to remove it.
        removePill: function (id) {
            var idx;
            for (var x = 0; x < store.pills.length; x += 1) {
                if (store.pills[x].id === id) {
                    idx = x;
                }
            }

            if (typeof idx === "number") {
                store.pills[idx].$element.remove();
                var pill = store.pills.splice(idx, 1);
                if (typeof store.removePillFunction === "function") {
                    store.removePillFunction(pill);
                }

                return pill;
            }
        },

        // this will remove the last pill in the container -- by defaulat tied
        // to the "backspace" key when the value of the input is empty.
        removeLastPill: function () {
            var pill = store.pills.pop();

            if (pill) {
                pill.$element.remove();
                if (typeof store.removePillFunction === "function") {
                    store.removePillFunction(pill);
                }
            }
        },

        removeAllPills: function () {
            while (store.pills.length > 0) {
                this.removeLastPill();
            }

            this.clear(store.$input[0]);
        },

        insertManyPills: function (pills) {
            if (typeof pills === "string") {
                pills = pills.split(/,/g).map(function (pill) {
                    return pill.trim();
                });
            }

            // this is an array to push all the errored values to, so it's drafts
            // of pills for the user to fix.
            var drafts = [];

            pills.forEach(function (pill) {
                // if this returns `false`, it erroed and we should push it to
                // the draft pills.
                if (funcs.appendPill(pill) === false) {
                    drafts.push(pill);
                }
            });

            store.$input.text(drafts.join(", "));
            // when using the `text` insertion feature with jQuery the caret is
            // placed at the beginning of the input field, so this moves it to
            // the end.
            ui_util.place_caret_at_end(store.$input[0]);
<commit_msg>pills: Allow removing pill without calling the remove event handler.<commit_after>var input_pill = (function () {

var exports = {};

exports.random_id = function () {
    return Math.random().toString(16);
};

exports.create = function (opts) {
    // a dictionary of the key codes that are associated with each key
    // to make if/else more human readable.
    var KEY = {
        ENTER: 13,
        BACKSPACE: 8,
        LEFT_ARROW: 37,
        RIGHT_ARROW: 39,
        COMMA: 188,
    };

    if (!opts.container) {
        blueslip.error('Pill needs container.');
        return;
    }

    if (!opts.create_item_from_text) {
        blueslip.error('Pill needs create_item_from_text');
        return;
    }

    if (!opts.get_text_from_item) {
        blueslip.error('Pill needs get_text_from_item');
        return;
    }

    // a stateful object of this `pill_container` instance.
    // all unique instance information is stored in here.
    var store = {
        pills: [],
        $parent: opts.container,
        $input: opts.container.find(".input"),
        create_item_from_text: opts.create_item_from_text,
        get_text_from_item: opts.get_text_from_item,
    };

    // a dictionary of internal functions. Some of these are exposed as well,
    // and nothing in here should be assumed to be private (due to the passing)
    // of the `this` arg in the `Function.prototype.bind` use in the prototype.
    var funcs = {
        // return the value of the contenteditable input form.
        value: function (input_elem) {
            return input_elem.innerText.trim();
        },

        // clear the value of the input form.
        clear: function (input_elem) {
            input_elem.innerText = "";
        },

        clear_text: function () {
            store.$input.text("");
        },

        create_item: function (text) {
            var existing_items = funcs.items();
            var item = store.create_item_from_text(text, existing_items);

            if (!item || !item.display_value) {
                store.$input.addClass("shake");
                return;
            }

            if (typeof store.onPillCreate === "function") {
                store.onPillCreate();
            }

            return item;
        },

        // This is generally called by typeahead logic, where we have all
        // the data we need (as opposed to, say, just a user-typed email).
        appendValidatedData: function (item) {
            var id = exports.random_id();

            if (!item.display_value) {
                blueslip.error('no display_value returned');
                return;
            }

            var payload = {
                id: id,
                item: item,
            };

            store.pills.push(payload);

            var has_image = item.img_src !== undefined;

            var opts = {
                id: payload.id,
                display_value: item.display_value,
                has_image: has_image,
            };

            if (has_image) {
                opts.img_src = item.img_src;
            }

            var pill_html = templates.render('input_pill', opts);
            payload.$element = $(pill_html);
            store.$input.before(payload.$element);
        },

        // this appends a pill to the end of the container but before the
        // input block.
        appendPill: function (value) {
            if (value.length === 0) {
                return;
            }
            if (value.match(",")) {
                funcs.insertManyPills(value);
                return false;
            }

            var payload = this.create_item(value);
            // if the pill object is undefined, then it means the pill was
            // rejected so we should return out of this.
            if (!payload) {
                return false;
            }

            this.appendValidatedData(payload);
        },

        // this searches given a particlar pill ID for it, removes the node
        // from the DOM, removes it from the array and returns it.
        // this would generally be used for DOM-provoked actions, such as a user
        // clicking on a pill to remove it.
        removePill: function (id) {
            var idx;
            for (var x = 0; x < store.pills.length; x += 1) {
                if (store.pills[x].id === id) {
                    idx = x;
                }
            }

            if (typeof idx === "number") {
                store.pills[idx].$element.remove();
                var pill = store.pills.splice(idx, 1);
                if (typeof store.removePillFunction === "function") {
                    store.removePillFunction(pill);
                }

                return pill;
            }
        },

        // this will remove the last pill in the container -- by default tied
        // to the "backspace" key when the value of the input is empty.
        // If quiet is a truthy value, the event handler associated with the
        // pill will not be evaluated. This is useful when using clear to reset
        // the pills.
        removeLastPill: function (quiet) {
            var pill = store.pills.pop();

            if (pill) {
                pill.$element.remove();
                if (!quiet && typeof store.removePillFunction === "function") {
                    store.removePillFunction(pill);
                }
            }
        },

        removeAllPills: function (quiet) {
            while (store.pills.length > 0) {
                this.removeLastPill(quiet);
            }

            this.clear(store.$input[0]);
        },

        insertManyPills: function (pills) {
            if (typeof pills === "string") {
                pills = pills.split(/,/g).map(function (pill) {
                    return pill.trim();
                });
            }

            // this is an array to push all the errored values to, so it's drafts
            // of pills for the user to fix.
            var drafts = [];

            pills.forEach(function (pill) {
                // if this returns `false`, it erroed and we should push it to
                // the draft pills.
                if (funcs.appendPill(pill) === false) {
                    drafts.push(pill);
                }
            });

            store.$input.text(drafts.join(", "));
            // when using the `text` insertion feature with jQuery the caret is
            // placed at the beginning of the input field, so this moves it to
            // the end.
            ui_util.place_caret_at_end(store.$input[0]);

{'fraction_non_alphanumeric': 0.10181311018131102, 'fraction_numerical': 0.0029443669611033626, 'mean_word_length': 1.4514719848053181, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1425408', 'n_tokens_mistral': 3548, 'n_tokens_neox': 3360, 'n_words': 1035}