src/views/google-map/google-map.model.js
import { StringUtils } from '../../utils';
/**
* @external {google~Map} https://developers.google.com/maps/documentation/javascript/reference/map
*/
/**
* @external {google~LatLng} https://developers.google.com/maps/documentation/javascript/reference/coordinates
*/
/**
* @external {google~MapOptions} https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions
*/
/**
* @external {Element} https://developer.mozilla.org/en-US/docs/Web/API/Element
*/
/**
* Controls communication and handling of the Google Maps JavaScript API
*
* @example
* const googleMap = new GoogleMapModel(
* 'your-google-api-key',
* document.getElementById('some-map'),
* { center: { lat: 10.4657, lng: -66.8796 }, zoom: 12 }
* );
*/
class GoogleMapModel {
/**
* Initialises the map using Google Maps JavaScript API
*
* @param {String} apiKey - Google provides this in their console
* @param {Element} $el - The element where the map will be rendered
* @param {google~MapOptions} options - Google Maps options
*/
constructor(apiKey, $el, options) {
/**
* Google's API key
* @type {String}
*/
this.apiKey = apiKey;
/**
* The element where the map will be rendered
* @type {Element}
*/
this.$el = $el;
/**
* Google Maps options
* @type {google~MapOptions}
*/
this.options = options;
/**
* The map instance from Google Maps JavaScript API
* @type {google~Map}
*/
this.map = null;
}
/**
* The callback name attached to the Google's script library
*
* @return {String}
*/
static get INIT_CALLBACK() {
return 'initGoogleMapsLib';
}
/**
* The callback name triggered when the Google's script library is called
*
* @return {String}
*/
static get READY_CALLBACK() {
return 'googleMapsLibReady';
}
/**
* Initialises the Google Maps JavaScript library and safely calls {@link renderMap}
*
* There are three ways this can go down:
*
* 1. It's the first map added to the page and the script library has not been included yet
* 2. The library was included and INIT_CALLBACK already triggered
* 3. The library was included but it has not initialised yet
*
* @return {Promise<google~Map>}
*/
initGoogleMap() {
return new Promise((resolve) => {
if (!window[GoogleMapModel.INIT_CALLBACK]) {
window[GoogleMapModel.INIT_CALLBACK] = () => {
const initEvent = new Event(GoogleMapModel.READY_CALLBACK);
document.dispatchEvent(initEvent);
window[GoogleMapModel.READY_CALLBACK] = true;
resolve(this.renderMap());
};
const API_URL = 'https://maps.googleapis.com/maps/api/js';
const libScript = document.createElement('script');
const params = {
key: this.apiKey,
callback: GoogleMapModel.INIT_CALLBACK,
};
libScript.async = true;
libScript.defer = true;
libScript.src = `${API_URL}?${StringUtils.serialize(params)}`;
document.body.appendChild(libScript);
} else if (window[GoogleMapModel.READY_CALLBACK] === true) {
// Library is ready and event was already called
resolve(this.renderMap());
} else {
// Library was defined but not ready yet
document.addEventListener(GoogleMapModel.READY_CALLBACK, () => {
resolve(this.renderMap());
});
}
});
}
/**
* Adds map listener
*
* @see https://developers.google.com/maps/documentation/javascript/events
*
* @param {String} name - The event name
* @param {Function} callback - The event callback function
*/
addListener(name, callback) {
if (callback) {
this.map.addListener(name, callback);
}
}
/**
* Renders the map in the chosen {@link $el}
*
* @return {google~Map}
*/
renderMap() {
this.map = new window.google.maps.Map(this.$el, Object.assign({
clickableIcons: false,
center: { lat: 10.4657, lng: -66.8796 },
zoom: 12,
}, this.options));
return this.map;
}
}
export default GoogleMapModel;