API Docs for:
Show:

File: development/js/utils/observer.js

import {isFunction} from './type';
import objects from './object';

/**
 * Provides a observable implementation
 *
 * @module Utils
 * @class Utils.Observable
 * @static
 */



const POINTER = Symbol('__observerInstance');
const HANDLER = Symbol('__observerFunction');


class Observer {
    constructor(pointer, handler) {
        this[POINTER] = pointer;
        this[HANDLER] = handler;
    }
    static of(pointer, handler) {
        return new Observer(pointer, handler);
    }
    matches(x, y) {
        return x === this[POINTER] && y === this[HANDLER];
    }
    update(data) {
        this[POINTER][this[HANDLER]](...data);
    }
    destroy() {
        this[POINTER] = null;
        this[HANDLER] = null;
        return true;
    }
}

/**
 * Creates a observable object with isObserver(), hasObserver(), addObserver(),
 *     removeObserver() and notifyObservers() methods
 *
 * @method Observable
 * @for Utils.Observable
 * @return {object} Observable
 *
 * @example
 *     import Observable from './utils/observer';
 *     
 *     var foo = Observable();
 *
 *     var observer = {
 *         update: function (data) {
 *             console.log(data);
 *         }
 *     };
 *
 *     foo.addObserver(observer); // -> foo
 *
 *     foo.notifyObservers(1); // -> foo
 *     // logs 1 into the console
 *
 *     foo.hasObserver(observer); // -> true
 *
 *     foo.removeObserver(observer); // -> foo
 *
 *
 *     // === ALTERNATIVE ===
 *
 *     var bar = Observable.of({});
 *     bar.addObserver(observer);
 *
 *     bar.notifyObservers(1);
 *     
 *
 * @example
 *     import Observable from './utils/observer';
 *     
 *     var foo = Observable();
 *
 *     var observer = {
 *         myMethod: function (data) {
 *             console.log(data);
 *         }
 *     };
 *
 *     foo.addObserver(observer, 'myMethod');
 *
 *     foo.notifyObservers(1);
 *     // -> logs 1 into the console
 */
const Observable = () => {
    let observers = [];
    return {
        isObserver(it) {
            return Observer.prototype.isPrototypeOf(it) ||
                   !!it && isFunction(it.update);

        },
        hasObserver(it, method = 'update') {
            return observers.some((x) => x.matches(it, method));
        },
        addObserver(it, method = 'update') {
            if (!!it && isFunction(it[method]) && !this.hasObserver(it, method)) {
                observers.push(Observer.of(it, method));
            }
            return this;
        },
        removeObserver(it, method = 'update') {
            observers = observers.filter((x) => !x.matches(it, method));
            return this;
        },
        notifyObservers(...data) {
            observers.forEach((x) => x.update(data));
            return this;
        },
        destroyObservers() {
            observers = observers.reduce((a, o) => o.destroy() && a, []);
        }
    };
}

Observable.of = function (target) {
    return objects.forward(target, Observable(), [
        'hasObserver',
        'isObserver',
        'addObserver',
        'removeObserver',
        'notifyObservers'
    ]);
}



export default Observable;