<commit_before>import { define } from '../lib/obj-define.js';
import { reduceObjectProperty } from '../lib/reduce.js';
import { isString, isFunction } from '../lib/typeof.js';
import { matches } from '../lib/matches.js';
import { dispatch } from '../lib/dispatch.js';

/**
 * Check if an event has a selector in mathc in target list.
 * @private
 *
 * @param {Event} event The event to check.
 * @param {HTMLElement} node The root node.
 * @param {String} selector The CSS selector to match.
 * @return {HTMLElement} The element in target list which matches the selector.
 */
function checkDelegate(event, node, selector) {
    if (!selector) {
        return node;
    }
    let target = event.target;
    while (target && target !== node) {
        if (matches(target, selector)) {
            return target;
        }
        target = target.parentNode;
    }
    return null;
}
/**
 * Generate a delegate listener for an event.
 * @private
 *
 * @param {String} selector The CSS selector to match.
 * @param {Function} callback The original callback for the event.
 * @return {Function} Wrapped callback with a delegation check.
 */
function delegateCallback(selector, callback) {
    return (ev) => {
        let target = checkDelegate(ev, this.node, selector);
        if (target) {
            callback.call(this, ev, target);
        }
    };
}

/**
 * Simple Custom Component with events delegation,
 * It also implement a `dispatchEvent` wrapper named `trigger`.
 * @mixin EventsMixin
 * @memberof DNA.MIXINS.
 * @static
 *
 * @example
 * ```js
 * // my-component.js
 * import { BaseComponent } from '@dnajs/core';
 * export class MyComponent extends BaseComponent {
 *   get events() {
 *     return {
 *       'click button': 'onButtonClick'
 *     }
 *   }
 *   onButtonClick() {
 *     console.log('button clicked');
 *   }
 * }
 * ```
 * ```js
 * // app.js
 * import { define } from '@dnajs/core';
 * import { MyComponent } from './my-component.js';
 * define('my-component', MyComponent);
 * var element = new MyComponent();
 * var button = document.createElement('button');
 * button.innerText = 'Click me';
 * element.appendChild(button);
 * button.click(); // logs "button clicked"
 * ```
 */
