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;