1
1
'use strict' ;
2
-
3
- var request = require ( 'request' ) ;
4
- var crypto = require ( 'crypto' ) ;
5
-
2
+ var shajs = require ( 'sha.js' )
6
3
/**
7
4
* Construct a new mapbox event client to send interaction events to the mapbox event service
8
5
* @param {Object } options options with which to create the service
9
6
* @param {String } options.accessToken the mapbox access token to make requests
10
7
* @private
11
8
*/
12
- function MapboxEventManager ( options ) {
9
+ function MapboxEventManager ( options ) {
13
10
this . origin = options . origin || 'https://api.mapbox.com' ;
14
- this . endpoint = this . origin + '/events/v2' ;
11
+ this . endpoint = '/events/v2' ;
15
12
this . access_token = options . accessToken ;
16
13
this . version = '0.0.1'
17
14
this . sessionID = this . generateSessionID ( ) ;
18
15
this . userAgent = this . getUserAgent ( ) ;
19
16
// parse global options to be sent with each request
20
- this . countries = ( options . countries ) ? options . countries . split ( "," ) : null ;
21
- this . types = ( options . types ) ? options . types . split ( "," ) : null ;
17
+ this . countries = ( options . countries ) ? options . countries . split ( "," ) : null ;
18
+ this . types = ( options . types ) ? options . types . split ( "," ) : null ;
22
19
this . bbox = ( options . bbox ) ? options . bbox : null ;
23
20
this . language = ( options . language ) ? options . language . split ( "," ) : null ;
24
21
this . limit = ( options . limit ) ? + options . limit : null ;
@@ -32,15 +29,15 @@ function MapboxEventManager(options){
32
29
MapboxEventManager . prototype = {
33
30
34
31
/**
35
- * Send a search.select event to the mapbox events service
36
- * This event marks the array index of the item selected by the user out of the array of possible options
37
- * @private
38
- * @param {Object } selected the geojson feature selected by the user
39
- * @param {Object } geocoder a mapbox-gl-geocoder instance
40
- * @param {Function } callback a callback function to invoke once the event has been sent (optional)
41
- * @returns {Promise }
42
- */
43
- select : function ( selected , geocoder , callback ) {
32
+ * Send a search.select event to the mapbox events service
33
+ * This event marks the array index of the item selected by the user out of the array of possible options
34
+ * @private
35
+ * @param {Object } selected the geojson feature selected by the user
36
+ * @param {Object } geocoder a mapbox-gl-geocoder instance
37
+ * @param {Function } callback a callback function to invoke once the event has been sent (optional)
38
+ * @returns {Promise }
39
+ */
40
+ select : function ( selected , geocoder , callback ) {
44
41
var resultIndex = this . getSelectedIndex ( selected , geocoder ) ;
45
42
var payload = this . getEventPayload ( 'search.select' , geocoder ) ;
46
43
payload . resultIndex = resultIndex ;
@@ -55,36 +52,37 @@ MapboxEventManager.prototype = {
55
52
} ,
56
53
57
54
/**
58
- * Send a search-start event to the mapbox events service
59
- * This turnstile event marks when a user starts a new search
60
- * @private
61
- * @param {Object } geocoder a mapbox-gl-geocoder instance
62
- * @param {Function } callback
63
- * @returns {Promise }
64
- */
65
- start : function ( geocoder , callback ) {
55
+ * Send a search-start event to the mapbox events service
56
+ * This turnstile event marks when a user starts a new search
57
+ * @private
58
+ * @param {Object } geocoder a mapbox-gl-geocoder instance
59
+ * @param {Function } callback
60
+ * @returns {Promise }
61
+ */
62
+ start : function ( geocoder , callback ) {
66
63
var payload = this . getEventPayload ( 'search.start' , geocoder ) ;
67
64
return this . send ( payload , callback ) ;
68
65
} ,
69
66
70
67
/**
71
- * Send an event to the events service
72
- *
73
- * The event is skipped if the instance is not enabled to send logging events
74
- *
75
- * @private
76
- * @param {Object } payload the http POST body of the event
77
- * @returns {Promise }
78
- */
79
- send : function ( payload , callback ) {
80
- if ( ! callback ) callback = function ( ) { return } ;
81
- if ( ! this . enableEventLogging ) {
68
+ * Send an event to the events service
69
+ *
70
+ * The event is skipped if the instance is not enabled to send logging events
71
+ *
72
+ * @private
73
+ * @param {Object } payload the http POST body of the event
74
+ * @returns {Promise }
75
+ */
76
+ send : function ( payload , callback ) {
77
+ if ( ! callback ) callback = function ( ) {
78
+ return
79
+ } ;
80
+ if ( ! this . enableEventLogging ) {
82
81
return callback ( ) ;
83
82
}
84
83
var options = this . getRequestOptions ( payload ) ;
85
- this . request ( options , function ( err , res ) {
84
+ this . request ( options , function ( err ) {
86
85
if ( err ) return this . handleError ( err , callback ) ;
87
- if ( res . statusCode != 204 ) return this . handleError ( res , callback ) ;
88
86
if ( callback ) return callback ( ) ;
89
87
} )
90
88
} ,
@@ -93,121 +91,131 @@ MapboxEventManager.prototype = {
93
91
* @private
94
92
* @param {* } payload
95
93
*/
96
- getRequestOptions : function ( payload ) {
94
+ getRequestOptions : function ( payload ) {
97
95
var options = {
98
96
// events must be sent with POST
99
- method : "POST" ,
100
- url : this . endpoint ,
101
- headers :{
97
+ method : "POST" ,
98
+ host : this . origin ,
99
+ path : this . endpoint + "?access_token=" + this . access_token ,
100
+ headers : {
102
101
'Content-Type' : 'application/json'
103
102
} ,
104
- qs : {
105
- access_token : this . access_token
106
- } ,
107
- body :JSON . stringify ( [ payload ] ) //events are arrays
103
+ body : JSON . stringify ( [ payload ] ) //events are arrays
108
104
}
109
105
return options
110
106
} ,
111
107
112
108
/**
113
- * Get the event payload to send to the events service
114
- * Most payload properties are shared across all events
115
- * @private
116
- * @param {String } event the name of the event to send to the events service. Valid options are 'search.start', 'search.select', 'search.feedback'.
117
- * @param {Object } geocoder a mapbox-gl-geocoder instance
118
- * @returns {Object } an event payload
119
- */
120
- getEventPayload : function ( event , geocoder ) {
109
+ * Get the event payload to send to the events service
110
+ * Most payload properties are shared across all events
111
+ * @private
112
+ * @param {String } event the name of the event to send to the events service. Valid options are 'search.start', 'search.select', 'search.feedback'.
113
+ * @param {Object } geocoder a mapbox-gl-geocoder instance
114
+ * @returns {Object } an event payload
115
+ */
116
+ getEventPayload : function ( event , geocoder ) {
121
117
var proximity ;
122
118
if ( ! geocoder . options . proximity ) proximity = null ;
123
- else proximity = [ geocoder . options . proximity . longitude , geocoder . options . proximity . latitude ] ;
119
+ else proximity = [ geocoder . options . proximity . longitude , geocoder . options . proximity . latitude ] ;
124
120
125
121
var zoom = ( geocoder . _map ) ? geocoder . _map . getZoom ( ) : null ;
126
122
return {
127
123
event : event ,
128
124
created : + new Date ( ) ,
129
125
sessionIdentifier : this . sessionID ,
130
- country : this . countries ,
126
+ country : this . countries ,
131
127
userAgent : this . userAgent ,
132
128
language : this . language ,
133
129
bbox : this . bbox ,
134
- types : this . types ,
130
+ types : this . types ,
135
131
endpoint : 'mapbox.places' ,
136
132
// fuzzyMatch: search.fuzzy, //todo --> add to plugin
137
133
proximity : proximity ,
138
134
limit : geocoder . options . limit ,
139
135
// routing: search.routing, //todo --> add to plugin
140
136
queryString : geocoder . inputString ,
141
137
mapZoom : zoom ,
142
- keyboardLocale : this . locale
138
+ keyboardLocale : this . locale
143
139
}
144
140
} ,
145
141
146
142
/**
147
- * Wraps the request function for easier testing
148
- * Make an http request and invoke a callback
149
- * @private
150
- * @param {Object } opts options describing the http request to be made
151
- * @param {Function } callback the callback to invoke when the http request is completed
152
- */
153
- request : function ( opts , callback ) {
154
- request ( opts , function ( err , res ) {
155
- return callback ( err , res ) ;
156
- } )
143
+ * Wraps the request function for easier testing
144
+ * Make an http request and invoke a callback
145
+ * @private
146
+ * @param {Object } opts options describing the http request to be made
147
+ * @param {Function } callback the callback to invoke when the http request is completed
148
+ */
149
+ request : function ( opts , callback ) {
150
+ var xhttp = new XMLHttpRequest ( ) ;
151
+ xhttp . onreadystatechange = function ( ) {
152
+ if ( this . readyState == 4 ) {
153
+ if ( this . status == 204 ) {
154
+ //success
155
+ return callback ( null ) ;
156
+ } else {
157
+ return callback ( this . statusText ) ;
158
+ }
159
+ }
160
+ } ;
161
+ xhttp . open ( opts . method , opts . host + opts . path , true ) ;
162
+ for ( var header in opts . headers ) {
163
+ var headerValue = opts . headers [ header ] ;
164
+ xhttp . setRequestHeader ( header , headerValue )
165
+ }
166
+ xhttp . send ( opts . body ) ;
157
167
} ,
158
168
159
169
/**
160
- * Handle an error that occurred while making a request
161
- * @param {Object } err an error instance to log
162
- * @private
163
- */
164
- handleError : function ( err , callback ) {
170
+ * Handle an error that occurred while making a request
171
+ * @param {Object } err an error instance to log
172
+ * @private
173
+ */
174
+ handleError : function ( err , callback ) {
165
175
if ( callback ) return callback ( err ) ;
166
176
} ,
167
177
168
178
/**
169
- * Generate a session ID to be returned with all of the searches made by this geocoder instance
170
- * ID is random and cannot be tracked across sessions
171
- * @private
172
- */
173
- generateSessionID : function ( ) {
174
- var sha = crypto . createHash ( 'sha256' ) ;
175
- sha . update ( Math . random ( ) . toString ( ) ) ;
176
- return sha . digest ( 'hex' )
179
+ * Generate a session ID to be returned with all of the searches made by this geocoder instance
180
+ * ID is random and cannot be tracked across sessions
181
+ * @private
182
+ */
183
+ generateSessionID : function ( ) {
184
+ return new shajs . sha256 ( ) . update ( Math . random ( ) . toString ( ) ) . digest ( 'hex' )
177
185
} ,
178
186
179
187
/**
180
- * Get a user agent string to send with the request to the events service
181
- * @private
182
- */
183
- getUserAgent : function ( ) {
188
+ * Get a user agent string to send with the request to the events service
189
+ * @private
190
+ */
191
+ getUserAgent : function ( ) {
184
192
return 'mapbox-gl-geocoder.' + this . version + "." + navigator . userAgent ;
185
193
} ,
186
194
187
195
/**
188
- * Get the 0-based numeric index of the item that the user selected out of the list of options
189
- * @private
190
- * @param {Object } selected the geojson feature selected by the user
191
- * @param {Object } geocoder a Mapbox-GL-Geocoder instance
192
- * @returns {Number } the index of the selected result
193
- */
194
- getSelectedIndex : function ( selected , geocoder ) {
196
+ * Get the 0-based numeric index of the item that the user selected out of the list of options
197
+ * @private
198
+ * @param {Object } selected the geojson feature selected by the user
199
+ * @param {Object } geocoder a Mapbox-GL-Geocoder instance
200
+ * @returns {Number } the index of the selected result
201
+ */
202
+ getSelectedIndex : function ( selected , geocoder ) {
195
203
var results = geocoder . _typeahead . data ;
196
204
var selectedID = selected . id ;
197
- var resultIDs = results . map ( function ( feature ) {
205
+ var resultIDs = results . map ( function ( feature ) {
198
206
return feature . id ;
199
207
} ) ;
200
208
var selectedIdx = resultIDs . indexOf ( selectedID ) ;
201
209
return selectedIdx ;
202
210
} ,
203
211
204
212
/**
205
- * Check whether events should be logged
206
- * Clients using a localGeocoder or an origin other than mapbox should not have events logged
207
- * @private
208
- */
209
- shouldEnableLogging : function ( options ) {
210
- if ( ! this . origin . includes ( 'api.mapbox.com' ) ) return false ;
213
+ * Check whether events should be logged
214
+ * Clients using a localGeocoder or an origin other than mapbox should not have events logged
215
+ * @private
216
+ */
217
+ shouldEnableLogging : function ( options ) {
218
+ if ( this . origin . indexOf ( 'api.mapbox.com' ) == - 1 ) return false ;
211
219
// hard to make sense of events when a local instance is suplementing results from origin
212
220
if ( options . localGeocoder ) return false ;
213
221
// hard to make sense of events when a custom filter is in use
0 commit comments