UNPKG

3.28 kBPlain TextView Raw
1import methods from './methodsConfig'
2import { Map, MapboxEvent } from 'mapbox-gl'
3
4type Event = { eventId: string } & MapboxEvent
5
6function generateEventId(methodName: string): string {
7 return `${methodName}-${('' + Math.random()).split('.')[1]}`
8}
9
10function catchEventFabric(
11 map: Map,
12 eventName: string,
13 eventId: string,
14 resolve: (event) => void
15): (event: any) => void {
16 const catchEvent = (event: Event) => {
17 if (event.type !== eventName || event.eventId !== eventId) {
18 return
19 }
20 map.off(eventName, catchEvent)
21 resolve(event)
22 }
23 return catchEvent
24}
25
26function promisifyMethod(map: Map, methodName: string): (...args: any) => Promise<object> {
27 const method = map[methodName]
28 const argsCount = method.length
29
30 return (...args) => {
31 const handlers = []
32 const eventData = { eventId: generateEventId(methodName) }
33 // Creating list of events and event listeners
34 const catchers = methods[methodName].events.map((event, index) => {
35 return {
36 event,
37 func: new Promise((resolve, reject) => {
38 handlers[index] = { event, func: catchEventFabric(map, event.name, eventData.eventId, resolve) }
39 map.on(event.name, handlers[index].func)
40 })
41 }
42 })
43
44 const argsArray = []
45 // Creating list of arguments.
46 for (let i = 0; i < argsCount; i++) {
47 if (i === argsCount - 1) {
48 // If args[i] is last argument, we assume that this is eventData argument,
49 // merge it with eventData passed by user and add in the end of list of arguments
50 argsArray.push({ ...eventData, ...(args[i] || {}) })
51 } else {
52 // If args[i] is not last argument, just add it in the list of arguments
53 argsArray.push(args[i] || null)
54 }
55 }
56
57 let funcs = []
58 let options = args[0] || {}
59 try {
60 method.apply(map, argsArray)
61 // Filter catchers.
62 // If map state is not changes (e.g. zoomTo(1) don't produce any events if map already on zoom 1),
63 // just return resolved promise
64
65 // .fitBounds() and .fitScreenCoordinates() needs special processing due to different number of arguments
66 if (methodName === 'fitBounds') {
67 // args[0] is bounding box, options is args[1], but we don't need them to calculate events to listen
68 options = {}
69 }
70
71 if (methodName === 'fitScreenCoordinates') {
72 options = { bearing: null }
73 options.bearing = null
74 // bearing can be passed by user as optional argument
75 if (typeof args[2] === 'number') {
76 options.bearing = args[2]
77 }
78 // pass bearing merged with other options
79 if (args[3] && typeof args[3] === 'object') {
80 options = {
81 ...options,
82 ...args[3]
83 }
84 }
85 }
86
87 funcs = catchers.map(({ event, func }) => {
88 if (event.check(map, options)) {
89 return func
90 } else {
91 map.off(event.name, func)
92 return Promise.resolve()
93 }
94 })
95 } catch (err) {
96 handlers.forEach(({ event, func }) => {
97 map.off(event.name, func)
98 })
99 throw err
100 }
101
102 return Promise.all(funcs).then(() => {
103 return methods[methodName].getter(map)
104 })
105 }
106}
107
108export default promisifyMethod