|
1 | 1 | const BASE_URL = 'https://maps';
|
2 | 2 | const DEFAULT_URL = `${BASE_URL}.googleapis.com`;
|
3 |
| -const API_PATH = '/maps/api/js?callback=_$_google_map_initialize_$_'; |
| 3 | +const API_PATH = '/maps/api/js?callback=googleMapsAPILoadedPromise'; |
| 4 | +const EVENT_GMAPS_LOADED = 'EVENT_GMAPS_LOADED'; |
4 | 5 |
|
5 |
| -const getUrl = region => { |
| 6 | +const getBaseUrl = region => { |
6 | 7 | if (region && region.toLowerCase() === 'cn') {
|
7 | 8 | return `${BASE_URL}.google.cn`;
|
8 | 9 | }
|
9 | 10 | return DEFAULT_URL;
|
10 | 11 | };
|
11 | 12 |
|
12 |
| -let $script_ = null; |
| 13 | +let currentResolver = null; |
| 14 | +let lastBaseUrl = ''; |
| 15 | +let lastScriptUrl = ''; |
| 16 | +let googleMapsPromise; |
13 | 17 |
|
14 |
| -let loadPromise_; |
15 |
| - |
16 |
| -let resolveCustomPromise_; |
17 |
| - |
18 |
| -const _customPromise = new Promise(resolve => { |
19 |
| - resolveCustomPromise_ = resolve; |
20 |
| -}); |
21 |
| - |
22 |
| -// TODO add libraries language and other map options |
23 |
| -export default (bootstrapURLKeys, heatmapLibrary) => { |
24 |
| - if (!$script_) { |
25 |
| - $script_ = require('scriptjs'); // eslint-disable-line |
26 |
| - } |
27 |
| - |
28 |
| - // call from outside google-map-react |
29 |
| - // will be as soon as loadPromise_ resolved |
30 |
| - if (!bootstrapURLKeys) { |
31 |
| - return _customPromise; |
32 |
| - } |
| 18 | +const destroyOldGoogleMapsInstance = url => { |
| 19 | + document |
| 20 | + .querySelectorAll(`script[src^='${url}']`) |
| 21 | + .forEach(script => script.remove()); |
| 22 | + if (window.google) delete window.google.maps; |
| 23 | +}; |
33 | 24 |
|
34 |
| - if (loadPromise_) { |
35 |
| - return loadPromise_; |
36 |
| - } |
| 25 | +// Callback for the Google Maps API src |
| 26 | +window.googleMapsAPILoadedPromise = () => |
| 27 | + window.dispatchEvent(new CustomEvent(EVENT_GMAPS_LOADED)); |
| 28 | + |
| 29 | +const getScriptUrl = bootstrapURLKeys => { |
| 30 | + const baseUrl = getBaseUrl(bootstrapURLKeys.region); |
| 31 | + const params = Object.keys(bootstrapURLKeys).reduce( |
| 32 | + (r, key) => `${r}&${key}=${bootstrapURLKeys[key]}`, |
| 33 | + '' |
| 34 | + ); |
| 35 | + return `${baseUrl}${API_PATH}${params}`; |
| 36 | +}; |
37 | 37 |
|
38 |
| - loadPromise_ = new Promise((resolve, reject) => { |
39 |
| - if (typeof window === 'undefined') { |
40 |
| - reject(new Error('google map cannot be loaded outside browser env')); |
41 |
| - return; |
42 |
| - } |
| 38 | +const loadScript = url => { |
| 39 | + const script = document.createElement('script'); |
43 | 40 |
|
44 |
| - if (window.google && window.google.maps) { |
45 |
| - resolve(window.google.maps); |
46 |
| - return; |
47 |
| - } |
| 41 | + script.type = 'text/javascript'; |
| 42 | + script.async = true; |
| 43 | + script.src = url; |
| 44 | + document.querySelector('head').appendChild(script); |
48 | 45 |
|
49 |
| - if (typeof window._$_google_map_initialize_$_ !== 'undefined') { |
50 |
| - reject(new Error('google map initialization error')); |
| 46 | + return new Promise(resolve => { |
| 47 | + if (currentResolver) { |
| 48 | + window.removeEventListener(EVENT_GMAPS_LOADED, currentResolver); |
51 | 49 | }
|
52 |
| - |
53 |
| - window._$_google_map_initialize_$_ = () => { |
54 |
| - delete window._$_google_map_initialize_$_; |
55 |
| - resolve(window.google.maps); |
| 50 | + currentResolver = () => { |
| 51 | + resolve(); |
56 | 52 | };
|
| 53 | + window.addEventListener(EVENT_GMAPS_LOADED, currentResolver); |
| 54 | + }); |
| 55 | +}; |
57 | 56 |
|
58 |
| - if (process.env.NODE_ENV !== 'production') { |
59 |
| - if (Object.keys(bootstrapURLKeys).indexOf('callback') > -1) { |
60 |
| - const message = `"callback" key in bootstrapURLKeys is not allowed, |
61 |
| - use onGoogleApiLoaded property instead`; |
62 |
| - // eslint-disable-next-line no-console |
63 |
| - console.error(message); |
64 |
| - throw new Error(message); |
65 |
| - } |
66 |
| - } |
67 |
| - |
68 |
| - const params = Object.keys(bootstrapURLKeys).reduce( |
69 |
| - (r, key) => `${r}&${key}=${bootstrapURLKeys[key]}`, |
70 |
| - '' |
71 |
| - ); |
| 57 | +const loadGoogleMaps = bootstrapURLKeys => |
| 58 | + new Promise(async resolve => { |
| 59 | + lastScriptUrl = getScriptUrl(bootstrapURLKeys); |
| 60 | + await loadScript(lastScriptUrl); |
| 61 | + resolve(window.google.maps); |
| 62 | + }); |
72 | 63 |
|
73 |
| - const baseUrl = getUrl(bootstrapURLKeys.region); |
74 |
| - const libraries = heatmapLibrary ? '&libraries=visualization' : ''; |
| 64 | +export default bootstrapURLKeys => { |
| 65 | + if (typeof window === 'undefined') { |
| 66 | + throw new Error('google map cannot be loaded outside browser env'); |
| 67 | + } |
75 | 68 |
|
76 |
| - $script_( |
77 |
| - `${baseUrl}${API_PATH}${params}${libraries}`, |
78 |
| - () => |
79 |
| - typeof window.google === 'undefined' && |
80 |
| - reject(new Error('google map initialization error (not loaded)')) |
81 |
| - ); |
82 |
| - }); |
| 69 | + if (process.env.NODE_ENV !== 'production') { |
| 70 | + if (Object.keys(bootstrapURLKeys).includes('callback')) { |
| 71 | + const message = `'callback' key in bootstrapURLKeys is not allowed, use onGoogleapiLoadedPromise property instead`; |
| 72 | + // eslint-disable-next-line no-console |
| 73 | + console.error(message); |
| 74 | + throw new Error(message); |
| 75 | + } |
| 76 | + } |
| 77 | + if (googleMapsPromise) { |
| 78 | + if (lastScriptUrl !== getScriptUrl(bootstrapURLKeys)) { |
| 79 | + destroyOldGoogleMapsInstance(lastBaseUrl); |
| 80 | + googleMapsPromise = loadGoogleMaps(bootstrapURLKeys); |
| 81 | + } |
| 82 | + return googleMapsPromise; |
| 83 | + } |
83 | 84 |
|
84 |
| - resolveCustomPromise_(loadPromise_); |
| 85 | + googleMapsPromise = loadGoogleMaps(bootstrapURLKeys); |
| 86 | + lastBaseUrl = getBaseUrl(bootstrapURLKeys.region); |
85 | 87 |
|
86 |
| - return loadPromise_; |
| 88 | + return googleMapsPromise; |
87 | 89 | };
|
0 commit comments