API Docs for:
Show:

File: development/modules/__base/dommodule.js

import $ from 'jquery';
import { isVoid, isString, isInteger, isObject,
         isNode, isFunction, isSymbol } from '../../js/utils/type';
import {eventType} from '../../js/utils/events';
import BaseModule from './basemodule';

/**
 * @module Modules
 */


const _attach = Symbol('_attachHandler');
const _detach = Symbol('_detachHandler');


/**
 * A basic module to build other modules upon. The constructor either awaits a
 *     single DOMNode or a configuration object which must have a "container"
 *     property
 * @class Modules._DOMModule
 * @constructor
 * @extends Modules._BaseModule
 */


class DOMModule extends BaseModule {
    constructor(config) {
        super();

        this.$win = $(window);
        this.$doc = $(document);
        this.$head = $(document.head);
        this.$body = $(document.body);

        if (isObject(config)) {
            this.$rootNode = $(config.container);
        } else if (isNode(config) || isString(config)) {
            this.$rootNode = $(config);
        } else {
            throw `DOMModule: Unable to create $rootNode from ${config}`;
        }
    }
    /**
     * Destroys the instance, releases all attached eventlisteners and
     *     removes all observers and states. Also removes the associated
     *     HTMLElement from the DOM
     *
     * @method destroy
     * @for Modules._DOMModule
     * @return {undefined} Nothing
     */
    destroy() {
        super.destroy();
        this.$win.off(eventType.RESIZE);
        this.$rootNode.off(eventType.CLICK);
        this.$rootNode.off(eventType.TRANSITION);
        this.$rootNode.off(eventType.INPUT);
        this.$rootNode.off(eventType.CHANGE);
        this.$rootNode.off(eventType.FOCUSIN);
        this.$rootNode.off(eventType.FOCUSOUT);
        this.$rootNode.off(eventType.SUBMIT);
        this.$rootNode.remove();

        this.$rootNode = null;
        this.$win = null;
    }
    
