├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── index.js
├── package.json
├── test.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | notifications:
2 | email: false
3 |
4 | services:
5 | - xvfb
6 |
7 | language: node_js
8 |
9 | node_js:
10 | - 'node'
11 |
12 | script:
13 | - yarn lint
14 | - yarn test
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 2.0.0
4 |
5 | - Bump dependencies
6 | - Use modern syntax, namely let/const and default parameters
7 |
8 | ## 1.3.1
9 |
10 | - Support repeated/duplicate calls by caching and returning the initial Promise
11 |
12 | ## 1.3.0
13 |
14 | - Return `google.maps` if previously-loaded
15 |
16 | ## 1.2.0
17 |
18 | - Remove check on `window`
19 | - Add `standard`
20 | - Bump dependencies
21 |
22 | ## 1.1.0
23 |
24 | - Add a simple CodePen
25 | - Use `prettier-standard`
26 | - Add a `weight` script
27 |
28 | ## 1.0.1
29 |
30 | - Add `channel` option
31 |
32 | ## 1.0.0
33 |
34 | - Major rewrite
35 | - Upgrade dependencies
36 |
37 | ## 0.0.3
38 |
39 | - Add `region` option
40 |
41 | ## 0.0.2
42 |
43 | - Add `language` option
44 |
45 | ## 0.0.1
46 |
47 | - Initial release
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Lim Yuan Qing
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # load-google-maps-api [](https://www.npmjs.org/package/load-google-maps-api) [](https://travis-ci.org/yuanqing/load-google-maps-api) [](https://bundlephobia.com/result?p=load-google-maps-api)
2 |
3 | > A lightweight Promise-returning helper for loading the [Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/)
4 |
5 | - The Promise’s fulfilled callback is passed the `google.maps` object
6 | - Optionally set a timeout, an API key, the language, [and more](#loadgooglemapsapioptions)
7 |
8 | ## Usage
9 |
10 | > [**Editable demo (CodePen)**](https://codepen.io/lyuanqing/pen/YeYBrN)
11 |
12 | ```js
13 | const loadGoogleMapsApi = require('load-google-maps-api')
14 |
15 | loadGoogleMapsApi().then(function (googleMaps) {
16 | new googleMaps.Map(document.querySelector('.map'), {
17 | center: {
18 | lat: 40.7484405,
19 | lng: -73.9944191
20 | },
21 | zoom: 12
22 | })
23 | }).catch(function (error) {
24 | console.error(error)
25 | })
26 | ```
27 |
28 | *N.B.* Just like the Google Maps API itself, this module is client-side only.
29 |
30 | ## Motivation
31 |
32 | [Without this module](https://developers.google.com/maps/documentation/javascript/tutorial#Loading_the_Maps_API), you would need to specify a named *global* callback, and pass said callback’s name as a parameter in the `script` tag’s `src`. For example:
33 |
34 | ```html
35 |
40 |
41 | ```
42 |
43 | This module abstracts this ceremony away, and fits better with modern bundlers like [Browserify](http://browserify.org/) or [Webpack](https://webpack.github.io/).
44 |
45 | ## API
46 |
47 | ```js
48 | const loadGoogleMapsApi = require('load-google-maps-api')
49 | ```
50 |
51 | ### loadGoogleMapsApi([options])
52 |
53 | Returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
54 |
55 | - **Fulfilled** if loading was successful. The fulfilled callback is passed the `google.maps` object. If `loadGoogleMapsApi` is called multiple times on a page, the fulfilled callback will be passed the previously-loaded `google.maps` object.
56 | - **Rejected** if we weren’t able to load the Google Maps API after `options.timeout`.
57 |
58 | See [Usage](#usage).
59 |
60 | `options` is an optional object literal:
61 |
62 | Key | Description | Default
63 | :--|:--|:--
64 | `apiUrl` | The Google Maps API `script` tag URL | `'https://maps.googleapis.com/maps/api/js'`
65 | `channel` | [Client usage reporting channel](https://developers.google.com/maps/premium/reports/usage-reports#channels) | `undefined`
66 | `client` | [Client ID](https://developers.google.com/maps/documentation/javascript/get-api-key#specifying-a-client-id-when-loading-the-api) | `undefined`
67 | `key` | [Your API key](https://developers.google.com/maps/documentation/javascript/get-api-key#step-2-add-the-api-key-to-your-application) | `undefined`
68 | `language` | [Language](https://developers.google.com/maps/documentation/javascript/localization#Language) | `undefined`
69 | `libraries` | [Supplemental libraries to load](https://developers.google.com/maps/documentation/javascript/libraries) | `[]`
70 | `region` | [Region](https://developers.google.com/maps/documentation/javascript/localization#Region) | `undefined`
71 | `timeout` | Time in milliseconds before rejecting the Promise | `10000`
72 | `v` | [API version](https://developers.google.com/maps/documentation/javascript/versions) | `undefined`
73 |
74 | ## Installation
75 |
76 | ```sh
77 | $ yarn add load-google-maps-api
78 | ```
79 |
80 | ## License
81 |
82 | [MIT](LICENSE.md)
83 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const API_URL = 'https://maps.googleapis.com/maps/api/js'
2 | const CALLBACK_NAME = '__googleMapsApiOnLoadCallback'
3 |
4 | const optionsKeys = ['channel', 'client', 'key', 'language', 'region', 'v']
5 |
6 | let promise = null
7 |
8 | module.exports = function (options = {}) {
9 | promise =
10 | promise ||
11 | new Promise(function (resolve, reject) {
12 | // Reject the promise after a timeout
13 | const timeoutId = setTimeout(function () {
14 | window[CALLBACK_NAME] = function () {} // Set the on load callback to a no-op
15 | reject(new Error('Could not load the Google Maps API'))
16 | }, options.timeout || 10000)
17 |
18 | // Hook up the on load callback
19 | window[CALLBACK_NAME] = function () {
20 | if (timeoutId !== null) {
21 | clearTimeout(timeoutId)
22 | }
23 | resolve(window.google.maps)
24 | delete window[CALLBACK_NAME]
25 | }
26 |
27 | // Prepare the `script` tag to be inserted into the page
28 | const scriptElement = document.createElement('script')
29 | const params = [`callback=${CALLBACK_NAME}`]
30 | optionsKeys.forEach(function (key) {
31 | if (options[key]) {
32 | params.push(`${key}=${options[key]}`)
33 | }
34 | })
35 | if (options.libraries && options.libraries.length) {
36 | params.push(`libraries=${options.libraries.join(',')}`)
37 | }
38 | scriptElement.src = `${options.apiUrl || API_URL}?${params.join('&')}`
39 |
40 | // Insert the `script` tag
41 | document.body.appendChild(scriptElement)
42 | })
43 | return promise
44 | }
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "load-google-maps-api",
3 | "version": "2.0.2",
4 | "description": "A lightweight Promise-returning helper for loading the Google Maps JavaScript API",
5 | "author": "Lim Yuan Qing",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/yuanqing/load-google-maps-api.git"
10 | },
11 | "devDependencies": {
12 | "browserify": "^16.5.0",
13 | "gzip-size-cli": "^3.0.0",
14 | "husky": "^4.2.3",
15 | "lint-staged": "^10.0.8",
16 | "prettier-standard": "^16.2.1",
17 | "rimraf": "^3.0.2",
18 | "standard": "^14.3.1",
19 | "tape": "^4.13.2",
20 | "tape-run": "^6.0.1",
21 | "terser": "^4.6.6"
22 | },
23 | "scripts": {
24 | "clean": "rimraf '*.log'",
25 | "fix": "prettier-standard '*.js'",
26 | "lint": "standard '*.js'",
27 | "test": "browserify test.js | tape-run",
28 | "weight": "terser index.js --compress --mangle --toplevel | gzip-size"
29 | },
30 | "husky": {
31 | "hooks": {
32 | "pre-commit": "lint-staged"
33 | }
34 | },
35 | "lint-staged": {
36 | "*.js": [
37 | "standard",
38 | "prettier-standard"
39 | ]
40 | },
41 | "files": [
42 | "index.js"
43 | ],
44 | "keywords": [
45 | "api",
46 | "google",
47 | "google-maps",
48 | "google-maps-api",
49 | "loader",
50 | "maps",
51 | "promise"
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | const test = require('tape')
2 | const loadGoogleMapsApi = require('./')
3 |
4 | test('resolves the promise to the `googleMaps` object', function (t) {
5 | t.plan(1)
6 | loadGoogleMapsApi().then(function (googleMaps) {
7 | t.true(typeof googleMaps.Map === 'function')
8 | }, t.fail)
9 | })
10 |
11 | test('resolves the promise to the `googleMaps` object, with support for duplicate calls', function (t) {
12 | t.plan(2)
13 | const promises = [loadGoogleMapsApi(), loadGoogleMapsApi()]
14 | Promise.all(promises).then(function (values) {
15 | t.equal(values[0], values[1])
16 | t.true(typeof values[0].Map === 'function')
17 | }, t.fail)
18 | })
19 |
--------------------------------------------------------------------------------