export const EventsMixin = (SuperClass) => class extends SuperClass {
    /**
     * Iterate `events` properties.
     * @method constructor
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    constructor(node) {
        super(node);
        let events = reduceObjectProperty(this, 'events');
        for (let k in events) {
            let callback = isString(events[k]) ?
                this[events[k]] :
                events[k];
            if (isFunction(callback)) {
                let selector = k.split(' ').slice(1).join(' ').trim();
                events[k] = delegateCallback.call(this, selector, callback);
            } else {
                throw new TypeError('Invalid callback for event.');
            }
        }
        define(this, 'events', { value: events });
    }
    /**
     * Attach and delegate events to the component.
     * @method connectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    connectedCallback() {
        super.connectedCallback();
        // bind events
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.node.addEventListener(evName, events[k]);
        }
    }
    /**
     * Detach and undelegate events from the component.
     * @method disconnectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    disconnectedCallback() {
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.node.removeEventListener(evName, events[k]);
        }
        super.disconnectedCallback();
    }
    /**
     * Delegate events to the component descendents.
     * @method delegate
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to delegate.
     * @param {String} selector A CSS selector for descendents.
     * @param {Function} callback The callback to fire.
     */
    delegate(evName, selector, callback) {
        let wrapCallback = delegateCallback.call(this, selector, callback);
        this.node.addEventListener(evName, wrapCallback);
    }
    /**
     * `Node.prototype.dispatchEvent` wrapper.
     * @method trigger
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to fire.
     * @param {Object} data A set of custom data to pass to the event.
     * @param {Boolean} bubbles Should the event bubble throw the DOM tree.
     * @param {Boolean} cancelable Can be the event cancel by a callback.
     * @return {Boolean} True if event propagation has not be stopped.<commit_msg>feat: add on and off methods to events mixin prototype<commit_after>import { define } from '../lib/obj-define.js';
import { reduceObjectProperty } from '../lib/reduce.js';
import { isString, isFunction } from '../lib/typeof.js';
import { matches } from '../lib/matches.js';
import { dispatch } from '../lib/dispatch.js';

/**
 * Check if an event has a selector in mathc in target list.
 * @private
 *
 * @param {Event} event The event to check.
 * @param {HTMLElement} node The root node.
 * @param {String} selector The CSS selector to match.
 * @return {HTMLElement} The element in target list which matches the selector.
 */
function checkDelegate(event, node, selector) {
    if (!selector) {
        return node;
    }
    let target = event.target;
    while (target && target !== node) {
        if (matches(target, selector)) {
            return target;
        }
        target = target.parentNode;
    }
    return null;
}
/**
 * Generate a delegate listener for an event.
 * @private
 *
 * @param {String} selector The CSS selector to match.
 * @param {Function} callback The original callback for the event.
 * @return {Function} Wrapped callback with a delegation check.
 */
function delegateCallback(selector, callback) {
    return (ev) => {
        let target = checkDelegate(ev, this.node, selector);
        if (target) {
            callback.call(this, ev, target);
        }
    };
}

/**
 * Simple Custom Component with events delegation,
 * It also implement a `dispatchEvent` wrapper named `trigger`.
 * @mixin EventsMixin
 * @memberof DNA.MIXINS.
 * @static
 *
 * @example
 * ```js
 * // my-component.js
 * import { BaseComponent } from '@dnajs/core';
 * export class MyComponent extends BaseComponent {
 *   get events() {
 *     return {
 *       'click button': 'onButtonClick'
 *     }
 *   }
 *   onButtonClick() {
 *     console.log('button clicked');
 *   }
 * }
 * ```
 * ```js
 * // app.js
 * import { define } from '@dnajs/core';
 * import { MyComponent } from './my-component.js';
 * define('my-component', MyComponent);
 * var element = new MyComponent();
 * var button = document.createElement('button');
 * button.innerText = 'Click me';
 * element.appendChild(button);
 * button.click(); // logs "button clicked"
 * ```
 */
export const EventsMixin = (SuperClass) => class extends SuperClass {
    /**
     * Iterate `events` properties.
     * @method constructor
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    constructor(node) {
        super(node);
        let events = reduceObjectProperty(this, 'events');
        for (let k in events) {
            let callback = isString(events[k]) ?
                this[events[k]] :
                events[k];
            if (isFunction(callback)) {
                let selector = k.split(' ').slice(1).join(' ').trim();
                events[k] = delegateCallback.call(this, selector, callback);
            } else {
                throw new TypeError('Invalid callback for event.');
            }
        }
        define(this, 'events', { value: events });
    }
    /**
     * Attach and delegate events to the component.
     * @method connectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    connectedCallback() {
        super.connectedCallback();
        // bind events
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.on(evName, events[k]);
        }
    }
    /**
     * Detach and undelegate events from the component.
     * @method disconnectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    disconnectedCallback() {
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.off(evName, events[k]);
        }
        super.disconnectedCallback();
    }
    /**
     * Delegate events to the component descendents.
     * @method delegate
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to delegate.
     * @param {String} selector A CSS selector for descendents.
     * @param {Function} callback The callback to fire.
     */
    delegate(evName, selector, callback) {
        let wrapCallback = delegateCallback.call(this, selector, callback);
        return this.on(evName, wrapCallback);
    }
    /**
     * `Node.prototype.addEventListener` wrapper.
     * @method on
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to fire.
     * @param {Function} callback The callback to fire.
     * @param {Object} options Listener options.
     */
    on(evName, callback, options) {
        return this.node.addEventListener(evName, callback, options);
    }
    /**
     * `Node.prototype.removeEventListener` wrapper.
     * @method off
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to unlisten.
     * @param {Function} callback The callback to trim.
     */
    off(evName, callback) {
        return this.node.removeEventListener(evName, callback);
    }
    /**
     * `Node.prototype.dispatchEvent` wrapper.
     * @method trigger
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to fire.
     * @param {Object} data A set of custom data to pass to the event.
     * @param {Boolean} bubbles Should the event bubble throw the DOM tree.
     * @param {Boolean} cancelable Can be the event cancel by a callback.
     * @return {Boolean} True if event propagation has not be stopped.
{'fraction_non_alphanumeric': 0.12540101906020004, 'fraction_numerical': 0.0001887148518588413, 'mean_word_length': 2.392765685019206, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '5233', 'n_tokens_mistral': 2979, 'n_tokens_neox': 2956, 'n_words': 922}