1-
1+ export enum ChangePhase {
2+ Changing ,
3+ Changed
4+ }
5+
6+ export interface ChangeData {
7+ eventName : string ;
8+ sender : Observable ;
9+ phase ?: ChangePhase ;
10+ }
11+
12+ export interface PropertyChangeData extends ChangeData {
13+ propertyName : string ;
14+ oldValue : any ;
15+ newValue : any ;
16+ cancel ?: boolean ;
17+ }
18+
19+ export class Observable {
20+ public static propertyChangeEvent = "propertyChange" ;
21+ private _observers = { } ;
22+
23+ // true to track the Changing phase, false otherwise
24+ private _trackChanging = false ;
25+
26+ public bind ( eventName : string , callback : ( data : ChangeData ) => void ) {
27+ this . verifyCallback ( callback ) ;
28+ var list = this . getEventList ( eventName , true ) ;
29+ list . push ( callback ) ;
30+ }
31+
32+ public setProperty ( name : string , value : any ) {
33+ // TODO: Parameter validation
34+
35+ if ( ! ( name in this . _observers ) ) {
36+ // no observers to notify for the PropertyChange event
37+ return ;
38+ }
39+
40+ // create data for the change
41+ var data : PropertyChangeData = {
42+ eventName : Observable . propertyChangeEvent ,
43+ propertyName : name ,
44+ sender : this ,
45+ oldValue : this [ name ] ,
46+ newValue : value ,
47+ cancel : false
48+ } ;
49+
50+ if ( this . _trackChanging ) {
51+ data . phase = ChangePhase . Changing ;
52+ this . notifyObservers ( data ) ;
53+ if ( data . cancel ) {
54+ // change is canceled by an observer
55+ // TODO: define some priority, e.g. if someone cancels the change should others be able to override this cancelation?
56+ return ;
57+ }
58+ }
59+
60+ data . phase = ChangePhase . Changed ;
61+ this . notifyObservers ( data ) ;
62+
63+ this . setPropertyCore ( data ) ;
64+ }
65+
66+ public getProperty ( name : string ) : any {
67+ return this [ name ] ;
68+ }
69+
70+ /**
71+ * This method is intended to be overriden by inheritors to specify additional
72+ */
73+ public setPropertyCore ( data : PropertyChangeData ) {
74+ // get the new value from the data since some observer may have it modified - e.g. validation scenario
75+ this [ data . eventName ] = data . newValue ;
76+ }
77+
78+ private getEventList ( eventName : string , createIfNeeded ?: boolean ) : Array < ( data : ChangeData ) => void > {
79+ if ( ! eventName ) {
80+ throw new TypeError ( "EventName must be valid string." ) ;
81+ }
82+
83+ var list = < Array < ( data : ChangeData ) => void > > this . _observers [ eventName ] ;
84+ if ( ! list && createIfNeeded ) {
85+ list = new Array < ( data : ChangeData ) => void > ( ) ;
86+ this . _observers [ eventName ] = list ;
87+ }
88+
89+ return list ;
90+ }
91+
92+ private verifyCallback ( callback : any ) {
93+ if ( ! callback || typeof callback !== "function" ) {
94+ throw new TypeError ( "Callback must be a valid function." ) ;
95+ }
96+ }
97+
98+ // The method will return true if the change is accepted, false otherwise
99+ private notifyObservers ( data : ChangeData ) {
100+ var observers = this . getEventList ( data . eventName ) ;
101+ if ( ! observers ) {
102+ return ;
103+ }
104+
105+ var i ,
106+ callback ;
107+ for ( i = 0 ; i < observers . length ; i ++ ) {
108+ callback = observers [ i ] ;
109+ callback ( data ) ;
110+ }
111+ }
112+ }
0 commit comments