/**
 * Copyright (c) 2016-2018 Dmitry Panyushkin
 * Available under MIT license
 * Version: 2.0.0
 */ import * as ko from "knockout" ; import { defineProperty , extendObject , getOwnPropertyDescriptor , hasOwnProperty , isArray , PATCHED_KEY , SUBSCRIPTIONS_KEY , } from "./common-functions" ; import { defineEventProperty } from "./event-property" ; import { defineObservableArray } from "./observable-array" ; import { defineObservableProperty } from "./observable-property" ; import { applyExtenders , defineExtenders } from "./property-extenders" ; /**
 * Property decorator that creates hidden (shallow or deep) ko.observable with ES6 getter and setter for it
 * If initialized by Array then hidden ko.observableArray will be created
 */ export function observable ( options ) ; /**
 * Property decorator that creates hidden (shallow) ko.observable with ES6 getter and setter for it
 * If initialized by Array then hidden (shallow) ko.observableArray will be created
 */ export function observable ( prototype , key ) ; /**
 * Property decorator that creates hidden (shallow or deep) ko.observable with ES6 getter and setter for it
 * If initialized by Array then hidden (shallow or deep) ko.observableArray will be created
 */ export function observable ( prototypeOrOptions , key ? ) { observableArrayOption = false ; deepObservableOption = false ; exposeObservableOption = false ; if ( arguments . length === 1 ) { deepObservableOption = prototypeOrOptions . deep ; exposeObservableOption = prototypeOrOptions . expose ; return observableDecorator ; } return observableDecorator ( prototypeOrOptions , key ) ; } /**
 * Property decorator that creates hidden (shallow or deep) ko.observableArray with ES6 getter and setter for it
 */ export function observableArray ( options ) ; /**
 * Property decorator that creates hidden (shallow) ko.observableArray with ES6 getter and setter for it
 */ export function observableArray ( prototype , key ) ; /**
 * Property decorator that creates hidden (shallow or deep) ko.observableArray with ES6 getter and setter for it
 */ export function observableArray ( prototypeOrOptions , key ? ) { observableArrayOption = true ; deepObservableOption = false ; exposeObservableOption = false ; if ( arguments . length === 1 ) { deepObservableOption = prototypeOrOptions . deep ; exposeObservableOption = prototypeOrOptions . expose ; return observableDecorator ; } return observableDecorator ( prototypeOrOptions , key ) ; } let observableArrayOption ; let deepObservableOption ; let exposeObservableOption ; function observableDecorator ( prototype , propKey ) { const array = observableArrayOption ; const deep = deepObservableOption ; const expose = exposeObservableOption ; defineProperty ( prototype , propKey , { get ( ) { throw new Error ( "@observable property '" + String ( propKey ) + "' was not initialized" ) ; } , set ( this , value ) { if ( array || isArray ( value ) ) { defineObservableArray ( this , propKey , value , deep , expose ) ; } else { defineObservableProperty ( this , propKey , value , deep , expose ) ; } } , } ) ; } export interface ObservableArray < T > extends Array < T > { replace ( oldItem , newItem ) : void ; remove ( item ) : T [ ] ; remove ( removeFunction ) : T [ ] ; removeAll ( ) : T [ ] ; removeAll ( items ) : T [ ] ; destroy ( item ) : void ; destroy ( destroyFunction ) : void ; destroyAll ( ) : void ; destroyAll ( items ) : void ; subscribe ( callback ) : ko . Subscription ; subscribe ( callback , callbackTarget ) : ko . Subscription ; subscribe ( callback , callbackTarget , event ) : ko . Subscription ; /**
     * Run mutator function that can write to array at some index (`array[index] = value;`)
     * Then notify about observableArray changes
     */ mutate ( mutator ) : void ; /**
     * Replace value at some index and return old value
     */ set ( index , value ) : T ; } /**
 * Accessor decorator that wraps ES6 getter to hidden ko.computed or ko.pureComputed
 *
 * Setter is not wrapped to hidden ko.pureComputed and stays unchanged
 *
 * But we can still extend getter @computed by extenders like { rateLimit: 500 }
 */ export function computed ( options ) ; /**
 * Accessor decorator that wraps ES6 getter to hidden ko.pureComputed
 *
 * Setter is not wrapped to hidden ko.pureComputed and stays unchanged
 *
 * But we can still extend getter @computed by extenders like { rateLimit: 500 }
 */ export function computed ( prototype , key , desc ? ) ; /**
 * Accessor decorator that wraps ES6 getter to hidden ko.computed or ko.pureComputed
 *
 * Setter is not wrapped to hidden ko.pureComputed and stays unchanged
 *
 * But we can still extend getter @computed by extenders like { rateLimit: 500 }
 */ export function computed ( prototypeOrOptinos , key ? , propDesc ? ) { computedDecoratorOptions = { pure : true } ; if ( arguments . length === 1 ) { computedDecoratorOptions = prototypeOrOptinos ; return computedDecorator ; } return computedDecorator ( prototypeOrOptinos , key , propDesc ) ; } let computedDecoratorOptions ; function computedDecorator ( prototype , propKey , desc ) { const options = computedDecoratorOptions ; const { get , set } = desc || ( desc = getOwnPropertyDescriptor ( prototype , propKey ) ) ; if ( ! get ) { throw new Error ( "@computed property '" + String ( propKey ) + "' has no getter" ) ; } desc . get = function ( this ) { const koComputed = applyExtenders ( this , propKey , ko . computed ( get , this , options ) ) ; defineProperty ( this , propKey , { get : koComputed , set : set , } ) ; return koComputed ( ) ; } ; return desc ; } /**
 * Apply extenders to decorated @observable
 */ export function extend ( extenders ) ; /**
 * Apply extenders to decorated @observable
 */ export function extend ( extendersFactory ) ; extendersOrFactory export function extend ( extendersOrFactory ) { return function ( prototype , key ) { defineExtenders ( prototype , key , extendersOrFactory ) ; } ; } export type ComponentConstructor = new ( params ? , element ? , templateNodes ? ) => any ; export type ComponentDecorator = ( constructor : ComponentConstructor ) => void ; export type TemplateConfig = ( string | Node [ ] | DocumentFragment | { require : string ; } | { element : string | Node ; } ) ; /**
 * Register Knockout component by decorating ViewModel class
 */ export function component ( name , options ? ) ; /**
 * Register Knockout component by decorating ViewModel class
 */ export function component ( name , template , options ? ) ; /**
 * Register Knockout component by decorating ViewModel class
 */ export function component ( name , template , styles , options ? ) ; param name { String } param template { Any } param styles { Any } param options { Object } export function component ( name , template ? , styles ? , options ? ) { if ( options === void 0 ) { if ( styles === void 0 ) { if ( typeof template === "object" && template . constructor === Object && ! ( "require" in template ) && ! ( "element" in template ) ) { options = template ; template = void 0 ; } } else if ( typeof styles === "object" ) { options = styles ; styles = void 0 ; } } return function ( constructor ) { ko . components . register ( name , extendObject ( { viewModel : constructor . length < 2 ? constructor : { createViewModel ( params , { element , templateNodes , } ) { return new constructor ( params , element , templateNodes ) ; } , } , template : template || "<!---->" , synchronous : true , } , options as Object ) ) ; } ; } /**
 * Like https://github.com/jayphelps/core-decorators.js @autobind but less smart and complex
 * Do NOT use with ES6 inheritance!
 */ export function autobind ( prototype , key , desc ? ) { const { value , configurable , enumerable } = desc || ( desc = getOwnPropertyDescriptor ( prototype , key ) ) ; return { configurable : configurable , enumerable : enumerable , get ( this ) { if ( this === prototype ) { return value ; } const bound = value . bind ( this ) ; defineProperty ( this , key , { value : bound , } ) ; return bound ; } , } as PropertyDescriptor ; } /**
 * Define hidden ko.subscribable, that notifies subscribers when decorated method is invoked
 */ export function event ( prototype , key ) { defineProperty ( prototype , key , { get ( this ) { return defineEventProperty ( this , key ) ; } , } ) ; } export type EventType = Function & { subscribe ( callback ) : ko . Subscription ; } ; /**
 * Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event` property
 */ export function subscribe < T > ( dependencyOrEvent , callback , options ? ) ; /**
 * Subscribe callback to `@observableArray` dependency "arrayChange" event
 */ export function subscribe < T > ( dependency , callback , options ) ; /**
 * Subscribe callback to some `@event` property
 */ export function subscribe < T > ( event , callback , options ? ) ; /**
 * Subscribe callback to some `@event` property
 */ export function subscribe < T1 , T2 > ( event , callback , options ? ) ; /**
 * Subscribe callback to some `@event` property
 */ export function subscribe < T1 , T2 , T3 > ( event , callback , options ? ) ; /**
 * Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event`
 */ export function subscribe ( dependencyOrEvent , callback , options ? ) { const once = options && options . once || false ; if ( hasOwnProperty ( dependencyOrEvent , "subscribe" ) ) { const eventFunc = dependencyOrEvent as EventType ; if ( once ) { const subscription = eventFunc . subscribe ( function ( ) { subscription . dispose ( ) ; callback . apply ( null , arguments ) ; } ) ; return subscription ; } else { return eventFunc . subscribe ( callback ) ; } } else { const eventFunc = options && options . event || "change" ; let handler ; let subscription ; if ( once ) { handler = function ( ) { subscription . dispose ( ) ; callback . apply ( null , arguments ) ; } ; } else { handler = callback ; } if ( eventFunc === "arrayChange" ) { const obsArray = dependencyOrEvent ( ) as ObservableArray < any > ; if ( isArray ( obsArray ) && hasOwnProperty ( obsArray , PATCHED_KEY ) ) { subscription = obsArray . subscribe ( handler , null , eventFunc ) ; } else { throw new Error ( "Can not subscribe to 'arrayChange' because dependency is not an 'observableArray'" ) ; } } else { const koComputed = ko . computed ( dependencyOrEvent as ( ) => any ) . extend ( { notify : "always" } ) ; subscription = koComputed . subscribe ( handler , null , eventFunc ) ; const originalDispose = subscription . dispose ; subscription . dispose = function ( this ) { originalDispose . call ( this ) ; koComputed . dispose ( ) ; } ; } return subscription ; } } /**
 * Get internal ko.observable() for object property decodated by @observable
 */ export function unwrap ( instance , key ) ; /**
 * Get internal ko.observable() for object property decodated by @observable
 */ export function unwrap < T > ( instance , key ) ; /**
 * Get internal ko.observable() for object property decodated by @observable
 */ export function unwrap ( instance , key ) { if ( ! hasOwnProperty ( instance , key ) ) { instance [ key ] ; } return getOwnPropertyDescriptor ( instance , key ) . get ; } /**
 * Mixin which add `subscribe()` instance method and implement `dispose()` method,
 * that disposes all subscription created by `subscribe()`
 */ export interface Disposable { /** Dispose all subscriptions from this class */ dispose ( ) : void ; /** Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event` property */ subscribe < T > ( dependencyOrEvent , callback , options ? ) : ko . Subscription ; /** Subscribe callback to `@observableArray` dependency "arrayChange" event */ subscribe < T > ( dependency , callback , options ) : ko . Subscription ; /** Subscribe callback to some `@event` property */ subscribe < T > ( event , callback , options ? ) : ko . Subscription ; /** Subscribe callback to some `@event` property */ subscribe < T1 , T2 > ( event , callback , options ? ) : ko . Subscription ; /** Subscribe callback to some `@event` property */ subscribe < T1 , T2 , T3 > ( event , callback , options ? ) : ko . Subscription ; /** Get internal ko.observable() for class property decodated by `@observable` */ unwrap ( key ) : any ; /** Get internal ko.observable() for class property decodated by `@observable` */ unwrap < T > ( key ) : ko . Observable < T > ; } /**
 * Mixin which add `subscribe()` instance method and implement `dispose()` method,
 * that disposes all subscription created by `subscribe()`
 */ export function Disposable ( ) ; param Base { Function } export function Disposable < T extends Function > ( Base ) ; param Base { Function } export function Disposable < T extends new ( ... args ) => any > ( Base ? ) { if ( typeof Base === "undefined" ) { Base = class { } as T ; } return class extends Base { /** Dispose all subscriptions from this class */ dispose ( ) { const subscriptions = this [ SUBSCRIPTIONS_KEY as any ] ; if ( subscriptions ) { subscriptions . forEach ( ( subscription ) => { subscription . dispose ( ) ; } ) ; delete this [ SUBSCRIPTIONS_KEY as any ] ; } } /** Subscribe callback to `@observable` or `@computed` dependency changes or to some `@event` */ subscribe ( ) { const subscription = subscribe . apply ( null , arguments ) ; const subscriptions = this [ SUBSCRIPTIONS_KEY as any ] || ( this [ SUBSCRIPTIONS_KEY as any ] = [ ] ) ; subscriptions . push ( subscription ) ; return subscription ; } /** Get internal ko.observable() for class property decodated by `@observable` */ unwrap ( key ) { return unwrap ( this , key ) ; } } ; }