2
2
'use strict' /*global angular */
3
3
angular . module ( 'ngSails' , [ 'ng' ] ) ;
4
4
5
- /*jslint sloppy:true*/
6
5
/*global angular, io */
7
- angular . module ( 'ngSails' ) . provider ( '$sails' , function ( ) {
8
- var provider = this ,
9
- httpVerbs = [ 'get' , 'post' , 'put' , 'delete' ] ,
10
- eventNames = [ 'on' , 'once' ] ;
11
-
12
- this . url = undefined ;
13
- this . interceptors = [ ] ;
14
- this . responseHandler = undefined ;
15
-
16
- this . $get = [ '$q' , '$timeout' , function ( $q , $timeout ) {
17
- var socket = io . connect ( provider . url ) ,
18
- defer = function ( ) {
19
- var deferred = $q . defer ( ) ,
20
- promise = deferred . promise ;
21
-
22
- promise . success = function ( fn ) {
23
- promise . then ( function ( response ) {
24
- fn ( response . data , response . status , response . headers ) ;
25
- } ) ;
26
- return promise ;
27
- } ;
6
+ ( function ( angular , io ) {
7
+ 'use strict' ;
8
+ io . sails . autoConnect = false ;
28
9
29
- promise . error = function ( fn ) {
30
- promise . then ( null , function ( response ) {
31
- fn ( response . data , response . status , response . headers ) ;
32
- } ) ;
33
- return promise ;
10
+ // copied from angular
11
+ function parseHeaders ( headers ) {
12
+ var parsed = { } ,
13
+ key , val , i ;
14
+ if ( ! headers ) return parsed ;
15
+ angular . forEach ( headers . split ( '\n' ) , function ( line ) {
16
+ i = line . indexOf ( ':' ) ;
17
+ key = lowercase ( trim ( line . substr ( 0 , i ) ) ) ;
18
+ val = trim ( line . substr ( i + 1 ) ) ;
19
+ if ( key ) {
20
+ parsed [ key ] = parsed [ key ] ? parsed [ key ] + ', ' + val : val ;
21
+ }
22
+ } ) ;
23
+
24
+ return parsed ;
25
+ }
26
+
27
+ function trim ( value ) {
28
+ return angular . isString ( value ) ? value . trim ( ) : value ;
29
+ }
30
+
31
+ function isPromiseLike ( obj ) {
32
+ return obj && angular . isFunction ( obj . then ) ;
33
+ }
34
+
35
+ // copied from angular
36
+ function headersGetter ( headers ) {
37
+ var headersObj = angular . isObject ( headers ) ? headers : undefined ;
38
+ return function ( name ) {
39
+ if ( ! headersObj ) headersObj = parseHeaders ( headers ) ;
40
+ if ( name ) {
41
+ var value = headersObj [ lowercase ( name ) ] ;
42
+ if ( value === void 0 ) {
43
+ value = null ;
44
+ }
45
+ return value ;
46
+ }
47
+ return headersObj ;
48
+ } ;
49
+ }
50
+
51
+ angular . module ( 'ngSails' ) . provider ( '$sails' , function ( ) {
52
+ var provider = this ;
53
+
54
+ this . httpVerbs = [ 'get' , 'post' , 'put' , 'delete' ] ;
55
+
56
+ this . eventNames = [ 'on' , 'off' , 'once' ] ;
57
+
58
+ this . url = undefined ;
59
+
60
+ this . urlPrefix = '' ;
61
+
62
+ this . config = {
63
+ transports : [ 'websocket' , 'polling' ] ,
64
+ useCORSRouteToGetCookie : false
65
+ } ;
66
+
67
+ this . debug = false ;
68
+
69
+ // like https://docs.angularjs.org/api/ng/service/$http#interceptors
70
+ // but with sails.io arguments
71
+ var interceptorFactories = this . interceptors = [
72
+ /*function($injectables) {
73
+ return {
74
+ request: function(config) {},
75
+ response: function(response) {},
76
+ requestError: function(rejection) {},
77
+ responseError: function(rejection) {}
34
78
};
79
+ }*/
80
+ ] ;
35
81
36
- return deferred ;
37
- } ,
38
- resolveOrReject = this . responseHandler || function ( deferred , response ) {
39
- var jwr = response ;
40
-
41
- // backward compatibility with older sails.io (no JWR)
42
- if ( ! ( response instanceof Object && response . constructor . name === "JWR" ) ) {
43
- jwr = {
44
- body : response ,
45
- headers : response . headers || { } ,
46
- statusCode : response . statusCode || response . status
47
- } ;
82
+ /*@ngInject */
83
+ this . $get = [ "$q" , "$injector" , "$rootScope" , "$log" , "$timeout" , function ( $q , $injector , $rootScope , $log , $timeout ) {
84
+ var socket = ( io . sails && io . sails . connect || io . connect ) ( provider . url , provider . config ) ;
85
+
86
+ socket . connect = function ( opts ) {
87
+ if ( ! socket . isConnected ( ) ) {
88
+ var _opts = opts || { } ;
89
+ _opts = angular . extend ( { } , provider . config , opts ) ;
90
+
91
+ // These are the options sails.io.js actually sets when making the connection.
92
+ socket . useCORSRouteToGetCookie = _opts . useCORSRouteToGetCookie ;
93
+ socket . url = _opts . url || provider . url ;
94
+ socket . multiplex = _opts . multiplex ;
95
+
96
+ socket . _connect ( ) ;
48
97
}
98
+ return socket ;
99
+ }
49
100
50
- // angular $http returns the 'body' as 'data'.
51
- jwr . data = jwr . body ;
101
+ // TODO: separate out interceptors into its own file (and provider?).
102
+ // build interceptor chain
103
+ var reversedInterceptors = [ ] ;
104
+ angular . forEach ( interceptorFactories , function ( interceptorFactory ) {
105
+ reversedInterceptors . unshift (
106
+ angular . isString ( interceptorFactory ) ?
107
+ $injector . get ( interceptorFactory ) : $injector . invoke ( interceptorFactory )
108
+ ) ;
109
+ } ) ;
52
110
53
- // angular $http returns the 'statusCode' as 'status'.
54
- jwr . status = jwr . statusCode ;
111
+ // Send the request using the socket
112
+ function serverRequest ( config ) {
113
+ var defer = $q . defer ( ) ;
114
+ if ( provider . debug ) $log . info ( '$sails ' + config . method + ' ' + config . url , config . data || '' ) ;
55
115
56
- // TODO: map 'status'/'statusCode' to a 'statusText' to mimic angular $http
116
+ if ( config . timeout > 0 ) {
117
+ $timeout ( timeoutRequest , config . timeout ) ;
118
+ } else if ( isPromiseLike ( config . timeout ) ) {
119
+ config . timeout . then ( timeoutRequest ) ;
120
+ }
121
+
122
+ socket [ 'legacy_' + config . method . toLowerCase ( ) ] ( config . url , config . data , serverResponse ) ;
57
123
58
- if ( jwr . error ) {
59
- deferred . reject ( jwr ) ;
60
- } else {
61
- deferred . resolve ( jwr ) ;
124
+ function timeoutRequest ( ) {
125
+ serverResponse ( null ) ;
62
126
}
63
- } ,
64
- angularify = function ( cb , data ) {
65
- $timeout ( function ( ) {
66
- cb ( data ) ;
67
- } ) ;
68
- } ,
69
- promisify = function ( methodName ) {
70
- socket [ 'legacy_' + methodName ] = socket [ methodName ] ;
71
- socket [ methodName ] = function ( url , data , cb ) {
72
- var deferred = defer ( ) ;
73
- if ( cb === undefined && angular . isFunction ( data ) ) {
74
- cb = data ;
75
- data = null ;
127
+
128
+ function serverResponse ( result , jwr ) {
129
+
130
+ if ( ! jwr ) {
131
+ jwr = {
132
+ body : result ,
133
+ headers : result . headers || { } ,
134
+ statusCode : result . statusCode || result . status || 0 ,
135
+ error : ( function ( ) {
136
+ if ( this . statusCode < 200 || this . statusCode >= 400 ) {
137
+ return this . body || this . statusCode ;
138
+ }
139
+ } ) ( )
140
+ } ;
141
+ }
142
+
143
+ jwr . data = jwr . body ; // $http compat
144
+ jwr . status = jwr . statusCode ; // $http compat
145
+ jwr . socket = socket ;
146
+ jwr . url = config . url ;
147
+ jwr . method = config . method ;
148
+ jwr . config = config . config ;
149
+ if ( jwr . error ) {
150
+ if ( provider . debug ) $log . warn ( '$sails response ' + jwr . statusCode + ' ' + config . url , jwr ) ;
151
+ defer . reject ( jwr ) ;
152
+ } else {
153
+ if ( provider . debug ) $log . info ( '$sails response ' + config . url , jwr ) ;
154
+ defer . resolve ( jwr ) ;
76
155
}
77
- deferred . promise . then ( cb ) ;
78
- socket [ 'legacy_' + methodName ] ( url , data , function ( emulatedHTTPBody , jsonWebSocketResponse ) {
79
- resolveOrReject ( deferred , jsonWebSocketResponse || emulatedHTTPBody ) ;
156
+ }
157
+
158
+ return defer . promise ;
159
+ }
160
+
161
+ function promisify ( methodName ) {
162
+ socket [ 'legacy_' + methodName ] = socket [ methodName ] ;
163
+
164
+ socket [ methodName ] = function ( url , data , config ) {
165
+
166
+ var chain = [ serverRequest , undefined ] ;
167
+
168
+ //TODO: more compatible with $http methods and config
169
+
170
+ var promise = $q . when ( {
171
+ url : provider . urlPrefix + url ,
172
+ data : data ,
173
+ socket : socket ,
174
+ config : config || { } ,
175
+ method : methodName . toUpperCase ( )
80
176
} ) ;
81
- return deferred . promise ;
82
- } ;
83
- } ,
84
- wrapEvent = function ( eventName ) {
85
- socket [ 'legacy_' + eventName ] = socket [ eventName ] ;
86
- socket [ eventName ] = function ( event , cb ) {
87
- if ( cb !== null && angular . isFunction ( cb ) ) {
88
- socket [ 'legacy_' + eventName ] ( event , function ( result ) {
89
- angularify ( cb , result ) ;
90
- } ) ;
177
+
178
+ // apply interceptors
179
+ angular . forEach ( reversedInterceptors , function ( interceptor ) {
180
+ if ( interceptor . request || interceptor . requestError ) {
181
+ chain . unshift ( interceptor . request , interceptor . requestError ) ;
182
+ }
183
+ if ( interceptor . response || interceptor . responseError ) {
184
+ chain . push ( interceptor . response , interceptor . responseError ) ;
185
+ }
186
+ } ) ;
187
+
188
+ while ( chain . length ) {
189
+ var thenFn = chain . shift ( ) ;
190
+ var rejectFn = chain . shift ( ) ;
191
+
192
+ promise = promise . then ( thenFn , rejectFn ) ;
91
193
}
194
+
195
+ // be $http compatible
196
+ promise . success = function ( fn ) {
197
+ promise . then ( function ( jwr ) {
198
+ fn ( jwr . body , jwr . statusCode , headersGetter ( jwr . headers ) , jwr ) ;
199
+ } ) ;
200
+ return promise ;
201
+ } ;
202
+ promise . error = function ( fn ) {
203
+ promise . then ( null , function ( jwr ) {
204
+ fn ( jwr . body , jwr . statusCode , headersGetter ( jwr . headers ) , jwr ) ;
205
+ } ) ;
206
+ return promise ;
207
+ } ;
208
+
209
+ return promise ;
92
210
} ;
93
- } ;
211
+ }
212
+
213
+ function wrapEvent ( eventName ) {
214
+ if ( socket [ eventName ] || socket . _raw [ eventName ] ) {
215
+ socket [ 'legacy_' + eventName ] = socket [ eventName ] || socket . _raw [ eventName ] ;
216
+ socket [ eventName ] = function ( event , cb ) {
217
+ if ( cb !== null && angular . isFunction ( cb ) ) {
218
+ socket [ 'legacy_' + eventName ] ( event , function ( result ) {
219
+ $rootScope . $evalAsync ( cb . bind ( socket , result ) ) ;
220
+ } ) ;
221
+ }
222
+ } ;
223
+ }
224
+ }
225
+
226
+ angular . forEach ( provider . httpVerbs , promisify ) ;
227
+ angular . forEach ( provider . eventNames , wrapEvent ) ;
228
+
229
+
230
+ /**
231
+ * Update a model on sails pushes
232
+ * @param {String } name Sails model name
233
+ * @param {Array } models Array with model objects
234
+ */
235
+ socket . $modelUpdater = function ( name , models ) {
236
+
237
+ socket . on ( name , function ( message ) {
238
+ var i ;
239
+
240
+ switch ( message . verb ) {
94
241
95
- angular . forEach ( httpVerbs , promisify ) ;
96
- angular . forEach ( eventNames , wrapEvent ) ;
242
+ case "created" :
243
+ // create new model item
244
+ models . push ( message . data ) ;
245
+ break ;
246
+
247
+ case "updated" :
248
+ var obj ;
249
+ for ( i = 0 ; i < models . length ; i ++ ) {
250
+ if ( models [ i ] . id === message . id ) {
251
+ obj = models [ i ] ;
252
+ break ;
253
+ }
254
+ }
255
+
256
+ // cant update if the angular-model does not have the item and the
257
+ // sails message does not give us the previous record
258
+ if ( ! obj && ! message . previous ) return ;
259
+
260
+ if ( ! obj ) {
261
+ // sails has given us the previous record, create it in our model
262
+ obj = message . previous ;
263
+ models . push ( obj ) ;
264
+ }
265
+
266
+ // update the model item
267
+ angular . extend ( obj , message . data ) ;
268
+ break ;
269
+
270
+ case "destroyed" :
271
+ for ( i = 0 ; i < models . length ; i ++ ) {
272
+ if ( models [ i ] . id === message . id ) {
273
+ models . splice ( i , 1 ) ;
274
+ break ;
275
+ }
276
+ }
277
+ break ;
278
+ }
279
+ } ) ;
280
+ } ;
97
281
98
- return socket ;
99
- } ] ;
100
- } ) ;
282
+ return socket ;
283
+ } ] ;
284
+ this . $get . $inject = [ "$q" , "$injector" , "$rootScope" , "$log" , "$timeout" ] ;
285
+ } ) ;
286
+ } ( angular , io ) ) ;
101
287
} ( angular , io ) ) ;
0 commit comments