<commit_before>function PluginInterface() {
    this.listeners = [];
}

PluginInterface.prototype.on = PluginInterface.prototype.addListener = function addListener(type, listener) {
    if (typeof listener !== 'function') {
        throw new TypeError('listener must be a function');
    }

    if (this.listeners[type]) {
        if (!_.contains(this.listeners[type], listener)) {
            this.listeners[type].push(listener);
        }
    } else {
        this.listeners[type] = [listener];
    }

    return this;
};

PluginInterface.prototype.once = function once(type, listener) {
    if (typeof listener !== 'function') {
        throw new TypeError('listener must be a function');
    }

    var fired = false;

    function g() {
        this.removeListener(type, g);

        if (!fired) {
            fired = true;
            listener.apply(this, arguments);
        }
    }

    g.listener = listener;
    this.on(type, g);

    return this;
};

PluginInterface.prototype.off = PluginInterface.prototype.removeListener = function removeListener(type, listener) {
    if (!this.listeners[type]) {
        return this;
    }

    this.listeners[type] = _.without(this.listeners[type], listener);

    return this;
};

PluginInterface.prototype.emit = function emit(type, data) {
    var that = this;
    return new Promise(function (emit_resolve, emit_reject) {
        var rejected = false,
            rejected_reasons = [];

        if (!that.listeners[type]) {
            return emit_resolve(data);
        }

        (that.listeners[type].reduce(function (listener_promise, listener) {
            return listener_promise.then(function (data) {
                return new Promise(function (resolve) {
                    listener({
                        callback: function () {
                            resolve(data);
                        },
                        preventDefault: function (reason) {
                            rejected = true;
                            if (reason) {
                                rejected_reasons.push(reason);
                            }
                        }
                    }, data);
                });
            });
        }, Promise.resolve(data))).then(function (data) {
            if (rejected) {
                emit_reject({data: data, reasons: rejected_reasons});
            } else {
                emit_resolve(data);
            }
        });
    });
};

// If running a node module, set the exports
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
    module.exports = PluginInterface;
}

/* Test cases

var p = new PluginInterface();
p.on('test', function (event, data) {
    data.a += '!';
    event.callback();
});

p.emit('test', {a: 'hello world'}).then(function (data) {
    if (data.a === 'hello world!') {
        console.log('Test passed');
    } else {
        console.error('Test failed!');
    }
}, function (err) {
    console.error('Test failed!');<commit_msg>Updating client-side PluginInterface to its server-side version<commit_after>function PluginInterface() {
    this.listeners = [];
}

PluginInterface.prototype.on = PluginInterface.prototype.addListener = function addListener(type, listener) {
    if (typeof listener !== 'function') {
        throw new TypeError('listener must be a function');
    }

    if (this.listeners[type]) {
        if (!_.contains(this.listeners[type], listener)) {
            this.listeners[type].push(listener);
        }
    } else {
        this.listeners[type] = [listener];
    }

    return this;
};

PluginInterface.prototype.once = function once(type, listener) {
    if (typeof listener !== 'function') {
        throw new TypeError('listener must be a function');
    }

    var fired = false;

    function g() {
        this.removeListener(type, g);

        if (!fired) {
            fired = true;
            listener.apply(this, arguments);
        }
    }

    g.listener = listener;
    this.on(type, g);

    return this;
};

PluginInterface.prototype.off = PluginInterface.prototype.removeListener = function removeListener(type, listener) {
    if (!this.listeners[type]) {
        return this;
    }

    this.listeners[type] = _.without(this.listeners[type], listener);

    return this;
};

PluginInterface.prototype.emit = function emit(type, data) {
    var that = this;

    return new Promise(function (emit_resolve, emit_reject) {
        var rejected = false,
            rejected_reasons = [],
            listeners_promise;

        if (!that.listeners[type]) {
            return emit_resolve(data);
        }

        // Add each listener as a promise .then()
        listeners_promise = that.listeners[type].reduce(function (listener_promise, listener) {
            return listener_promise.then(function (data) {
                return new Promise(function (resolve) {
                    var event_data = {
                        callback: function () {
                            resolve(data);
                            event_data.callback = null;
                        },
                        preventDefault: function (reason) {
                            rejected = true;
                            if (reason) {
                                rejected_reasons.push(reason);
                            }
                        },
                        wait: false
                    };

                    listener(event_data, data);

                    // If the module has not specified that we should wait, callback now
                    if (!event_data.wait && event_data.callback) {
                        event_data.callback();
                    }
                });
            });
        }, Promise.resolve(data));

        // After all the listeners have been called, resolve back with any modified data
        listeners_promise.then(function (data) {
            if (rejected) {
                emit_reject({data: data, reasons: rejected_reasons});
            } else {
                emit_resolve(data);
            }
        });
    });
};

// If running a node module, set the exports
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
    module.exports = PluginInterface;
}

/* Test cases

var p = new PluginInterface();
p.on('test', function (event, data) {
    data.a += '!';
    event.callback();
});

p.emit('test', {a: 'hello world'}).then(function (data) {
    if (data.a === 'hello world!') {
        console.log('Test passed');
    } else {
        console.error('Test failed!');
    }
}, function (err) {
    console.error('Test failed!');
{'fraction_non_alphanumeric': 0.12330419053361472, 'fraction_numerical': 0.0, 'mean_word_length': 1.796038769490097, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '3295644', 'n_tokens_mistral': 1766, 'n_tokens_neox': 1706, 'n_words': 406}