import Cascade from './Cascade'; import Observable from '../graph/Observable'; import ObservableArray from '../graph/ObservableArray'; import ObservableHash from '../graph/ObservableHash'; import Computed from '../graph/Computed'; import { IHash } from '../graph/IObservable'; function createObservableIfNotExists(obj: any, property: string, value?: T, set?: boolean): Observable { Cascade.attachGraph(obj); if (!obj._graph.observables[property]) { obj._graph.observables[property] = new Observable(value); } else if (set) { obj._graph.observables[property].setValue(value); } return obj._graph.observables[property]; } function createArrayIfNotExists(obj: any, property: string, value?: Array, set?: boolean): ObservableArray { Cascade.attachGraph(obj); if (!obj._graph.observables[property]) { obj._graph.observables[property] = new ObservableArray(value); } else if (set) { obj._graph.observables[property].setValue(value); } return obj._graph.observables[property]; } function createHashIfNotExists(obj: any, property: string, value?: IHash, set?: boolean): ObservableHash { Cascade.attachGraph(obj); if (!obj._graph.observables[property]) { obj._graph.observables[property] = new ObservableHash(value); } else if (set) { obj._graph.observables[property].setValue(value); } return obj._graph.observables[property]; } function createComputedIfNotExists(obj: any, property: string, definition: (n?: T) => T, defer?: boolean, thisArg?: any, setter?: (n?: T) => any): Computed { Cascade.attachGraph(obj); if (!obj._graph.observables[property]) { obj._graph.observables[property] = new Computed(definition, defer, thisArg, setter); } return obj._graph.observables[property]; } function attachObservable(target: any, propertyKey: string) { Object.defineProperty(target, propertyKey, { enumerable: true, configurable: true, get: function () { return createObservableIfNotExists(this, propertyKey).getValue(); }, set: function (value: T) { createObservableIfNotExists(this, propertyKey, value, true); } }); } export function array(target: any, propertyKey: string): any { Object.defineProperty(target, propertyKey, { enumerable: true, configurable: true, get: function () { return createArrayIfNotExists(this, propertyKey).getValue(); }, set: function (value: Array) { createArrayIfNotExists(this, propertyKey, value, true); } }); } export function hash(target: any, propertyKey: string): any { Object.defineProperty(target, propertyKey, { enumerable: true, configurable: true, get: function () { return createHashIfNotExists(this, propertyKey).getValue(); }, set: function (value: IHash) { createHashIfNotExists(this, propertyKey, value, true); } }); } function attachComputed(target: any, propertyKey: string, descriptor?: TypedPropertyDescriptor, definition?: (n?: T) => T, setter?: (n: T) => any) { descriptor = descriptor || {}; definition = definition || descriptor.get; descriptor.enumerable = true; descriptor.get = function () { return createComputedIfNotExists(this, propertyKey, definition, false, this, setter).getValue(); } return descriptor; } // TODO: Remove Reflect check export function observable(target: any, propertyKey: string, descriptor?: TypedPropertyDescriptor): any { if (descriptor) { attachComputed(target, propertyKey, descriptor); } else { // Only use Reflection if it exists. if (Cascade.reflectAvailable) { var type = Reflect.getMetadata("design:type", target, propertyKey); } if (type === Array) { array(target, propertyKey); } else { attachObservable(target, propertyKey); } } } export function computed(definition: (n?: T) => T, setter?: (n: T) => any) { return function (target: any, propertyKey: string, descriptor?: TypedPropertyDescriptor): any { return attachComputed(target, propertyKey, descriptor, definition, setter); } }