1
+ angular . module ( "OmniBinder" , [ ] ) . factory ( "obBinder" , [ "$timeout" , "$q" , "$parse" , "$window" , "obSyncEvents" , "obBinderTypes" , "obModelWriter" , "obObserver" , function ( $timeout , $q , $parse , $window , obSyncEvents , obBinderTypes , obModelWriter , obObserver ) {
2
+ function Binder ( scope , model , protocol , options ) {
3
+ if ( options = options || { } , ! protocol ) throw new Error ( "protocol is required" ) ;
4
+ if ( ! scope ) throw new Error ( "scope is required" ) ;
5
+ if ( ! model ) throw new Error ( "model is required" ) ;
6
+ if ( options . key && "string" != typeof options . key ) throw new Error ( "key must be a string" ) ;
7
+ this . protocol = protocol , this . scope = scope , this . model = model , this . query = options . query ,
8
+ this . type = options . type , this . key = options . key , this . bindModel ( this . type , scope , model ) ,
9
+ this . protocol . subscribe ( this ) , this . ignoreNModelChanges = 0 , this . ignoreNProtocolChanges = 0 ;
10
+ }
11
+ return Binder . prototype . bindModel = function ( type , scope , model ) {
12
+ switch ( type ) {
13
+ case obBinderTypes . COLLECTION :
14
+ this . observer = obObserver . observeCollection ( this , scope [ model ] , this . onModelChange ) ;
15
+ }
16
+ } , Binder . prototype . onModelChange = function ( changes ) {
17
+ for ( var numAffectedItems = 0 , delta = {
18
+ changes : changes
19
+ } , i = 0 ; i < changes . length ; i ++ ) numAffectedItems += changes . name && 1 || changes [ i ] . addedCount + ( changes [ i ] . removed && changes [ i ] . removed . length ) || 0 ;
20
+ return delta . changes . length ? this . ignoreNModelChanges ? this . ignoreNModelChanges -= numAffectedItems : ( this . protocol . processChanges ( this , delta ) ,
21
+ void 0 ) : void 0 ;
22
+ } , Binder . prototype . onProtocolChange = function ( changes ) {
23
+ if ( delta = {
24
+ changes : changes
25
+ } , changes . length ) if ( this . ignoreNProtocolChanges ) {
26
+ newChanges = [ ] ;
27
+ for ( var i = 0 ; i < changes . length ; i ++ ) changes [ i ] . force && newChanges . push ( changes [ i ] ) ,
28
+ this . ignoreNProtocolChanges -- ;
29
+ if ( ! newChanges . length ) return ;
30
+ delta . changes = newChanges , obModelWriter . processChanges ( this , delta ) ;
31
+ } else obModelWriter . processChanges ( this , delta ) ;
32
+ } , Binder . prototype . val = function ( ) {
33
+ var getter = $parse ( this . model ) ;
34
+ return getter ( this . scope ) ;
35
+ } , function ( ) {
36
+ var binder = Object . create ( Binder . prototype ) ;
37
+ return Binder . apply ( binder , arguments ) , binder ;
38
+ } ;
39
+ } ] ) , angular . module ( "OmniBinder" ) . factory ( "obBinderTypes" , [ function ( ) {
40
+ return {
41
+ COLLECTION : "collection" ,
42
+ OBJECT : "object" ,
43
+ BOOLEAN : "boolean" ,
44
+ STRING : "string" ,
45
+ NUMBER : "number" ,
46
+ BINARY : "binary" ,
47
+ BINARY_STREAM : "binaryStream"
48
+ } ;
49
+ } ] ) , function ( ) {
50
+ var DeltaFactory = function ( ) { } ;
51
+ DeltaFactory . prototype . addChange = function ( change ) {
52
+ if ( ! change . type ) throw new Error ( "Change must contain a type" ) ;
53
+ this . changes . push ( change ) ;
54
+ } , DeltaFactory . prototype . updateObject = function ( object ) {
55
+ this . object = object , angular . forEach ( this . changes , function ( change , i , list ) {
56
+ list [ i ] . object = object ;
57
+ } ) ;
58
+ } , angular . module ( "OmniBinder" ) . factory ( "obDelta" , function ( ) {
59
+ return function ( change ) {
60
+ var delta = Object . create ( DeltaFactory . prototype ) ;
61
+ return DeltaFactory . call ( delta ) , delta . changes = [ ] , change && delta . addChange ( change ) ,
62
+ delta ;
63
+ } ;
64
+ } ) ;
65
+ } ( ) , angular . module ( "OmniBinder" ) . service ( "obModelWriter" , [ "$parse" , "obBinderTypes" , "obSyncEvents" , function ( $parse , obBinderTypes ) {
66
+ this . applyArrayChange = function ( binder , change ) {
67
+ var model = $parse ( binder . model ) ( binder . scope ) ;
68
+ if ( change . added ) {
69
+ var firstChange = change . added . shift ( ) ;
70
+ for ( model . splice ( change . index , change . removed ? change . removed . length : 0 , firstChange ) ; next = change . added . shift ( ) ; ) change . index ++ ,
71
+ model . splice ( change . index , 0 , next ) ;
72
+ } else model . splice ( change . index , change . removed ? change . removed . length : 0 ) ;
73
+ binder . ignoreNModelChanges += ( change . removed && change . removed . length || 0 ) + change . addedCount ,
74
+ $parse ( binder . model ) . assign ( binder . scope , model ) , binder . scope . $$phase || binder . scope . $apply ( ) ;
75
+ } , this . applyObjectChange = function ( binder , change ) {
76
+ function findObject ( keyName , key ) {
77
+ var obj , collection = binder . scope [ binder . model ] ;
78
+ return angular . forEach ( collection , function ( item ) {
79
+ obj || ( item [ keyName ] === key ? obj = item : "undefined" == typeof item [ keyName ] && ( obj = item ) ) ;
80
+ } ) , obj ;
81
+ }
82
+ if ( binder . key ) {
83
+ var obj = findObject ( binder . key , change . object [ binder . key ] ) ;
84
+ if ( ! obj ) throw new Error ( "Could not find object with key" + change . object [ binder . key ] ) ;
85
+ switch ( change . type ) {
86
+ case "update" :
87
+ obj [ change . name ] !== change . object [ change . name ] && binder . ignoreNModelChanges ++ ,
88
+ obj [ change . name ] = change . object [ change . name ] ;
89
+ break ;
90
+
91
+ case "delete" :
92
+ binder . ignoreNModelChanges ++ , delete obj [ change . name ] ;
93
+ break ;
94
+
95
+ case "new" :
96
+ obj [ change . name ] !== change . object [ change . name ] && binder . ignoreNModelChanges ++ ,
97
+ obj [ change . name ] = change . object [ change . name ] ;
98
+ }
99
+ binder . scope . $$phase || binder . scope . $apply ( ) ;
100
+ }
101
+ } , this . processChanges = function ( binder , delta ) {
102
+ angular . forEach ( delta . changes , function ( change ) {
103
+ switch ( binder . type ) {
104
+ case obBinderTypes . COLLECTION :
105
+ "number" == typeof change . index ? this . applyArrayChange ( binder , change ) : "string" == typeof change . name && this . applyObjectChange ( binder , change ) ;
106
+ }
107
+ } , this ) ;
108
+ } ;
109
+ } ] ) , angular . module ( "OmniBinder" ) . factory ( "obArrayChange" , function ( ) {
110
+ return function ( addedCount , removed , index ) {
111
+ return {
112
+ addedCount : addedCount ,
113
+ removed : removed ,
114
+ index : index
115
+ } ;
116
+ } ;
117
+ } ) . factory ( "obOldObject" , function ( ) {
118
+ return function ( change ) {
119
+ var oldObject = angular . copy ( change . object ) ;
120
+ return oldObject [ change . name ] = change . oldValue , oldObject ;
121
+ } ;
122
+ } ) . service ( "obObserver" , [ "obArrayChange" , "obOldObject" , function ( obArrayChange , obOldObject ) {
123
+ this . observeObjectInCollection = function ( context , collection , object , callback ) {
124
+ function onObjectObserved ( changes ) {
125
+ function pushSplice ( change ) {
126
+ var oldObject = obOldObject ( change ) , index = collection . indexOf ( change . object ) , change = obArrayChange ( 1 , [ oldObject ] , index ) ;
127
+ splices . push ( change ) ;
128
+ }
129
+ var splices = [ ] ;
130
+ context . key ? callback . call ( context , changes ) : ( angular . forEach ( changes , pushSplice ) ,
131
+ callback . call ( context , splices ) ) ;
132
+ }
133
+ this . observers [ object ] = onObjectObserved , Object . observe ( object , onObjectObserved ) ;
134
+ } , this . observers = { } , this . observeCollection = function ( context , collection , callback ) {
135
+ function observeOne ( obj ) {
136
+ self . observeObjectInCollection ( context , collection , obj , callback ) ;
137
+ }
138
+ function onArrayChange ( changes ) {
139
+ angular . forEach ( changes , watchNewObjects ) , callback . call ( context , changes ) ;
140
+ }
141
+ function watchNewObjects ( change ) {
142
+ for ( var i = change . index , lastIndex = change . addedCount + change . index ; lastIndex > i ; ) observeOne ( collection [ i ] ) ,
143
+ i ++ ;
144
+ change . removed . length && angular . forEach ( change . removed , function ( obj ) {
145
+ Object . unobserve ( obj , self . observers [ obj ] ) ;
146
+ } ) ;
147
+ }
148
+ var observer , self = this ;
149
+ return angular . forEach ( collection , observeOne ) , observer = new ArrayObserver ( collection , onArrayChange ) ;
150
+ } ;
151
+ } ] ) , angular . module ( "OmniBinder" ) . value ( "obSyncEvents" , {
152
+ NEW : "new" ,
153
+ UPDATED : "update" ,
154
+ DELETED : "deleted" ,
155
+ RECONFIGURED : "reconfigured" ,
156
+ READ : "read" ,
157
+ MOVE : "move" ,
158
+ NONE : "none" ,
159
+ INIT : "init" ,
160
+ UNKNOWN : "unknown"
161
+ } ) ;
0 commit comments