maykinmedia/lazymaps

View on GitHub
src/lazymaps.js

Summary

Maintainability
A
0 mins
Test Coverage
import 'babel-polyfill';



/** {string} The Google Maps API endpoint. */
export const GOOGLE_MAPS_API = 'https://maps.googleapis.com/maps/api/js';


/**
 * Constructs a Google Map on node.
 * @class
 */
class GMap {
    /**
     * Constructor method.
     * @param {HTMLElement} node The Google Map to install.
     * @param {string} apiKey Our Google Maps JavaScript key.
     * @returns {Promise} Promise which resolves and calls the the createMap() function as son as THe Google API beocmes availalble.
     */
    constructor(node, apiKey) {
        if(!apiKey) {
            logKeyMissing();
            return;
        }

        /** {HTMLElement} Reference to the map container. */
        this.map = node;

        /** {Object} The Google Maps map instance. */
        this.googleMap = {};

        /** {Object[]} An array of optional markers. */
        this.markers = [];

        /** {Object[]} An array of optional info windows.. */
        this.infoWindows = [];

        /** {string} Parsed string containing the complete URL to the Google Maps API (including API key). */
        this.googleMapsSrc = GOOGLE_MAPS_API + '?key=' + apiKey;

        return this.setUpGoogleMaps();
    }

    /**
     * Installs Google Maps API.
     * Creates the map when ready.
     */
    setUpGoogleMaps() {
        return this.installGoogleMapsApi()
            .then(this.createMap.bind(this));
    }

    /**
     * Installs the Google Maps API if not already installed.
     * Polls for Google Maps api and resolves promise when available.
     * @returns {Promise} resolve when API is ready to use.
     */
    installGoogleMapsApi() {
        // Installs the Google Maps API if not already installed.
        if (!this.isGoogleMapsApiInstalled()) {
            let script = document.createElement('script');
            script.src = this.googleMapsSrc;
            document.head.appendChild(script);
        }
        // Polls for Google Maps api and resolves promise when available.
        return new Promise((resolve) => {
            function pollGoogleObject() {
                if (window.google && window.google.maps) {
                    resolve();
                    return;
                }
                setTimeout(pollGoogleObject, 100);
            }

            setTimeout(pollGoogleObject, 100);
        });
    }

    /**
     * Returns whether the Google Maps API is installed.
     * @returns {boolean}
     */
    isGoogleMapsApiInstalled() {
        return !![...document.scripts].find(script => script.src === this.googleMapsSrc);
    }

    /**
     * Creates the Google Maps instance.
     * @returns {Promise}
     */
    createMap() {
        return new Promise((resolve, reject) => {

            if (!(this.map.dataset.coordinates && this.map.dataset.zoom)) {
                logKeyMissing();
                reject();
            }

            let lat = parseFloat(this.map.dataset.coordinates.split(',')[0]);
            let lng = parseFloat(this.map.dataset.coordinates.split(',')[1]);
            let zoom = parseInt(this.map.dataset.zoom);
            let markers = (this.map.dataset.markers) ? JSON.parse(this.map.dataset.markers) : [];
            let disableDefaultUI = this.map.dataset.disableDefaultUi || false;
            let disableInfoWindows = this.map.dataset.disableInfoWindows || false;
            let infoWindowConfig = (this.map.dataset.infowindow) ? JSON.parse(this.map.dataset.infowindow) : {};

            if (!lat || !lng || !zoom) {
                console.warn('Parsing failed, please provide data-coordinates as commas separated lat/lng pair and data-zoom as number.');
                reject();
            }

            this.googleMap = new google.maps.Map(this.map, {
                center: {lat, lng},
                zoom,
                disableDefaultUI
            });

            this.markers = markers.map(marker => {
                let lat = parseFloat(marker.latitude);
                let lng = parseFloat(marker.longitude);

                let mapMarker = new google.maps.Marker({
                    map: this.googleMap,
                    position: {lat, lng},
                    title: marker.title,
                    optimized: false,  // IE
                });

                if (!disableInfoWindows) {
                    let infoWindow = new google.maps.InfoWindow(infoWindowConfig);
                    infoWindow.setContent(marker.html || `<h1>${marker.title}</h1><p>${marker.description}</p>`);

                    mapMarker.addListener('click', () => {  // jshint ignore:line
                        infoWindow.open(this.googleMap, mapMarker);
                    });

                    this.infoWindows.push(infoWindow)
                }

                // if (this.map.dataset.markerIcon) {
                //     tempMarker.icon = {
                //         url: this.map.dataset.markerIcon,
                //         scaledSize: new google.maps.Size(16, 27),  // FIXME
                //     };
                // }
                return mapMarker;
            });

            resolve(this);
        });
    }
}


/**
 * Creates new GMap() for every element matching selector.
 * @param {string} selector
 * @param {string} apiKey
 * @returns {Promise[]}
 */
function lazymaps(selector, apiKey) {
    if(!apiKey) {
        logKeyMissing();
        return;
    }
    return [...document.querySelectorAll(selector)].map(gmap => new GMap(gmap, apiKey));
}


/**
 * Logs a message indicating that no API key is provided.
 * @private
 */
function logKeyMissing() {
    console.warn('No Google Maps API key provided, please pass key as second argument.')
}


export default GMap;
export { GMap, lazymaps };