<commit_before>import { getScope, debounce, warn } from './utils/helpers';

const DEFAULT_EVENT_NAME = 'veeValidate';

export default class ListenerGenerator
{
    constructor(el, binding, context, options) {
        this.callbacks = [];
        this.el = el;
        this.binding = binding;
        this.vm = context;
        this.options = options;
        this.fieldName = binding.expression || el.name;
    }

    /**
     * Determines if the validation rule requires additional listeners on target fields.
     */
    _hasFieldDependency(rules) {
        const results = rules.split('|').filter(r => !! r.match(/\b(confirmed|after|before):/));
        if (! results.length) {
            return false;
        }

        return results[0].split(':')[1];
    }

    /**
     * Validates input value, triggered by 'input' event.
     */
    _inputListener() {
        this.vm.$validator.validate(this.fieldName, this.el.value, getScope(this.el));
    }

    /**
     * Validates files, triggered by 'change' event.
     */
    _fileListener() {
        const isValid = this.vm.$validator.validate(
            this.fieldName, this.el.files, getScope(this.el)
        );
        if (! isValid && this.binding.modifiers.reject) {
            // eslint-disable-next-line
            el.value = '';
        }
    }

    /**
     * Validates radio buttons, triggered by 'change' event.
     */
    _radioListener() {
        const checked = document.querySelector(`input[name="${this.el.name}"]:checked`);
        if (! checked) {
            this.vm.$validator.validate(this.fieldName, null, getScope(this.el));
            return;
        }

        this.vm.$validator.validate(this.fieldName, checked.value, getScope(this.el));
    }

    /**
     * Validates checkboxes, triggered by change event.
     */
    _checkboxListener() {
        const checkedBoxes = document.querySelectorAll(`input[name="${this.el.name}"]:checked`);
        if (! checkedBoxes || ! checkedBoxes.length) {
            this.vm.$validator.validate(this.fieldName, null, getScope(this.el));
            return;
        }

        [...checkedBoxes].forEach(box => {
            this.vm.$validator.validate(this.fieldName, box.value, getScope(this.el));
        });
    }

    /**
     * Returns a scoped callback, only runs if the el scope is the same as the recieved scope
     * From the event.
     */
    _getScopedListener(callback) {
        return (scope) => {
            if (! scope || scope === getScope(this.el) || scope instanceof Event) {
                callback();
            }
        };
    }

    /**
     * Attaches validator event-triggered validation.
     */
    _attachValidatorEvent() {
        const listener = this._getScopedListener(this._getSuitableListener().listener.bind(this));

        this.vm.$on(DEFAULT_EVENT_NAME, listener);
        this.callbacks.push({ name: DEFAULT_EVENT_NAME, listener });
        this.vm.$on('VALIDATOR_OFF', (field) => {
            if (this.fieldName === field) {
                this.detach();
            }
        });

        const fieldName = this._hasFieldDependency(this.el.dataset.rules);
        if (fieldName) {
            // Wait for the validator ready triggered when vm is mounted because maybe
            // the element isn't mounted yet.
            this.vm.$once('validatorReady', () => {
                const target = document.querySelector(`input[name='${fieldName}']`);
                if (! target) {
                    warn('Cannot find target field, no additional listeners were attached.');
                    return;
                }

                target.addEventListener('input', listener);
                this.callbacks.push({ name: 'input', listener, el: target });
            });
        }
    }

    /**
     * Determines a suitable listener for the element.
     */
    _getSuitableListener() {
        let listener;

        // determine the suitable listener and events to handle
        switch (this.el.type) {
            case 'file':
                listener = {
                    names: ['change'],
                    listener: this._fileListener
                };
                break;

            case 'radio':
                listener = {
                    names: ['change'],
                    listener: this._radioListener
                };
                break;

            case 'checkbox':
                listener = {
                    names: ['change'],
                    listener: this._checkboxListener
                };
                break;
        
            default:
                listener = {
                    names: ['input', 'blur'],
                    listener: this._inputListener
                };
                break;
        }

        // users are able to specify which events they want to validate on
        // pipe separated list of handler names to use
        if (this.el.dataset.validateOn) {
            listener.names = this.el.dataset.validateOn.split('|');
        } 

        return listener;
    }