    // === TRAVERSAL ===
    /**
     * Takes a CSS style selector and returns all elements under the root node
     *     of the widget which match the selector as jQuery collection
     *
     * @method $findChilds
     * @for Modules._DOMModule
     * @param {string} selector Selector to match elements against
     * @return {jQuery} A jQuery collection of matched elements
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.$buttons = this.$findChilds('button');
     *         }
     *     }
     */
    $findChilds(selector) {
        return this.$rootNode.find(selector);
    }
    /**
     * Takes a CSS style selector and returns the closes parent element of
     *     the root element of the widget that matches the selector. If no
     *     selector is specified, returns the immediate parent element of the
     *     root element. 
     *
     * @method $findParent
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @return {jQuery} A jQuery collection of matched elements
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.$rootContainer = this.$findParent('div');
     *         }
     *     }
     */
    $findParent(selector) {
        return !selector ? this.$rootNode.parent() : this.$rootNode.closest(selector);
    }
    /**
     * Takes a key and returns either the value of a matching data- attribute
     *     or null if no data- attribute is set. Works only on the $root element 
     *
     * @method $data
     * @for Modules._DOMModule
     * @param {string} key Key of the data- attribute to get
     * @return {any|null} The value or null
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             const urlVal = this.$data('url') || '/default/url';
     *         }
     *     }
     */
    $data(key) {
        return isString(key) ? this.$rootNode.data(key) || null : null;
    }
    /**
     * Takes an zero based index and returns the raw DOMNode from the underlying
     *     jQuery collection on the given index. If the index is higher than the
     *     length of the collection, returns the last element of the collection.
     *     If it is omitted, returns the first element of the collection.
     *     Works only on the $root element 
     *
     * @method $raw
     * @for Modules._DOMModule
     * @param {integer|any} index Index of the raw DOMNode you want to get
     * @return {DOMNode} The node on the given position
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     let items = new DOMModule('li'); // get all <li> elements as $rootNode
     *     
     *     items.$raw(); // -> first <li>, equivalent to items.$raw(0)
     *     items.$raw(4); // -> fourth or last <li>, depending on number of elements
     */
    $raw(i) {
        // max/min/abs stuff in here ensures we cannot get past the length
        // of the entire jQuery collection, regardless of which integer is given
        return this.$rootNode.get(!isInteger(i) ? 0 : Math.max(0, Math.min(
            this.$rootNode.length - 1,
            Math.abs(i)
        )));
    }
    // === EVENTS ===
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when either the 
     *     mousedown/touchstart/pointerdown event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onPress
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onPress('button', 'pressHandle');
     *         }
     *         pressHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onPress(selector, handler) {
        return this[_attach](eventType.DOWN, selector, handler);
    }
    /**
     * Opposite of the onPress method
     *
     * @method offPress
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offPress(selector, handler) {
        return this[_detach](eventType.DOWN, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when either the 
     *     mousemove/touchmove/pointermove event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onMove
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onMove('.movearea', 'moveHandle');
     *         }
     *         moveHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onMove(selector, handler) {
        return this[_attach](eventType.Move, selector, handler);
    }
    /**
     * Opposite of the onMove method
     *
     * @method offMove
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offMove(selector, handler) {
        return this[_detach](eventType.Move, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when either the 
     *     mouseup/touchend/pointerup event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onRelease
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onRelease('button', 'releaseHandle');
     *         }
     *         releaseHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onRelease(selector, handler) {
        return this[_attach](eventType.UP, selector, handler);
    }
    /**
     * Opposite of the onRelease method
     *
     * @method offRelease
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offRelease(selector, handler) {
        return this[_detach](eventType.UP, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when the click event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onClick
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onClick('button', 'clickHandle');
     *         }
     *         clickHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onClick(selector, handler) {
        return this[_attach](eventType.CLICK, selector, handler);
    }
    /**
     * Opposite of the onClick method
     *
     * @method offClick
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offClick(selector, handler) {
        return this[_detach](eventType.CLICK, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when the change event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onChange
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onChange('button', 'changeHandle');
     *         }
     *         changeHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onChange(selector, handler) {
        return this[_attach](eventType.CHANGE, selector, handler);
    }
    /**
     * Opposite of the onChange method
     *
     * @method offChange
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offChange(selector, handler) {
        return this[_detach](eventType.CHANGE, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when the focus event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onFocus
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onFocus('input[name=MyInput]', 'focusHandle');
     *         }
     *         focusHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onFocus(selector, handler) {
        return this[_attach](eventType.FOCUSIN, selector, handler);
    }
    /**
     * Opposite of the onFocus method
     *
     * @method offFocus
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offFocus(selector, handler) {
        return this[_detach](eventType.FOCUSIN, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when the blur event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onBlur
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onBlur('input[name=MyInput]', 'blurHandle');
     *         }
     *         blurHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onBlur(selector, handler) {
        return this[_attach](eventType.FOCUSOUT, selector, handler);
    }
    /**
     * Opposite of the onBlur method
     *
     * @method offBlur
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offBlur(selector, handler) {
        return this[_detach](eventType.FOCUSOUT, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when either the 
     *     keyup/input event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onInput
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onInput('input[name=MyInput]', 'inputHandle');
     *         }
     *         inputHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onInput(selector, handler) {
        return this[_attach](eventType.INPUT, selector, handler);
    }
    /**
     * Opposite of the onInput method
     *
     * @method offInput
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offInput(selector, handler) {
        return this[_detach](eventType.INPUT, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when either the 
     *     transitionend/webkitTransitionEnd/mozTransitionEnd/msTransitionEnd/
     *     oTransitionEnd event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onTransitionEnd
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onTransitionEnd('.effectarea', 'transitionEndHandle');
     *         }
     *         transitionEndHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onTransitionEnd(selector, handler) {
        return this[_attach](eventType.TRANSITION, selector, handler);
    }
    /**
     * Opposite of the onTransitionEnd method
     *
     * @method offTransitionEnd
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offTransitionEnd(selector, handler) {
        return this[_detach](eventType.TRANSITION, selector, handler);
    }
    /**
     * Takes a CSS style selector and a function or methodname and registers 
     *     the function or method as eventhandler for all child elements of
     *     the root element which match the selector when submit event occurs.
     *     If the selector is omitted and only a function/methodname is given,
     *     registers the handler on the root element.
     *
     * @method onSubmit
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onSubmit('.my-form', 'submitHandler');
     *         }
     *         submitHandler(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onSubmit(selector, handler) {
        return this[_attach](eventType.SUBMIT, selector, handler);
    }
    /**
     * Opposite of the onSubmit method
     *
     * @method offSubmit
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} handler Eventhandler function or method name
     * @return {this} The instance
     */
    offSubmit(selector, handler) {
        return this[_detach](eventType.SUBMIT, selector, handler);
    }
    /**
     * Takes function or methodname and registers the function or method as
     *     eventhandler on the window object.
     *
     * @method onResize
     * @for Modules._DOMModule
     * @param {string|function} fn Eventhandler function or method name
     * @return {this} The instance
     *
     * @example
     *     import DOMModule from './modules/__base/dommodule';
     *
     *     class MyClass extends DOMModule {
     *         render() {
     *             this.onResize('resizeHandle');
     *         }
     *         resizeHandle(event) {
     *             // ... your code ...
     *         }
     *     }
     */
    onResize(fn) {
        if (isFunction(fn)) {
            this.$win.on(eventType.RESIZE, fn);
            return this;
        }

        if ((isString(fn) || isSymbol(fn)) && isFunction(this[fn])) {
            this.$win.on(eventType.RESIZE, this[fn].bind(this));
        }
        return this;
    }
    /**
     * Opposite of the onResize method
     *
     * @method offResize
     * @for Modules._DOMModule
     * @param {string} [selector] Selector to match elements against
     * @param {string|function} fn Eventhandler function or method name
     * @return {this} The instance
     */
    offResize(fn) {
        if (isFunction(fn)) {
            this.$win.off(eventType.RESIZE, fn);
            return this;
        }

        if ((isString(fn) || isSymbol(fn)) && isFunction(this[fn])) {
            this.$win.off(eventType.RESIZE, this[fn].bind(this));
            return this;
        }

        this.$win.off(eventType.RESIZE);
        return this;
    }
    // === PRIVATE STUFF ===
    [_attach](type, selector, fn) {
        if (isVoid(fn)) {
            if (isFunction(selector)) {
                this.$rootNode.on(type, selector);
                return this;
            }
            if ((isString(selector) || isSymbol(selector)) && isFunction(this[selector])) {
                this.$rootNode.on(type, this[selector].bind(this));
            }
            return this;
        }

        if (isFunction(fn)) {
            this.$rootNode.on(type, selector, fn);
            return this;
        } 
        if ((isString(fn) || isSymbol(fn)) && isFunction(this[fn])) {
            this.$rootNode.on(type, selector, this[fn].bind(this));
        }

        return this;
    }
    [_detach](type, selector, fn) {
        if (isVoid(fn)) {
            if (isFunction(selector)) {
                this.$rootNode.off(type, selector);
                return this;
            }
            if ((isString(selector) || isSymbol(selector)) && isFunction(this[selector])) {
                this.$rootNode.off(type, this[selector].bind(this));
            }
            return this;
        }

        if (isFunction(fn)) {
            this.$rootNode.off(type, selector, fn);
            return this;
        } 
        if ((isString(fn) || isSymbol(fn)) && isFunction(this[fn])) {
            this.$rootNode.off(type, selector, this[fn].bind(this));
        }

        return this;
    }
}



export default DOMModule;