    /**
     * Attachs a suitable listener for the input.
     */
    _attachFieldListeners() {
        const handler = this._getSuitableListener();
        const listener = debounce(
            handler.listener.bind(this),<commit_msg>DRY calls to validator<commit_after>import { getScope, debounce, warn } from './utils/helpers';

const DEFAULT_EVENT_NAME = 'veeValidate';

export default class ListenerGenerator
{
    constructor(el, binding, context, options) {
        this.callbacks = [];
        this.el = el;
        this.binding = binding;
        this.vm = context;
        this.options = options;
        this.fieldName = binding.expression || el.name;
    }

    /**
     * Determines if the validation rule requires additional listeners on target fields.
     */
    _hasFieldDependency(rules) {
        const results = rules.split('|').filter(r => !! r.match(/\b(confirmed|after|before):/));
        if (! results.length) {
            return false;
        }

        return results[0].split(':')[1];
    }

    /**
     * Validates input value, triggered by 'input' event.
     */
    _inputListener() {
        this._validate(this.el.value);
    }

    /**
     * Validates files, triggered by 'change' event.
     */
    _fileListener() {
        const isValid = this._validate(this.el.files);

        if (! isValid && this.binding.modifiers.reject) {
            // eslint-disable-next-line
            el.value = '';
        }
    }

    /**
     * Validates radio buttons, triggered by 'change' event.
     */
    _radioListener() {
        const checked = document.querySelector(`input[name="${this.el.name}"]:checked`);
        this._validate(checked ? checked.value : null);
    }

    /**
     * Validates checkboxes, triggered by change event.
     */
    _checkboxListener() {
        const checkedBoxes = document.querySelectorAll(`input[name="${this.el.name}"]:checked`);
        if (! checkedBoxes || ! checkedBoxes.length) {
            this._validate(null);
            return;
        }

        [...checkedBoxes].forEach(box => {
            this._validate(box.value);
        });
    }

    /**
     * Trigger the validation for a specific value.
     */
    _validate(value) {
        return this.vm.$validator.validate(this.fieldName, value, getScope(this.el));
    }

    /**
     * Returns a scoped callback, only runs if the el scope is the same as the recieved scope
     * From the event.
     */
    _getScopedListener(callback) {
        return (scope) => {
            if (! scope || scope === getScope(this.el) || scope instanceof Event) {
                callback();
            }
        };
    }

    /**
     * Attaches validator event-triggered validation.
     */
    _attachValidatorEvent() {
        const listener = this._getScopedListener(this._getSuitableListener().listener.bind(this));

        this.vm.$on(DEFAULT_EVENT_NAME, listener);
        this.callbacks.push({ name: DEFAULT_EVENT_NAME, listener });
        this.vm.$on('VALIDATOR_OFF', (field) => {
            if (this.fieldName === field) {
                this.detach();
            }
        });

        const fieldName = this._hasFieldDependency(this.el.dataset.rules);
        if (fieldName) {
            // Wait for the validator ready triggered when vm is mounted because maybe
            // the element isn't mounted yet.
            this.vm.$once('validatorReady', () => {
                const target = document.querySelector(`input[name='${fieldName}']`);
                if (! target) {
                    warn('Cannot find target field, no additional listeners were attached.');
                    return;
                }

                target.addEventListener('input', listener);
                this.callbacks.push({ name: 'input', listener, el: target });
            });
        }
    }

    /**
     * Determines a suitable listener for the element.
     */
    _getSuitableListener() {
        let listener;

        // determine the suitable listener and events to handle
        switch (this.el.type) {
            case 'file':
                listener = {
                    names: ['change'],
                    listener: this._fileListener
                };
                break;

            case 'radio':
                listener = {
                    names: ['change'],
                    listener: this._radioListener
                };
                break;

            case 'checkbox':
                listener = {
                    names: ['change'],
                    listener: this._checkboxListener
                };
                break;

            default:
                listener = {
                    names: ['input', 'blur'],
                    listener: this._inputListener
                };
                break;
        }

        // users are able to specify which events they want to validate on
        // pipe separated list of handler names to use
        if (this.el.dataset.validateOn) {
            listener.names = this.el.dataset.validateOn.split('|');
        }

        return listener;
    }

    /**
     * Attachs a suitable listener for the input.
     */
    _attachFieldListeners() {
        const handler = this._getSuitableListener();
        const listener = debounce(
            handler.listener.bind(this),
{'fraction_non_alphanumeric': 0.12070960277670652, 'fraction_numerical': 0.0003856536829926726, 'mean_word_length': 1.648200153178453, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, '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': '6767279', 'n_tokens_mistral': 2724, 'n_tokens_neox': 2633, 'n_words': 621}