依赖文件
This commit is contained in:
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
# Google Maps JavaScript API Loader
|
||||
|
||||
[](https://www.npmjs.com/package/@googlemaps/js-api-loader)
|
||||

|
||||

|
||||
[](https://codecov.io/gh/googlemaps/js-api-loader)
|
||||

|
||||
[](https://github.com/semantic-release/semantic-release)
|
||||
[](https://discord.gg/jRteCzP)
|
||||
|
||||
## Description
|
||||
Load the Google Maps JavaScript API script dynamically. This takes inspiration from the [google-maps](https://www.npmjs.com/package/google-maps) npm package but updates it with ES6, Promises, and TypeScript.
|
||||
|
||||
## Install
|
||||
|
||||
Available via npm as the package [@googlemaps/js-api-loader](https://www.npmjs.com/package/@googlemaps/js-api-loader).
|
||||
|
||||
```sh
|
||||
npm i @googlemaps/js-api-loader
|
||||
```
|
||||
|
||||
Alternatively you may add the umd package directly to the html document using the unpkg link.
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/@googlemaps/js-api-loader@1.x/dist/index.min.js"></script>
|
||||
```
|
||||
|
||||
When adding via unpkg, the loader can be accessed at `google.maps.plugins.loader.Loader`.
|
||||
|
||||
### TypeScript
|
||||
|
||||
TypeScript users need to install the following types package.
|
||||
|
||||
```sh
|
||||
npm i -D @types/google.maps
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The reference documentation can be found at this [link](https://googlemaps.github.io/js-api-loader/index.html). The Google Maps JavaScript API [documentation](https://developers.google.com/maps/documentation/javascript/tutorial) is the authoritative source for the loader options.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
import { Loader } from '@googlemaps/js-api-loader';
|
||||
|
||||
const loader = new Loader({
|
||||
apiKey: "",
|
||||
version: "weekly",
|
||||
libraries: ["places"]
|
||||
});
|
||||
|
||||
const mapOptions = {
|
||||
center: {
|
||||
lat: 0,
|
||||
lng: 0
|
||||
},
|
||||
zoom: 4
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
Using a promise for a specific library.
|
||||
|
||||
```javascript
|
||||
// Promise for a specific library
|
||||
loader
|
||||
.importLibrary('maps')
|
||||
.then(({Map}) => {
|
||||
new Map(document.getElementById("map"), mapOptions);
|
||||
})
|
||||
.catch((e) => {
|
||||
// do something
|
||||
});
|
||||
```
|
||||
|
||||
Using a promise for when the script has loaded.
|
||||
|
||||
```javascript
|
||||
// Promise
|
||||
loader
|
||||
.load()
|
||||
.then((google) => {
|
||||
new google.maps.Map(document.getElementById("map"), mapOptions);
|
||||
})
|
||||
.catch(e => {
|
||||
// do something
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, if you want to use a callback.
|
||||
|
||||
```javascript
|
||||
// Callback
|
||||
loader.loadCallback(e => {
|
||||
if (e) {
|
||||
console.log(e);
|
||||
} else {
|
||||
new google.maps.Map(document.getElementById("map"), mapOptions);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
View the package in action [here](https://googlemaps.github.io/js-api-loader/examples/index.html).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
This library is community supported. We're comfortable enough with the stability and features of
|
||||
the library that we want you to build real production applications on it.
|
||||
|
||||
If you find a bug, or have a feature suggestion, please [log an issue][issues]. If you'd like to
|
||||
contribute, please read [How to Contribute][contrib].
|
||||
|
||||
[issues]: https://github.com/googlemaps/js-api-loader/issues
|
||||
[contrib]: https://github.com/googlemaps/js-api-loader/blob/main/CONTRIBUTING.md
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* Copyright 2019 Google LLC. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at.
|
||||
*
|
||||
* Http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/// <reference types="google.maps" />
|
||||
export declare const DEFAULT_ID = "__googleMapsScriptId";
|
||||
export type Library = "core" | "maps" | "places" | "geocoding" | "routes" | "marker" | "geometry" | "elevation" | "streetView" | "journeySharing" | "drawing" | "visualization";
|
||||
export type Libraries = Library[];
|
||||
/**
|
||||
* The Google Maps JavaScript API
|
||||
* [documentation](https://developers.google.com/maps/documentation/javascript/tutorial)
|
||||
* is the authoritative source for [[LoaderOptions]].
|
||||
/**
|
||||
* Loader options
|
||||
*/
|
||||
export interface LoaderOptions {
|
||||
/**
|
||||
* See https://developers.google.com/maps/documentation/javascript/get-api-key.
|
||||
*/
|
||||
apiKey: string;
|
||||
/**
|
||||
* @deprecated See https://developers.google.com/maps/premium/overview.
|
||||
*/
|
||||
channel?: string;
|
||||
/**
|
||||
* @deprecated See https://developers.google.com/maps/premium/overview, use `apiKey` instead.
|
||||
*/
|
||||
client?: string;
|
||||
/**
|
||||
* In your application you can specify release channels or version numbers:
|
||||
*
|
||||
* The weekly version is specified with `version=weekly`. This version is
|
||||
* updated once per week, and is the most current.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly'});
|
||||
* ```
|
||||
*
|
||||
* The quarterly version is specified with `version=quarterly`. This version
|
||||
* is updated once per quarter, and is the most predictable.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'quarterly'});
|
||||
* ```
|
||||
*
|
||||
* The version number is specified with `version=n.nn`. You can choose
|
||||
* `version=3.40`, `version=3.39`, or `version=3.38`. Version numbers are
|
||||
* updated once per quarter.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: '3.40'});
|
||||
* ```
|
||||
*
|
||||
* If you do not explicitly specify a version, you will receive the
|
||||
* weekly version by default.
|
||||
*/
|
||||
version?: string;
|
||||
/**
|
||||
* The id of the script tag. Before adding a new script, the Loader will check for an existing one.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* When loading the Maps JavaScript API via the URL you may optionally load
|
||||
* additional libraries through use of the libraries URL parameter. Libraries
|
||||
* are modules of code that provide additional functionality to the main Maps
|
||||
* JavaScript API but are not loaded unless you specifically request them.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({
|
||||
* apiKey,
|
||||
* libraries: ['drawing', 'geometry', 'places', 'visualization'],
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Set the [list of libraries](https://developers.google.com/maps/documentation/javascript/libraries) for more options.
|
||||
*/
|
||||
libraries?: Libraries;
|
||||
/**
|
||||
* By default, the Maps JavaScript API uses the user's preferred language
|
||||
* setting as specified in the browser, when displaying textual information
|
||||
* such as the names for controls, copyright notices, driving directions and
|
||||
* labels on maps. In most cases, it's preferable to respect the browser
|
||||
* setting. However, if you want the Maps JavaScript API to ignore the
|
||||
* browser's language setting, you can force it to display information in a
|
||||
* particular language when loading the Maps JavaScript API code.
|
||||
*
|
||||
* For example, the following example localizes the language to Japan:
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, language: 'ja', region: 'JP'});
|
||||
* ```
|
||||
*
|
||||
* See the [list of supported
|
||||
* languages](https://developers.google.com/maps/faq#languagesupport). Note
|
||||
* that new languages are added often, so this list may not be exhaustive.
|
||||
*
|
||||
*/
|
||||
language?: string;
|
||||
/**
|
||||
* When you load the Maps JavaScript API from maps.googleapis.com it applies a
|
||||
* default bias for application behavior towards the United States. If you
|
||||
* want to alter your application to serve different map tiles or bias the
|
||||
* application (such as biasing geocoding results towards the region), you can
|
||||
* override this default behavior by adding a region parameter when loading
|
||||
* the Maps JavaScript API code.
|
||||
*
|
||||
* The region parameter accepts Unicode region subtag identifiers which
|
||||
* (generally) have a one-to-one mapping to country code Top-Level Domains
|
||||
* (ccTLDs). Most Unicode region identifiers are identical to ISO 3166-1
|
||||
* codes, with some notable exceptions. For example, Great Britain's ccTLD is
|
||||
* "uk" (corresponding to the domain .co.uk) while its region identifier is
|
||||
* "GB."
|
||||
*
|
||||
* For example, the following example localizes the map to the United Kingdom:
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, region: 'GB'});
|
||||
* ```
|
||||
*/
|
||||
region?: string;
|
||||
/**
|
||||
* @deprecated Passing `mapIds` is no longer required in the script tag.
|
||||
*/
|
||||
mapIds?: string[];
|
||||
/**
|
||||
* Use a custom url and path to load the Google Maps API script.
|
||||
*/
|
||||
url?: string;
|
||||
/**
|
||||
* Use a cryptographic nonce attribute.
|
||||
*/
|
||||
nonce?: string;
|
||||
/**
|
||||
* The number of script load retries.
|
||||
*/
|
||||
retries?: number;
|
||||
/**
|
||||
* Maps JS customers can configure HTTP Referrer Restrictions in the Cloud
|
||||
* Console to limit which URLs are allowed to use a particular API Key. By
|
||||
* default, these restrictions can be configured to allow only certain paths
|
||||
* to use an API Key. If any URL on the same domain or origin may use the API
|
||||
* Key, you can set `auth_referrer_policy=origin` to limit the amount of data
|
||||
* sent when authorizing requests from the Maps JavaScript API. This is
|
||||
* available starting in version 3.46. When this parameter is specified and
|
||||
* HTTP Referrer Restrictions are enabled on Cloud Console, Maps JavaScript
|
||||
* API will only be able to load if there is an HTTP Referrer Restriction that
|
||||
* matches the current website's domain without a path specified.
|
||||
*/
|
||||
authReferrerPolicy?: "origin";
|
||||
}
|
||||
/**
|
||||
* The status of the [[Loader]].
|
||||
*/
|
||||
export declare enum LoaderStatus {
|
||||
INITIALIZED = 0,
|
||||
LOADING = 1,
|
||||
SUCCESS = 2,
|
||||
FAILURE = 3
|
||||
}
|
||||
/**
|
||||
* [[Loader]] makes it easier to add Google Maps JavaScript API to your application
|
||||
* dynamically using
|
||||
* [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
* It works by dynamically creating and appending a script node to the the
|
||||
* document head and wrapping the callback function so as to return a promise.
|
||||
*
|
||||
* ```
|
||||
* const loader = new Loader({
|
||||
* apiKey: "",
|
||||
* version: "weekly",
|
||||
* libraries: ["places"]
|
||||
* });
|
||||
*
|
||||
* loader.load().then((google) => {
|
||||
* const map = new google.maps.Map(...)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export declare class Loader {
|
||||
private static instance;
|
||||
/**
|
||||
* See [[LoaderOptions.version]]
|
||||
*/
|
||||
readonly version: string;
|
||||
/**
|
||||
* See [[LoaderOptions.apiKey]]
|
||||
*/
|
||||
readonly apiKey: string;
|
||||
/**
|
||||
* See [[LoaderOptions.channel]]
|
||||
*/
|
||||
readonly channel: string;
|
||||
/**
|
||||
* See [[LoaderOptions.client]]
|
||||
*/
|
||||
readonly client: string;
|
||||
/**
|
||||
* See [[LoaderOptions.id]]
|
||||
*/
|
||||
readonly id: string;
|
||||
/**
|
||||
* See [[LoaderOptions.libraries]]
|
||||
*/
|
||||
readonly libraries: Libraries;
|
||||
/**
|
||||
* See [[LoaderOptions.language]]
|
||||
*/
|
||||
readonly language: string;
|
||||
/**
|
||||
* See [[LoaderOptions.region]]
|
||||
*/
|
||||
readonly region: string;
|
||||
/**
|
||||
* See [[LoaderOptions.mapIds]]
|
||||
*/
|
||||
readonly mapIds: string[];
|
||||
/**
|
||||
* See [[LoaderOptions.nonce]]
|
||||
*/
|
||||
readonly nonce: string | null;
|
||||
/**
|
||||
* See [[LoaderOptions.retries]]
|
||||
*/
|
||||
readonly retries: number;
|
||||
/**
|
||||
* See [[LoaderOptions.url]]
|
||||
*/
|
||||
readonly url: string;
|
||||
/**
|
||||
* See [[LoaderOptions.authReferrerPolicy]]
|
||||
*/
|
||||
readonly authReferrerPolicy: "origin";
|
||||
private callbacks;
|
||||
private done;
|
||||
private loading;
|
||||
private onerrorEvent;
|
||||
private errors;
|
||||
/**
|
||||
* Creates an instance of Loader using [[LoaderOptions]]. No defaults are set
|
||||
* using this library, instead the defaults are set by the Google Maps
|
||||
* JavaScript API server.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly', libraries: ['places']});
|
||||
* ```
|
||||
*/
|
||||
constructor({ apiKey, authReferrerPolicy, channel, client, id, language, libraries, mapIds, nonce, region, retries, url, version, }: LoaderOptions);
|
||||
get options(): LoaderOptions;
|
||||
get status(): LoaderStatus;
|
||||
private get failed();
|
||||
/**
|
||||
* CreateUrl returns the Google Maps JavaScript API script url given the [[LoaderOptions]].
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated
|
||||
*/
|
||||
createUrl(): string;
|
||||
deleteScript(): void;
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
load(): Promise<typeof google>;
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadPromise(): Promise<typeof google>;
|
||||
/**
|
||||
* See https://developers.google.com/maps/documentation/javascript/reference/top-level#google.maps.importLibrary
|
||||
*/
|
||||
importLibrary(name: "core"): Promise<google.maps.CoreLibrary>;
|
||||
importLibrary(name: "maps"): Promise<google.maps.MapsLibrary>;
|
||||
importLibrary(name: "places"): Promise<google.maps.PlacesLibrary>;
|
||||
importLibrary(name: "geocoding"): Promise<google.maps.GeocodingLibrary>;
|
||||
importLibrary(name: "routes"): Promise<google.maps.RoutesLibrary>;
|
||||
importLibrary(name: "marker"): Promise<google.maps.MarkerLibrary>;
|
||||
importLibrary(name: "geometry"): Promise<google.maps.GeometryLibrary>;
|
||||
importLibrary(name: "elevation"): Promise<google.maps.ElevationLibrary>;
|
||||
importLibrary(name: "streetView"): Promise<google.maps.StreetViewLibrary>;
|
||||
importLibrary(name: "journeySharing"): Promise<google.maps.JourneySharingLibrary>;
|
||||
importLibrary(name: "drawing"): Promise<google.maps.DrawingLibrary>;
|
||||
importLibrary(name: "visualization"): Promise<google.maps.VisualizationLibrary>;
|
||||
importLibrary(name: Library): Promise<unknown>;
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script with a callback.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadCallback(fn: (e: ErrorEvent) => void): void;
|
||||
/**
|
||||
* Set the script on document.
|
||||
*/
|
||||
private setScript;
|
||||
/**
|
||||
* Reset the loader state.
|
||||
*/
|
||||
private reset;
|
||||
private resetIfRetryingFailed;
|
||||
private loadErrorCallback;
|
||||
private callback;
|
||||
private execute;
|
||||
}
|
||||
+450
@@ -0,0 +1,450 @@
|
||||
this.google = this.google || {};
|
||||
this.google.maps = this.google.maps || {};
|
||||
this.google.maps.plugins = this.google.maps.plugins || {};
|
||||
this.google.maps.plugins.loader = (function (exports) {
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise, SuppressedError, Symbol */
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) {
|
||||
return value instanceof P ? value : new P(function (resolve) {
|
||||
resolve(value);
|
||||
});
|
||||
}
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) {
|
||||
try {
|
||||
step(generator.next(value));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
function rejected(value) {
|
||||
try {
|
||||
step(generator["throw"](value));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
function step(result) {
|
||||
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
||||
}
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
}
|
||||
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
||||
var e = new Error(message);
|
||||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
||||
};
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
// do not edit .js files directly - edit src/index.jst
|
||||
|
||||
var fastDeepEqual = function equal(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a && b && typeof a == 'object' && typeof b == 'object') {
|
||||
if (a.constructor !== b.constructor) return false;
|
||||
var length, i, keys;
|
||||
if (Array.isArray(a)) {
|
||||
length = a.length;
|
||||
if (length != b.length) return false;
|
||||
for (i = length; i-- !== 0;) if (!equal(a[i], b[i])) return false;
|
||||
return true;
|
||||
}
|
||||
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
|
||||
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
||||
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
||||
keys = Object.keys(a);
|
||||
length = keys.length;
|
||||
if (length !== Object.keys(b).length) return false;
|
||||
for (i = length; i-- !== 0;) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
|
||||
for (i = length; i-- !== 0;) {
|
||||
var key = keys[i];
|
||||
if (!equal(a[key], b[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// true if both NaN, false otherwise
|
||||
return a !== a && b !== b;
|
||||
};
|
||||
var isEqual = /*@__PURE__*/getDefaultExportFromCjs(fastDeepEqual);
|
||||
|
||||
/**
|
||||
* Copyright 2019 Google LLC. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at.
|
||||
*
|
||||
* Http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const DEFAULT_ID = "__googleMapsScriptId";
|
||||
/**
|
||||
* The status of the [[Loader]].
|
||||
*/
|
||||
exports.LoaderStatus = void 0;
|
||||
(function (LoaderStatus) {
|
||||
LoaderStatus[LoaderStatus["INITIALIZED"] = 0] = "INITIALIZED";
|
||||
LoaderStatus[LoaderStatus["LOADING"] = 1] = "LOADING";
|
||||
LoaderStatus[LoaderStatus["SUCCESS"] = 2] = "SUCCESS";
|
||||
LoaderStatus[LoaderStatus["FAILURE"] = 3] = "FAILURE";
|
||||
})(exports.LoaderStatus || (exports.LoaderStatus = {}));
|
||||
/**
|
||||
* [[Loader]] makes it easier to add Google Maps JavaScript API to your application
|
||||
* dynamically using
|
||||
* [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
* It works by dynamically creating and appending a script node to the the
|
||||
* document head and wrapping the callback function so as to return a promise.
|
||||
*
|
||||
* ```
|
||||
* const loader = new Loader({
|
||||
* apiKey: "",
|
||||
* version: "weekly",
|
||||
* libraries: ["places"]
|
||||
* });
|
||||
*
|
||||
* loader.load().then((google) => {
|
||||
* const map = new google.maps.Map(...)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
class Loader {
|
||||
/**
|
||||
* Creates an instance of Loader using [[LoaderOptions]]. No defaults are set
|
||||
* using this library, instead the defaults are set by the Google Maps
|
||||
* JavaScript API server.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly', libraries: ['places']});
|
||||
* ```
|
||||
*/
|
||||
constructor(_ref) {
|
||||
let {
|
||||
apiKey,
|
||||
authReferrerPolicy,
|
||||
channel,
|
||||
client,
|
||||
id = DEFAULT_ID,
|
||||
language,
|
||||
libraries = [],
|
||||
mapIds,
|
||||
nonce,
|
||||
region,
|
||||
retries = 3,
|
||||
url = "https://maps.googleapis.com/maps/api/js",
|
||||
version
|
||||
} = _ref;
|
||||
this.callbacks = [];
|
||||
this.done = false;
|
||||
this.loading = false;
|
||||
this.errors = [];
|
||||
this.apiKey = apiKey;
|
||||
this.authReferrerPolicy = authReferrerPolicy;
|
||||
this.channel = channel;
|
||||
this.client = client;
|
||||
this.id = id || DEFAULT_ID; // Do not allow empty string
|
||||
this.language = language;
|
||||
this.libraries = libraries;
|
||||
this.mapIds = mapIds;
|
||||
this.nonce = nonce;
|
||||
this.region = region;
|
||||
this.retries = retries;
|
||||
this.url = url;
|
||||
this.version = version;
|
||||
if (Loader.instance) {
|
||||
if (!isEqual(this.options, Loader.instance.options)) {
|
||||
throw new Error(`Loader must not be called again with different options. ${JSON.stringify(this.options)} !== ${JSON.stringify(Loader.instance.options)}`);
|
||||
}
|
||||
return Loader.instance;
|
||||
}
|
||||
Loader.instance = this;
|
||||
}
|
||||
get options() {
|
||||
return {
|
||||
version: this.version,
|
||||
apiKey: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
id: this.id,
|
||||
libraries: this.libraries,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
mapIds: this.mapIds,
|
||||
nonce: this.nonce,
|
||||
url: this.url,
|
||||
authReferrerPolicy: this.authReferrerPolicy
|
||||
};
|
||||
}
|
||||
get status() {
|
||||
if (this.errors.length) {
|
||||
return exports.LoaderStatus.FAILURE;
|
||||
}
|
||||
if (this.done) {
|
||||
return exports.LoaderStatus.SUCCESS;
|
||||
}
|
||||
if (this.loading) {
|
||||
return exports.LoaderStatus.LOADING;
|
||||
}
|
||||
return exports.LoaderStatus.INITIALIZED;
|
||||
}
|
||||
get failed() {
|
||||
return this.done && !this.loading && this.errors.length >= this.retries + 1;
|
||||
}
|
||||
/**
|
||||
* CreateUrl returns the Google Maps JavaScript API script url given the [[LoaderOptions]].
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated
|
||||
*/
|
||||
createUrl() {
|
||||
let url = this.url;
|
||||
url += `?callback=__googleMapsCallback&loading=async`;
|
||||
if (this.apiKey) {
|
||||
url += `&key=${this.apiKey}`;
|
||||
}
|
||||
if (this.channel) {
|
||||
url += `&channel=${this.channel}`;
|
||||
}
|
||||
if (this.client) {
|
||||
url += `&client=${this.client}`;
|
||||
}
|
||||
if (this.libraries.length > 0) {
|
||||
url += `&libraries=${this.libraries.join(",")}`;
|
||||
}
|
||||
if (this.language) {
|
||||
url += `&language=${this.language}`;
|
||||
}
|
||||
if (this.region) {
|
||||
url += `®ion=${this.region}`;
|
||||
}
|
||||
if (this.version) {
|
||||
url += `&v=${this.version}`;
|
||||
}
|
||||
if (this.mapIds) {
|
||||
url += `&map_ids=${this.mapIds.join(",")}`;
|
||||
}
|
||||
if (this.authReferrerPolicy) {
|
||||
url += `&auth_referrer_policy=${this.authReferrerPolicy}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
deleteScript() {
|
||||
const script = document.getElementById(this.id);
|
||||
if (script) {
|
||||
script.remove();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
load() {
|
||||
return this.loadPromise();
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadPromise() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loadCallback(err => {
|
||||
if (!err) {
|
||||
resolve(window.google);
|
||||
} else {
|
||||
reject(err.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
importLibrary(name) {
|
||||
this.execute();
|
||||
return google.maps.importLibrary(name);
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script with a callback.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadCallback(fn) {
|
||||
this.callbacks.push(fn);
|
||||
this.execute();
|
||||
}
|
||||
/**
|
||||
* Set the script on document.
|
||||
*/
|
||||
setScript() {
|
||||
var _a, _b;
|
||||
if (document.getElementById(this.id)) {
|
||||
// TODO wrap onerror callback for cases where the script was loaded elsewhere
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
key: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
libraries: this.libraries.length && this.libraries,
|
||||
v: this.version,
|
||||
mapIds: this.mapIds,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
authReferrerPolicy: this.authReferrerPolicy
|
||||
};
|
||||
// keep the URL minimal:
|
||||
Object.keys(params).forEach(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
key => !params[key] && delete params[key]);
|
||||
if (!((_b = (_a = window === null || window === void 0 ? void 0 : window.google) === null || _a === void 0 ? void 0 : _a.maps) === null || _b === void 0 ? void 0 : _b.importLibrary)) {
|
||||
// tweaked copy of https://developers.google.com/maps/documentation/javascript/load-maps-js-api#dynamic-library-import
|
||||
// which also sets the base url, the id, and the nonce
|
||||
/* eslint-disable */
|
||||
(g => {
|
||||
// @ts-ignore
|
||||
let h,
|
||||
a,
|
||||
k,
|
||||
p = "The Google Maps JavaScript API",
|
||||
c = "google",
|
||||
l = "importLibrary",
|
||||
q = "__ib__",
|
||||
m = document,
|
||||
b = window;
|
||||
// @ts-ignore
|
||||
b = b[c] || (b[c] = {});
|
||||
// @ts-ignore
|
||||
const d = b.maps || (b.maps = {}),
|
||||
r = new Set(),
|
||||
e = new URLSearchParams(),
|
||||
u = () =>
|
||||
// @ts-ignore
|
||||
h || (h = new Promise((f, n) => __awaiter(this, void 0, void 0, function* () {
|
||||
var _a;
|
||||
yield a = m.createElement("script");
|
||||
a.id = this.id;
|
||||
e.set("libraries", [...r] + "");
|
||||
// @ts-ignore
|
||||
for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]);
|
||||
e.set("callback", c + ".maps." + q);
|
||||
a.src = this.url + `?` + e;
|
||||
d[q] = f;
|
||||
a.onerror = () => h = n(Error(p + " could not load."));
|
||||
// @ts-ignore
|
||||
a.nonce = this.nonce || ((_a = m.querySelector("script[nonce]")) === null || _a === void 0 ? void 0 : _a.nonce) || "";
|
||||
m.head.append(a);
|
||||
})));
|
||||
// @ts-ignore
|
||||
d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = function (f) {
|
||||
for (var _len = arguments.length, n = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||||
n[_key - 1] = arguments[_key];
|
||||
}
|
||||
return r.add(f) && u().then(() => d[l](f, ...n));
|
||||
};
|
||||
})(params);
|
||||
/* eslint-enable */
|
||||
}
|
||||
// While most libraries populate the global namespace when loaded via bootstrap params,
|
||||
// this is not the case for "marker" when used with the inline bootstrap loader
|
||||
// (and maybe others in the future). So ensure there is an importLibrary for each:
|
||||
const libraryPromises = this.libraries.map(library => this.importLibrary(library));
|
||||
// ensure at least one library, to kick off loading...
|
||||
if (!libraryPromises.length) {
|
||||
libraryPromises.push(this.importLibrary("core"));
|
||||
}
|
||||
Promise.all(libraryPromises).then(() => this.callback(), error => {
|
||||
const event = new ErrorEvent("error", {
|
||||
error
|
||||
}); // for backwards compat
|
||||
this.loadErrorCallback(event);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Reset the loader state.
|
||||
*/
|
||||
reset() {
|
||||
this.deleteScript();
|
||||
this.done = false;
|
||||
this.loading = false;
|
||||
this.errors = [];
|
||||
this.onerrorEvent = null;
|
||||
}
|
||||
resetIfRetryingFailed() {
|
||||
if (this.failed) {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
loadErrorCallback(e) {
|
||||
this.errors.push(e);
|
||||
if (this.errors.length <= this.retries) {
|
||||
const delay = this.errors.length * Math.pow(2, this.errors.length);
|
||||
console.error(`Failed to load Google Maps script, retrying in ${delay} ms.`);
|
||||
setTimeout(() => {
|
||||
this.deleteScript();
|
||||
this.setScript();
|
||||
}, delay);
|
||||
} else {
|
||||
this.onerrorEvent = e;
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
callback() {
|
||||
this.done = true;
|
||||
this.loading = false;
|
||||
this.callbacks.forEach(cb => {
|
||||
cb(this.onerrorEvent);
|
||||
});
|
||||
this.callbacks = [];
|
||||
}
|
||||
execute() {
|
||||
this.resetIfRetryingFailed();
|
||||
if (this.loading) {
|
||||
// do nothing but wait
|
||||
return;
|
||||
}
|
||||
if (this.done) {
|
||||
this.callback();
|
||||
} else {
|
||||
// short circuit and warn if google.maps is already loaded
|
||||
if (window.google && window.google.maps && window.google.maps.version) {
|
||||
console.warn("Google Maps already loaded outside @googlemaps/js-api-loader. " + "This may result in undesirable behavior as options and script parameters may not match.");
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.setScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.DEFAULT_ID = DEFAULT_ID;
|
||||
exports.Loader = Loader;
|
||||
|
||||
return exports;
|
||||
|
||||
})({});
|
||||
//# sourceMappingURL=index.dev.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+410
@@ -0,0 +1,410 @@
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise, SuppressedError, Symbol */
|
||||
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
}
|
||||
|
||||
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
||||
var e = new Error(message);
|
||||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
||||
};
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
// do not edit .js files directly - edit src/index.jst
|
||||
|
||||
|
||||
|
||||
var fastDeepEqual = function equal(a, b) {
|
||||
if (a === b) return true;
|
||||
|
||||
if (a && b && typeof a == 'object' && typeof b == 'object') {
|
||||
if (a.constructor !== b.constructor) return false;
|
||||
|
||||
var length, i, keys;
|
||||
if (Array.isArray(a)) {
|
||||
length = a.length;
|
||||
if (length != b.length) return false;
|
||||
for (i = length; i-- !== 0;)
|
||||
if (!equal(a[i], b[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
|
||||
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
||||
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
||||
|
||||
keys = Object.keys(a);
|
||||
length = keys.length;
|
||||
if (length !== Object.keys(b).length) return false;
|
||||
|
||||
for (i = length; i-- !== 0;)
|
||||
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
|
||||
|
||||
for (i = length; i-- !== 0;) {
|
||||
var key = keys[i];
|
||||
|
||||
if (!equal(a[key], b[key])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// true if both NaN, false otherwise
|
||||
return a!==a && b!==b;
|
||||
};
|
||||
|
||||
var isEqual = /*@__PURE__*/getDefaultExportFromCjs(fastDeepEqual);
|
||||
|
||||
/**
|
||||
* Copyright 2019 Google LLC. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at.
|
||||
*
|
||||
* Http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const DEFAULT_ID = "__googleMapsScriptId";
|
||||
/**
|
||||
* The status of the [[Loader]].
|
||||
*/
|
||||
var LoaderStatus;
|
||||
(function (LoaderStatus) {
|
||||
LoaderStatus[LoaderStatus["INITIALIZED"] = 0] = "INITIALIZED";
|
||||
LoaderStatus[LoaderStatus["LOADING"] = 1] = "LOADING";
|
||||
LoaderStatus[LoaderStatus["SUCCESS"] = 2] = "SUCCESS";
|
||||
LoaderStatus[LoaderStatus["FAILURE"] = 3] = "FAILURE";
|
||||
})(LoaderStatus || (LoaderStatus = {}));
|
||||
/**
|
||||
* [[Loader]] makes it easier to add Google Maps JavaScript API to your application
|
||||
* dynamically using
|
||||
* [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
* It works by dynamically creating and appending a script node to the the
|
||||
* document head and wrapping the callback function so as to return a promise.
|
||||
*
|
||||
* ```
|
||||
* const loader = new Loader({
|
||||
* apiKey: "",
|
||||
* version: "weekly",
|
||||
* libraries: ["places"]
|
||||
* });
|
||||
*
|
||||
* loader.load().then((google) => {
|
||||
* const map = new google.maps.Map(...)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
class Loader {
|
||||
/**
|
||||
* Creates an instance of Loader using [[LoaderOptions]]. No defaults are set
|
||||
* using this library, instead the defaults are set by the Google Maps
|
||||
* JavaScript API server.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly', libraries: ['places']});
|
||||
* ```
|
||||
*/
|
||||
constructor({ apiKey, authReferrerPolicy, channel, client, id = DEFAULT_ID, language, libraries = [], mapIds, nonce, region, retries = 3, url = "https://maps.googleapis.com/maps/api/js", version, }) {
|
||||
this.callbacks = [];
|
||||
this.done = false;
|
||||
this.loading = false;
|
||||
this.errors = [];
|
||||
this.apiKey = apiKey;
|
||||
this.authReferrerPolicy = authReferrerPolicy;
|
||||
this.channel = channel;
|
||||
this.client = client;
|
||||
this.id = id || DEFAULT_ID; // Do not allow empty string
|
||||
this.language = language;
|
||||
this.libraries = libraries;
|
||||
this.mapIds = mapIds;
|
||||
this.nonce = nonce;
|
||||
this.region = region;
|
||||
this.retries = retries;
|
||||
this.url = url;
|
||||
this.version = version;
|
||||
if (Loader.instance) {
|
||||
if (!isEqual(this.options, Loader.instance.options)) {
|
||||
throw new Error(`Loader must not be called again with different options. ${JSON.stringify(this.options)} !== ${JSON.stringify(Loader.instance.options)}`);
|
||||
}
|
||||
return Loader.instance;
|
||||
}
|
||||
Loader.instance = this;
|
||||
}
|
||||
get options() {
|
||||
return {
|
||||
version: this.version,
|
||||
apiKey: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
id: this.id,
|
||||
libraries: this.libraries,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
mapIds: this.mapIds,
|
||||
nonce: this.nonce,
|
||||
url: this.url,
|
||||
authReferrerPolicy: this.authReferrerPolicy,
|
||||
};
|
||||
}
|
||||
get status() {
|
||||
if (this.errors.length) {
|
||||
return LoaderStatus.FAILURE;
|
||||
}
|
||||
if (this.done) {
|
||||
return LoaderStatus.SUCCESS;
|
||||
}
|
||||
if (this.loading) {
|
||||
return LoaderStatus.LOADING;
|
||||
}
|
||||
return LoaderStatus.INITIALIZED;
|
||||
}
|
||||
get failed() {
|
||||
return this.done && !this.loading && this.errors.length >= this.retries + 1;
|
||||
}
|
||||
/**
|
||||
* CreateUrl returns the Google Maps JavaScript API script url given the [[LoaderOptions]].
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated
|
||||
*/
|
||||
createUrl() {
|
||||
let url = this.url;
|
||||
url += `?callback=__googleMapsCallback&loading=async`;
|
||||
if (this.apiKey) {
|
||||
url += `&key=${this.apiKey}`;
|
||||
}
|
||||
if (this.channel) {
|
||||
url += `&channel=${this.channel}`;
|
||||
}
|
||||
if (this.client) {
|
||||
url += `&client=${this.client}`;
|
||||
}
|
||||
if (this.libraries.length > 0) {
|
||||
url += `&libraries=${this.libraries.join(",")}`;
|
||||
}
|
||||
if (this.language) {
|
||||
url += `&language=${this.language}`;
|
||||
}
|
||||
if (this.region) {
|
||||
url += `®ion=${this.region}`;
|
||||
}
|
||||
if (this.version) {
|
||||
url += `&v=${this.version}`;
|
||||
}
|
||||
if (this.mapIds) {
|
||||
url += `&map_ids=${this.mapIds.join(",")}`;
|
||||
}
|
||||
if (this.authReferrerPolicy) {
|
||||
url += `&auth_referrer_policy=${this.authReferrerPolicy}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
deleteScript() {
|
||||
const script = document.getElementById(this.id);
|
||||
if (script) {
|
||||
script.remove();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
load() {
|
||||
return this.loadPromise();
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadPromise() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loadCallback((err) => {
|
||||
if (!err) {
|
||||
resolve(window.google);
|
||||
}
|
||||
else {
|
||||
reject(err.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
importLibrary(name) {
|
||||
this.execute();
|
||||
return google.maps.importLibrary(name);
|
||||
}
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script with a callback.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
loadCallback(fn) {
|
||||
this.callbacks.push(fn);
|
||||
this.execute();
|
||||
}
|
||||
/**
|
||||
* Set the script on document.
|
||||
*/
|
||||
setScript() {
|
||||
var _a, _b;
|
||||
if (document.getElementById(this.id)) {
|
||||
// TODO wrap onerror callback for cases where the script was loaded elsewhere
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
key: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
libraries: this.libraries.length && this.libraries,
|
||||
v: this.version,
|
||||
mapIds: this.mapIds,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
authReferrerPolicy: this.authReferrerPolicy,
|
||||
};
|
||||
// keep the URL minimal:
|
||||
Object.keys(params).forEach(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(key) => !params[key] && delete params[key]);
|
||||
if (!((_b = (_a = window === null || window === void 0 ? void 0 : window.google) === null || _a === void 0 ? void 0 : _a.maps) === null || _b === void 0 ? void 0 : _b.importLibrary)) {
|
||||
// tweaked copy of https://developers.google.com/maps/documentation/javascript/load-maps-js-api#dynamic-library-import
|
||||
// which also sets the base url, the id, and the nonce
|
||||
/* eslint-disable */
|
||||
((g) => {
|
||||
// @ts-ignore
|
||||
let h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window;
|
||||
// @ts-ignore
|
||||
b = b[c] || (b[c] = {});
|
||||
// @ts-ignore
|
||||
const d = b.maps || (b.maps = {}), r = new Set(), e = new URLSearchParams(), u = () =>
|
||||
// @ts-ignore
|
||||
h || (h = new Promise((f, n) => __awaiter(this, void 0, void 0, function* () {
|
||||
var _a;
|
||||
yield (a = m.createElement("script"));
|
||||
a.id = this.id;
|
||||
e.set("libraries", [...r] + "");
|
||||
// @ts-ignore
|
||||
for (k in g)
|
||||
e.set(k.replace(/[A-Z]/g, (t) => "_" + t[0].toLowerCase()), g[k]);
|
||||
e.set("callback", c + ".maps." + q);
|
||||
a.src = this.url + `?` + e;
|
||||
d[q] = f;
|
||||
a.onerror = () => (h = n(Error(p + " could not load.")));
|
||||
// @ts-ignore
|
||||
a.nonce = this.nonce || ((_a = m.querySelector("script[nonce]")) === null || _a === void 0 ? void 0 : _a.nonce) || "";
|
||||
m.head.append(a);
|
||||
})));
|
||||
// @ts-ignore
|
||||
d[l] ? console.warn(p + " only loads once. Ignoring:", g) : (d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)));
|
||||
})(params);
|
||||
/* eslint-enable */
|
||||
}
|
||||
// While most libraries populate the global namespace when loaded via bootstrap params,
|
||||
// this is not the case for "marker" when used with the inline bootstrap loader
|
||||
// (and maybe others in the future). So ensure there is an importLibrary for each:
|
||||
const libraryPromises = this.libraries.map((library) => this.importLibrary(library));
|
||||
// ensure at least one library, to kick off loading...
|
||||
if (!libraryPromises.length) {
|
||||
libraryPromises.push(this.importLibrary("core"));
|
||||
}
|
||||
Promise.all(libraryPromises).then(() => this.callback(), (error) => {
|
||||
const event = new ErrorEvent("error", { error }); // for backwards compat
|
||||
this.loadErrorCallback(event);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Reset the loader state.
|
||||
*/
|
||||
reset() {
|
||||
this.deleteScript();
|
||||
this.done = false;
|
||||
this.loading = false;
|
||||
this.errors = [];
|
||||
this.onerrorEvent = null;
|
||||
}
|
||||
resetIfRetryingFailed() {
|
||||
if (this.failed) {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
loadErrorCallback(e) {
|
||||
this.errors.push(e);
|
||||
if (this.errors.length <= this.retries) {
|
||||
const delay = this.errors.length * Math.pow(2, this.errors.length);
|
||||
console.error(`Failed to load Google Maps script, retrying in ${delay} ms.`);
|
||||
setTimeout(() => {
|
||||
this.deleteScript();
|
||||
this.setScript();
|
||||
}, delay);
|
||||
}
|
||||
else {
|
||||
this.onerrorEvent = e;
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
callback() {
|
||||
this.done = true;
|
||||
this.loading = false;
|
||||
this.callbacks.forEach((cb) => {
|
||||
cb(this.onerrorEvent);
|
||||
});
|
||||
this.callbacks = [];
|
||||
}
|
||||
execute() {
|
||||
this.resetIfRetryingFailed();
|
||||
if (this.loading) {
|
||||
// do nothing but wait
|
||||
return;
|
||||
}
|
||||
if (this.done) {
|
||||
this.callback();
|
||||
}
|
||||
else {
|
||||
// short circuit and warn if google.maps is already loaded
|
||||
if (window.google && window.google.maps && window.google.maps.version) {
|
||||
console.warn("Google Maps already loaded outside @googlemaps/js-api-loader. " +
|
||||
"This may result in undesirable behavior as options and script parameters may not match.");
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.setScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { DEFAULT_ID, Loader, LoaderStatus };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+73
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "@googlemaps/js-api-loader",
|
||||
"type": "commonjs",
|
||||
"version": "1.16.8",
|
||||
"description": "Wrapper for the loading of Google Maps JavaScript API script in the browser",
|
||||
"keywords": [
|
||||
"google",
|
||||
"maps"
|
||||
],
|
||||
"homepage": "https://github.com/googlemaps/js-api-loader",
|
||||
"bugs": {
|
||||
"url": "https://github.com/googlemaps/js-api-loader/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/googlemaps/js-api-loader.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "Justin Poehnelt",
|
||||
"main": "dist/index.umd.js",
|
||||
"unpkg": "dist/index.min.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/"
|
||||
],
|
||||
"scripts": {
|
||||
"docs": "typedoc src/index.ts && cp -r dist docs/dist && cp -r examples docs/examples",
|
||||
"format": "eslint . --fix",
|
||||
"lint": "eslint .",
|
||||
"prepare": "rm -rf dist && rollup -c",
|
||||
"test": "jest src/*",
|
||||
"test:e2e": "jest e2e/*"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@babel/runtime-corejs3": "^7.9.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-commonjs": "^25.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"@rollup/plugin-typescript": "^11.1.4",
|
||||
"@types/google.maps": "^3.53.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/selenium-webdriver": "^4.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"core-js": "^3.6.4",
|
||||
"eslint": "^8.12.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-jest": "^27.4.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"geckodriver": "^4.2.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"prettier": "^3.0.3",
|
||||
"rollup": "^4.6.1",
|
||||
"selenium-webdriver": "^4.0.0-alpha.7",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typedoc": "^0.25.1",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://wombat-dressing-room.appspot.com"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
}
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
/**
|
||||
* Copyright 2019 Google LLC. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at.
|
||||
*
|
||||
* Http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||
import { DEFAULT_ID, Loader, LoaderOptions, LoaderStatus } from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
afterEach(() => {
|
||||
document.getElementsByTagName("html")[0].innerHTML = "";
|
||||
delete Loader["instance"];
|
||||
if (window.google) delete window.google;
|
||||
});
|
||||
|
||||
test.each([
|
||||
[
|
||||
{},
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async",
|
||||
],
|
||||
[
|
||||
{ apiKey: "foo" },
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async&key=foo",
|
||||
],
|
||||
[
|
||||
{
|
||||
apiKey: "foo",
|
||||
version: "weekly",
|
||||
libraries: ["marker", "places"],
|
||||
language: "language",
|
||||
region: "region",
|
||||
},
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async&key=foo&libraries=marker,places&language=language®ion=region&v=weekly",
|
||||
],
|
||||
[
|
||||
{ mapIds: ["foo", "bar"] },
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async&map_ids=foo,bar",
|
||||
],
|
||||
[
|
||||
{ url: "https://example.com/js" },
|
||||
"https://example.com/js?callback=__googleMapsCallback&loading=async",
|
||||
],
|
||||
[
|
||||
{ client: "bar", channel: "foo" },
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async&channel=foo&client=bar",
|
||||
],
|
||||
[
|
||||
{ authReferrerPolicy: "origin" },
|
||||
"https://maps.googleapis.com/maps/api/js?callback=__googleMapsCallback&loading=async&auth_referrer_policy=origin",
|
||||
],
|
||||
])("createUrl is correct", (options: LoaderOptions, expected: string) => {
|
||||
const loader = new Loader(options);
|
||||
expect(loader.createUrl()).toEqual(expected);
|
||||
expect(loader.status).toBe(LoaderStatus.INITIALIZED);
|
||||
});
|
||||
|
||||
test("uses default id if empty string", () => {
|
||||
expect(new Loader({ apiKey: "foo", id: "" }).id).toBe(DEFAULT_ID);
|
||||
});
|
||||
|
||||
test("setScript adds a script to head with correct attributes", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
|
||||
expect(script.id).toEqual(loader.id);
|
||||
});
|
||||
|
||||
test("setScript adds a script with id", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", id: "bar" });
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
expect(script.localName).toEqual("script");
|
||||
expect(loader.id).toEqual("bar");
|
||||
expect(script.id).toEqual("bar");
|
||||
});
|
||||
|
||||
test("setScript does not add second script with same id", async () => {
|
||||
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
|
||||
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
|
||||
await 0;
|
||||
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
|
||||
await 0;
|
||||
|
||||
expect(document.head.childNodes.length).toBe(1);
|
||||
});
|
||||
|
||||
test("setScript adds a script to head with valid src", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
|
||||
expect(script.src).toEqual(
|
||||
"https://maps.googleapis.com/maps/api/js?libraries=core&key=foo&callback=google.maps.__ib__"
|
||||
);
|
||||
});
|
||||
|
||||
test("setScript adds a script to head with valid src with libraries", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", libraries: ["marker", "places"] });
|
||||
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
|
||||
expect(script.src).toEqual(
|
||||
"https://maps.googleapis.com/maps/api/js?libraries=marker%2Cplaces&key=foo&callback=google.maps.__ib__"
|
||||
);
|
||||
});
|
||||
|
||||
test("load should return a promise that resolves even if called twice", () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
loader.importLibrary = (() => Promise.resolve()) as any;
|
||||
|
||||
expect.assertions(1);
|
||||
const promise = Promise.all([loader.load(), loader.load()]).then(() => {
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
});
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
test("loadCallback callback should fire", () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
loader.importLibrary = (() => Promise.resolve()) as any;
|
||||
|
||||
expect.assertions(2);
|
||||
loader.loadCallback((e: Event) => {
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(e).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
test("script onerror should reject promise", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
|
||||
const rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
|
||||
loader["loadErrorCallback"](
|
||||
new ErrorEvent("ErrorEvent(", { error: new Error("") })
|
||||
);
|
||||
|
||||
await rejection;
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(loader["errors"].length).toBe(1);
|
||||
expect(loader.status).toBe(LoaderStatus.FAILURE);
|
||||
});
|
||||
|
||||
test("script onerror should reject promise with multiple loaders", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
const extraLoader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
|
||||
let rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
loader["loadErrorCallback"](
|
||||
new ErrorEvent("ErrorEvent(", { error: new Error("") })
|
||||
);
|
||||
|
||||
await rejection;
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(loader["onerrorEvent"]).toBeInstanceOf(ErrorEvent);
|
||||
|
||||
rejection = expect(extraLoader.load()).rejects.toBeInstanceOf(Error);
|
||||
loader["loadErrorCallback"](
|
||||
new ErrorEvent("ErrorEvent(", { error: new Error("") })
|
||||
);
|
||||
|
||||
await rejection;
|
||||
expect(extraLoader["done"]).toBeTruthy();
|
||||
expect(extraLoader["loading"]).toBeFalsy();
|
||||
});
|
||||
|
||||
test("script onerror should retry", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 1 });
|
||||
const deleteScript = jest.spyOn(loader, "deleteScript");
|
||||
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
|
||||
const rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
console.error = jest.fn();
|
||||
|
||||
// wait for the first failure
|
||||
await 0;
|
||||
await 0;
|
||||
expect(loader["errors"].length).toBe(1);
|
||||
// trigger the retry delay:
|
||||
jest.runAllTimers();
|
||||
|
||||
await rejection;
|
||||
expect(loader["errors"].length).toBe(2);
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(loader["failed"]).toBeTruthy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(deleteScript).toHaveBeenCalledTimes(1);
|
||||
expect(console.error).toHaveBeenCalledTimes(loader.retries);
|
||||
});
|
||||
|
||||
test("script onerror should reset retry mechanism with next loader", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 1 });
|
||||
const deleteScript = jest.spyOn(loader, "deleteScript");
|
||||
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
console.error = jest.fn();
|
||||
|
||||
let rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
// wait for the first first failure
|
||||
await 0;
|
||||
await 0;
|
||||
expect(loader["errors"].length).toBe(1);
|
||||
// trigger the retry delay:
|
||||
jest.runAllTimers();
|
||||
await rejection;
|
||||
|
||||
// try again...
|
||||
rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
expect(loader["done"]).toBeFalsy();
|
||||
expect(loader["failed"]).toBeFalsy();
|
||||
expect(loader["loading"]).toBeTruthy();
|
||||
expect(loader["errors"].length).toBe(0);
|
||||
|
||||
// wait for the second first failure
|
||||
await 0;
|
||||
await 0;
|
||||
expect(loader["errors"].length).toBe(1);
|
||||
// trigger the retry delay:
|
||||
jest.runAllTimers();
|
||||
|
||||
await rejection;
|
||||
expect(deleteScript).toHaveBeenCalledTimes(3);
|
||||
expect(console.error).toHaveBeenCalledTimes(loader.retries * 2);
|
||||
});
|
||||
|
||||
test("script onerror should not reset retry mechanism with parallel loaders", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 1 });
|
||||
const deleteScript = jest.spyOn(loader, "deleteScript");
|
||||
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
console.error = jest.fn();
|
||||
|
||||
const rejection1 = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
const rejection2 = expect(loader.load()).rejects.toBeInstanceOf(Error);
|
||||
// wait for the first first failure
|
||||
await 0;
|
||||
await 0;
|
||||
jest.runAllTimers();
|
||||
|
||||
await Promise.all([rejection1, rejection2]);
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(loader["errors"].length).toBe(2);
|
||||
expect(deleteScript).toHaveBeenCalledTimes(1);
|
||||
expect(console.error).toHaveBeenCalledTimes(loader.retries);
|
||||
});
|
||||
|
||||
test("reset should clear state", () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
const deleteScript = jest.spyOn(loader, "deleteScript");
|
||||
|
||||
loader["done"] = true;
|
||||
loader["loading"] = false;
|
||||
loader["errors"] = [new ErrorEvent("foo")];
|
||||
|
||||
loader["reset"]();
|
||||
|
||||
expect(loader["done"]).toBeFalsy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(loader["onerrorEvent"]).toBe(null);
|
||||
expect(deleteScript).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("failed getter should be correct", () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
|
||||
// initial
|
||||
expect(loader["failed"]).toBeFalsy();
|
||||
|
||||
// not done
|
||||
loader["done"] = false;
|
||||
loader["loading"] = false;
|
||||
loader["errors"] = [new ErrorEvent("foo")];
|
||||
expect(loader["failed"]).toBeFalsy();
|
||||
|
||||
// still loading
|
||||
loader["done"] = false;
|
||||
loader["loading"] = true;
|
||||
loader["errors"] = [new ErrorEvent("foo")];
|
||||
expect(loader["failed"]).toBeFalsy();
|
||||
|
||||
// no errors
|
||||
loader["done"] = true;
|
||||
loader["loading"] = false;
|
||||
loader["errors"] = [];
|
||||
expect(loader["failed"]).toBeFalsy();
|
||||
|
||||
// done with errors
|
||||
loader["done"] = true;
|
||||
loader["loading"] = false;
|
||||
loader["errors"] = [new ErrorEvent("foo")];
|
||||
expect(loader["failed"]).toBeTruthy();
|
||||
});
|
||||
|
||||
test("loader should not reset retry mechanism if successfully loaded", async () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
const deleteScript = jest.spyOn(loader, "deleteScript");
|
||||
loader.importLibrary = (() => Promise.resolve()) as any;
|
||||
|
||||
await expect(loader.load()).resolves.not.toBeUndefined();
|
||||
|
||||
expect(loader["done"]).toBeTruthy();
|
||||
expect(loader["loading"]).toBeFalsy();
|
||||
expect(deleteScript).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("singleton should be used", () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
const extraLoader = new Loader({ apiKey: "foo" });
|
||||
expect(extraLoader).toBe(loader);
|
||||
|
||||
loader["done"] = true;
|
||||
expect(extraLoader["done"]).toBe(loader["done"]);
|
||||
expect(loader.status).toBe(LoaderStatus.SUCCESS);
|
||||
});
|
||||
|
||||
test("singleton should throw with different options", () => {
|
||||
new Loader({ apiKey: "foo" });
|
||||
expect(() => {
|
||||
new Loader({ apiKey: "bar" });
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
test("loader should resolve immediately when successfully loaded", async () => {
|
||||
// use await/async pattern since the promise resolves without trigger
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
loader["done"] = true;
|
||||
// TODO causes warning
|
||||
window.google = { maps: { version: "3.*.*" } as any };
|
||||
await expect(loader.loadPromise()).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
test("loader should resolve immediately when failed loading", async () => {
|
||||
// use await/async pattern since the promise rejects without trigger
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
loader["done"] = true;
|
||||
loader["onerrorEvent"] = new ErrorEvent("ErrorEvent(", {
|
||||
error: new Error(""),
|
||||
});
|
||||
|
||||
await expect(loader.loadPromise()).rejects.toBeDefined();
|
||||
});
|
||||
|
||||
test("loader should wait if already loading", () => {
|
||||
const loader = new Loader({ apiKey: "foo", retries: 0 });
|
||||
loader["loading"] = true;
|
||||
expect(loader.status).toBe(LoaderStatus.LOADING);
|
||||
loader.load();
|
||||
});
|
||||
|
||||
test("setScript adds a nonce", async () => {
|
||||
const nonce = "bar";
|
||||
const loader = new Loader({ apiKey: "foo", nonce });
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
expect(script.nonce).toBe(nonce);
|
||||
});
|
||||
|
||||
test("loader should resolve immediately when google.maps defined", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
window.google = { maps: { version: "3.*.*" } as any };
|
||||
console.warn = jest.fn();
|
||||
await expect(loader.loadPromise()).resolves.toBeDefined();
|
||||
delete window.google;
|
||||
expect(console.warn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("loader should not warn if done and google.maps is defined", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
loader["done"] = true;
|
||||
window.google = { maps: { version: "3.*.*" } as any };
|
||||
console.warn = jest.fn();
|
||||
await expect(loader.loadPromise()).resolves.toBeDefined();
|
||||
delete window.google;
|
||||
expect(console.warn).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("deleteScript removes script tag from head", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
loader["setScript"]();
|
||||
await 0;
|
||||
expect(document.head.childNodes.length).toBe(1);
|
||||
loader.deleteScript();
|
||||
expect(document.head.childNodes.length).toBe(0);
|
||||
// should work without script existing
|
||||
loader.deleteScript();
|
||||
expect(document.head.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
test("importLibrary resolves correctly", async () => {
|
||||
window.google = { maps: {} } as any;
|
||||
google.maps.importLibrary = async (name) => ({ [name]: "fake" }) as any;
|
||||
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
const corePromise = loader.importLibrary("core");
|
||||
|
||||
const core = await corePromise;
|
||||
expect(core).toEqual({ core: "fake" });
|
||||
});
|
||||
|
||||
test("importLibrary resolves correctly without warning with sequential await", async () => {
|
||||
console.warn = jest.fn();
|
||||
window.google = { maps: {} } as any;
|
||||
google.maps.importLibrary = async (name) => {
|
||||
google.maps.version = "3.*.*";
|
||||
return { [name]: "fake" } as any;
|
||||
};
|
||||
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
const core = await loader.importLibrary("core");
|
||||
const marker = await loader.importLibrary("marker");
|
||||
|
||||
expect(console.warn).toHaveBeenCalledTimes(0);
|
||||
expect(core).toEqual({ core: "fake" });
|
||||
expect(marker).toEqual({ marker: "fake" });
|
||||
});
|
||||
|
||||
test("importLibrary can also set up bootstrap libraries (if bootstrap libraries empty)", async () => {
|
||||
const loader = new Loader({ apiKey: "foo" });
|
||||
loader.importLibrary("marker");
|
||||
loader.importLibrary("places");
|
||||
|
||||
await 0;
|
||||
|
||||
const script = document.head.childNodes[0] as HTMLScriptElement;
|
||||
|
||||
expect(script.src).toEqual(
|
||||
"https://maps.googleapis.com/maps/api/js?libraries=core%2Cmarker%2Cplaces&key=foo&callback=google.maps.__ib__"
|
||||
);
|
||||
});
|
||||
|
||||
test("importLibrary resolves correctly even with different bootstrap libraries", async () => {
|
||||
window.google = { maps: {} } as any;
|
||||
google.maps.importLibrary = async (name) => ({ [name]: "fake" }) as any;
|
||||
|
||||
const loader = new Loader({ apiKey: "foo", libraries: ["places"] });
|
||||
const corePromise = loader.importLibrary("core");
|
||||
|
||||
const core = await corePromise;
|
||||
expect(core).toEqual({ core: "fake" });
|
||||
expect(await loader.importLibrary("places")).toEqual({ places: "fake" });
|
||||
});
|
||||
+648
@@ -0,0 +1,648 @@
|
||||
/**
|
||||
* Copyright 2019 Google LLC. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at.
|
||||
*
|
||||
* Http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import isEqual from "fast-deep-equal";
|
||||
|
||||
export const DEFAULT_ID = "__googleMapsScriptId";
|
||||
|
||||
// https://developers.google.com/maps/documentation/javascript/libraries#libraries-for-dynamic-library-import
|
||||
export type Library =
|
||||
| "core"
|
||||
| "maps"
|
||||
| "places"
|
||||
| "geocoding"
|
||||
| "routes"
|
||||
| "marker"
|
||||
| "geometry"
|
||||
| "elevation"
|
||||
| "streetView"
|
||||
| "journeySharing"
|
||||
| "drawing"
|
||||
| "visualization";
|
||||
|
||||
export type Libraries = Library[];
|
||||
|
||||
/**
|
||||
* The Google Maps JavaScript API
|
||||
* [documentation](https://developers.google.com/maps/documentation/javascript/tutorial)
|
||||
* is the authoritative source for [[LoaderOptions]].
|
||||
/**
|
||||
* Loader options
|
||||
*/
|
||||
export interface LoaderOptions {
|
||||
/**
|
||||
* See https://developers.google.com/maps/documentation/javascript/get-api-key.
|
||||
*/
|
||||
apiKey: string;
|
||||
/**
|
||||
* @deprecated See https://developers.google.com/maps/premium/overview.
|
||||
*/
|
||||
channel?: string;
|
||||
/**
|
||||
* @deprecated See https://developers.google.com/maps/premium/overview, use `apiKey` instead.
|
||||
*/
|
||||
client?: string;
|
||||
/**
|
||||
* In your application you can specify release channels or version numbers:
|
||||
*
|
||||
* The weekly version is specified with `version=weekly`. This version is
|
||||
* updated once per week, and is the most current.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly'});
|
||||
* ```
|
||||
*
|
||||
* The quarterly version is specified with `version=quarterly`. This version
|
||||
* is updated once per quarter, and is the most predictable.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'quarterly'});
|
||||
* ```
|
||||
*
|
||||
* The version number is specified with `version=n.nn`. You can choose
|
||||
* `version=3.40`, `version=3.39`, or `version=3.38`. Version numbers are
|
||||
* updated once per quarter.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: '3.40'});
|
||||
* ```
|
||||
*
|
||||
* If you do not explicitly specify a version, you will receive the
|
||||
* weekly version by default.
|
||||
*/
|
||||
version?: string;
|
||||
/**
|
||||
* The id of the script tag. Before adding a new script, the Loader will check for an existing one.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* When loading the Maps JavaScript API via the URL you may optionally load
|
||||
* additional libraries through use of the libraries URL parameter. Libraries
|
||||
* are modules of code that provide additional functionality to the main Maps
|
||||
* JavaScript API but are not loaded unless you specifically request them.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({
|
||||
* apiKey,
|
||||
* libraries: ['drawing', 'geometry', 'places', 'visualization'],
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Set the [list of libraries](https://developers.google.com/maps/documentation/javascript/libraries) for more options.
|
||||
*/
|
||||
libraries?: Libraries;
|
||||
/**
|
||||
* By default, the Maps JavaScript API uses the user's preferred language
|
||||
* setting as specified in the browser, when displaying textual information
|
||||
* such as the names for controls, copyright notices, driving directions and
|
||||
* labels on maps. In most cases, it's preferable to respect the browser
|
||||
* setting. However, if you want the Maps JavaScript API to ignore the
|
||||
* browser's language setting, you can force it to display information in a
|
||||
* particular language when loading the Maps JavaScript API code.
|
||||
*
|
||||
* For example, the following example localizes the language to Japan:
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, language: 'ja', region: 'JP'});
|
||||
* ```
|
||||
*
|
||||
* See the [list of supported
|
||||
* languages](https://developers.google.com/maps/faq#languagesupport). Note
|
||||
* that new languages are added often, so this list may not be exhaustive.
|
||||
*
|
||||
*/
|
||||
language?: string;
|
||||
/**
|
||||
* When you load the Maps JavaScript API from maps.googleapis.com it applies a
|
||||
* default bias for application behavior towards the United States. If you
|
||||
* want to alter your application to serve different map tiles or bias the
|
||||
* application (such as biasing geocoding results towards the region), you can
|
||||
* override this default behavior by adding a region parameter when loading
|
||||
* the Maps JavaScript API code.
|
||||
*
|
||||
* The region parameter accepts Unicode region subtag identifiers which
|
||||
* (generally) have a one-to-one mapping to country code Top-Level Domains
|
||||
* (ccTLDs). Most Unicode region identifiers are identical to ISO 3166-1
|
||||
* codes, with some notable exceptions. For example, Great Britain's ccTLD is
|
||||
* "uk" (corresponding to the domain .co.uk) while its region identifier is
|
||||
* "GB."
|
||||
*
|
||||
* For example, the following example localizes the map to the United Kingdom:
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, region: 'GB'});
|
||||
* ```
|
||||
*/
|
||||
region?: string;
|
||||
/**
|
||||
* @deprecated Passing `mapIds` is no longer required in the script tag.
|
||||
*/
|
||||
mapIds?: string[];
|
||||
/**
|
||||
* Use a custom url and path to load the Google Maps API script.
|
||||
*/
|
||||
url?: string;
|
||||
/**
|
||||
* Use a cryptographic nonce attribute.
|
||||
*/
|
||||
nonce?: string;
|
||||
/**
|
||||
* The number of script load retries.
|
||||
*/
|
||||
retries?: number;
|
||||
/**
|
||||
* Maps JS customers can configure HTTP Referrer Restrictions in the Cloud
|
||||
* Console to limit which URLs are allowed to use a particular API Key. By
|
||||
* default, these restrictions can be configured to allow only certain paths
|
||||
* to use an API Key. If any URL on the same domain or origin may use the API
|
||||
* Key, you can set `auth_referrer_policy=origin` to limit the amount of data
|
||||
* sent when authorizing requests from the Maps JavaScript API. This is
|
||||
* available starting in version 3.46. When this parameter is specified and
|
||||
* HTTP Referrer Restrictions are enabled on Cloud Console, Maps JavaScript
|
||||
* API will only be able to load if there is an HTTP Referrer Restriction that
|
||||
* matches the current website's domain without a path specified.
|
||||
*/
|
||||
authReferrerPolicy?: "origin";
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of the [[Loader]].
|
||||
*/
|
||||
export enum LoaderStatus {
|
||||
INITIALIZED,
|
||||
LOADING,
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
}
|
||||
|
||||
/**
|
||||
* [[Loader]] makes it easier to add Google Maps JavaScript API to your application
|
||||
* dynamically using
|
||||
* [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
* It works by dynamically creating and appending a script node to the the
|
||||
* document head and wrapping the callback function so as to return a promise.
|
||||
*
|
||||
* ```
|
||||
* const loader = new Loader({
|
||||
* apiKey: "",
|
||||
* version: "weekly",
|
||||
* libraries: ["places"]
|
||||
* });
|
||||
*
|
||||
* loader.load().then((google) => {
|
||||
* const map = new google.maps.Map(...)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export class Loader {
|
||||
private static instance: Loader;
|
||||
/**
|
||||
* See [[LoaderOptions.version]]
|
||||
*/
|
||||
public readonly version: string;
|
||||
/**
|
||||
* See [[LoaderOptions.apiKey]]
|
||||
*/
|
||||
public readonly apiKey: string;
|
||||
/**
|
||||
* See [[LoaderOptions.channel]]
|
||||
*/
|
||||
public readonly channel: string;
|
||||
/**
|
||||
* See [[LoaderOptions.client]]
|
||||
*/
|
||||
public readonly client: string;
|
||||
/**
|
||||
* See [[LoaderOptions.id]]
|
||||
*/
|
||||
public readonly id: string;
|
||||
/**
|
||||
* See [[LoaderOptions.libraries]]
|
||||
*/
|
||||
public readonly libraries: Libraries;
|
||||
/**
|
||||
* See [[LoaderOptions.language]]
|
||||
*/
|
||||
public readonly language: string;
|
||||
|
||||
/**
|
||||
* See [[LoaderOptions.region]]
|
||||
*/
|
||||
public readonly region: string;
|
||||
|
||||
/**
|
||||
* See [[LoaderOptions.mapIds]]
|
||||
*/
|
||||
public readonly mapIds: string[];
|
||||
|
||||
/**
|
||||
* See [[LoaderOptions.nonce]]
|
||||
*/
|
||||
public readonly nonce: string | null;
|
||||
|
||||
/**
|
||||
* See [[LoaderOptions.retries]]
|
||||
*/
|
||||
public readonly retries: number;
|
||||
|
||||
/**
|
||||
* See [[LoaderOptions.url]]
|
||||
*/
|
||||
public readonly url: string;
|
||||
/**
|
||||
* See [[LoaderOptions.authReferrerPolicy]]
|
||||
*/
|
||||
public readonly authReferrerPolicy: "origin";
|
||||
|
||||
private callbacks: ((e: ErrorEvent) => void)[] = [];
|
||||
private done = false;
|
||||
private loading = false;
|
||||
private onerrorEvent: ErrorEvent;
|
||||
private errors: ErrorEvent[] = [];
|
||||
|
||||
/**
|
||||
* Creates an instance of Loader using [[LoaderOptions]]. No defaults are set
|
||||
* using this library, instead the defaults are set by the Google Maps
|
||||
* JavaScript API server.
|
||||
*
|
||||
* ```
|
||||
* const loader = Loader({apiKey, version: 'weekly', libraries: ['places']});
|
||||
* ```
|
||||
*/
|
||||
constructor({
|
||||
apiKey,
|
||||
authReferrerPolicy,
|
||||
channel,
|
||||
client,
|
||||
id = DEFAULT_ID,
|
||||
language,
|
||||
libraries = [],
|
||||
mapIds,
|
||||
nonce,
|
||||
region,
|
||||
retries = 3,
|
||||
url = "https://maps.googleapis.com/maps/api/js",
|
||||
version,
|
||||
}: LoaderOptions) {
|
||||
this.apiKey = apiKey;
|
||||
this.authReferrerPolicy = authReferrerPolicy;
|
||||
this.channel = channel;
|
||||
this.client = client;
|
||||
this.id = id || DEFAULT_ID; // Do not allow empty string
|
||||
this.language = language;
|
||||
this.libraries = libraries;
|
||||
this.mapIds = mapIds;
|
||||
this.nonce = nonce;
|
||||
this.region = region;
|
||||
this.retries = retries;
|
||||
this.url = url;
|
||||
this.version = version;
|
||||
|
||||
if (Loader.instance) {
|
||||
if (!isEqual(this.options, Loader.instance.options)) {
|
||||
throw new Error(
|
||||
`Loader must not be called again with different options. ${JSON.stringify(
|
||||
this.options
|
||||
)} !== ${JSON.stringify(Loader.instance.options)}`
|
||||
);
|
||||
}
|
||||
|
||||
return Loader.instance;
|
||||
}
|
||||
|
||||
Loader.instance = this;
|
||||
}
|
||||
|
||||
public get options(): LoaderOptions {
|
||||
return {
|
||||
version: this.version,
|
||||
apiKey: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
id: this.id,
|
||||
libraries: this.libraries,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
mapIds: this.mapIds,
|
||||
nonce: this.nonce,
|
||||
url: this.url,
|
||||
authReferrerPolicy: this.authReferrerPolicy,
|
||||
};
|
||||
}
|
||||
|
||||
public get status(): LoaderStatus {
|
||||
if (this.errors.length) {
|
||||
return LoaderStatus.FAILURE;
|
||||
}
|
||||
if (this.done) {
|
||||
return LoaderStatus.SUCCESS;
|
||||
}
|
||||
if (this.loading) {
|
||||
return LoaderStatus.LOADING;
|
||||
}
|
||||
return LoaderStatus.INITIALIZED;
|
||||
}
|
||||
|
||||
private get failed(): boolean {
|
||||
return this.done && !this.loading && this.errors.length >= this.retries + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateUrl returns the Google Maps JavaScript API script url given the [[LoaderOptions]].
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated
|
||||
*/
|
||||
public createUrl(): string {
|
||||
let url = this.url;
|
||||
|
||||
url += `?callback=__googleMapsCallback&loading=async`;
|
||||
|
||||
if (this.apiKey) {
|
||||
url += `&key=${this.apiKey}`;
|
||||
}
|
||||
|
||||
if (this.channel) {
|
||||
url += `&channel=${this.channel}`;
|
||||
}
|
||||
|
||||
if (this.client) {
|
||||
url += `&client=${this.client}`;
|
||||
}
|
||||
|
||||
if (this.libraries.length > 0) {
|
||||
url += `&libraries=${this.libraries.join(",")}`;
|
||||
}
|
||||
|
||||
if (this.language) {
|
||||
url += `&language=${this.language}`;
|
||||
}
|
||||
|
||||
if (this.region) {
|
||||
url += `®ion=${this.region}`;
|
||||
}
|
||||
|
||||
if (this.version) {
|
||||
url += `&v=${this.version}`;
|
||||
}
|
||||
|
||||
if (this.mapIds) {
|
||||
url += `&map_ids=${this.mapIds.join(",")}`;
|
||||
}
|
||||
|
||||
if (this.authReferrerPolicy) {
|
||||
url += `&auth_referrer_policy=${this.authReferrerPolicy}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public deleteScript(): void {
|
||||
const script = document.getElementById(this.id);
|
||||
if (script) {
|
||||
script.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
public load(): Promise<typeof google> {
|
||||
return this.loadPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script and return a Promise.
|
||||
*
|
||||
* @ignore
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
public loadPromise(): Promise<typeof google> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loadCallback((err: ErrorEvent) => {
|
||||
if (!err) {
|
||||
resolve(window.google);
|
||||
} else {
|
||||
reject(err.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://developers.google.com/maps/documentation/javascript/reference/top-level#google.maps.importLibrary
|
||||
*/
|
||||
public importLibrary(name: "core"): Promise<google.maps.CoreLibrary>;
|
||||
public importLibrary(name: "maps"): Promise<google.maps.MapsLibrary>;
|
||||
public importLibrary(name: "places"): Promise<google.maps.PlacesLibrary>;
|
||||
public importLibrary(
|
||||
name: "geocoding"
|
||||
): Promise<google.maps.GeocodingLibrary>;
|
||||
public importLibrary(name: "routes"): Promise<google.maps.RoutesLibrary>;
|
||||
public importLibrary(name: "marker"): Promise<google.maps.MarkerLibrary>;
|
||||
public importLibrary(name: "geometry"): Promise<google.maps.GeometryLibrary>;
|
||||
public importLibrary(
|
||||
name: "elevation"
|
||||
): Promise<google.maps.ElevationLibrary>;
|
||||
public importLibrary(
|
||||
name: "streetView"
|
||||
): Promise<google.maps.StreetViewLibrary>;
|
||||
public importLibrary(
|
||||
name: "journeySharing"
|
||||
): Promise<google.maps.JourneySharingLibrary>;
|
||||
public importLibrary(name: "drawing"): Promise<google.maps.DrawingLibrary>;
|
||||
public importLibrary(
|
||||
name: "visualization"
|
||||
): Promise<google.maps.VisualizationLibrary>;
|
||||
public importLibrary(name: Library): Promise<unknown>;
|
||||
public importLibrary(name: Library): Promise<unknown> {
|
||||
this.execute();
|
||||
return google.maps.importLibrary(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Google Maps JavaScript API script with a callback.
|
||||
* @deprecated, use importLibrary() instead.
|
||||
*/
|
||||
public loadCallback(fn: (e: ErrorEvent) => void): void {
|
||||
this.callbacks.push(fn);
|
||||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script on document.
|
||||
*/
|
||||
private setScript(): void {
|
||||
if (document.getElementById(this.id)) {
|
||||
// TODO wrap onerror callback for cases where the script was loaded elsewhere
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
key: this.apiKey,
|
||||
channel: this.channel,
|
||||
client: this.client,
|
||||
libraries: this.libraries.length && this.libraries,
|
||||
v: this.version,
|
||||
mapIds: this.mapIds,
|
||||
language: this.language,
|
||||
region: this.region,
|
||||
authReferrerPolicy: this.authReferrerPolicy,
|
||||
};
|
||||
// keep the URL minimal:
|
||||
Object.keys(params).forEach(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(key) => !(params as any)[key] && delete (params as any)[key]
|
||||
);
|
||||
|
||||
if (!window?.google?.maps?.importLibrary) {
|
||||
// tweaked copy of https://developers.google.com/maps/documentation/javascript/load-maps-js-api#dynamic-library-import
|
||||
// which also sets the base url, the id, and the nonce
|
||||
/* eslint-disable */
|
||||
((g) => {
|
||||
// @ts-ignore
|
||||
let h,
|
||||
a,
|
||||
k,
|
||||
p = "The Google Maps JavaScript API",
|
||||
c = "google",
|
||||
l = "importLibrary",
|
||||
q = "__ib__",
|
||||
m = document,
|
||||
b = window;
|
||||
// @ts-ignore
|
||||
b = b[c] || (b[c] = {});
|
||||
// @ts-ignore
|
||||
const d = b.maps || (b.maps = {}),
|
||||
r = new Set(),
|
||||
e = new URLSearchParams(),
|
||||
u = () =>
|
||||
// @ts-ignore
|
||||
h || (h = new Promise(async (f, n) => {
|
||||
await (a = m.createElement("script"));
|
||||
a.id = this.id;
|
||||
e.set("libraries", [...r] + "");
|
||||
// @ts-ignore
|
||||
for (k in g) e.set(k.replace(/[A-Z]/g, (t) => "_" + t[0].toLowerCase()), g[k]);
|
||||
e.set("callback", c + ".maps." + q);
|
||||
a.src = this.url + `?` + e;
|
||||
d[q] = f;
|
||||
a.onerror = () => (h = n(Error(p + " could not load.")));
|
||||
// @ts-ignore
|
||||
a.nonce = this.nonce || m.querySelector("script[nonce]")?.nonce || "";
|
||||
m.head.append(a);
|
||||
}));
|
||||
// @ts-ignore
|
||||
d[l] ? console.warn(p + " only loads once. Ignoring:", g) : (d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)));
|
||||
})(params);
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
// While most libraries populate the global namespace when loaded via bootstrap params,
|
||||
// this is not the case for "marker" when used with the inline bootstrap loader
|
||||
// (and maybe others in the future). So ensure there is an importLibrary for each:
|
||||
const libraryPromises = this.libraries.map((library) =>
|
||||
this.importLibrary(library)
|
||||
);
|
||||
// ensure at least one library, to kick off loading...
|
||||
if (!libraryPromises.length) {
|
||||
libraryPromises.push(this.importLibrary("core"));
|
||||
}
|
||||
Promise.all(libraryPromises).then(
|
||||
() => this.callback(),
|
||||
(error) => {
|
||||
const event = new ErrorEvent("error", { error }); // for backwards compat
|
||||
this.loadErrorCallback(event);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the loader state.
|
||||
*/
|
||||
private reset(): void {
|
||||
this.deleteScript();
|
||||
this.done = false;
|
||||
this.loading = false;
|
||||
this.errors = [];
|
||||
this.onerrorEvent = null;
|
||||
}
|
||||
|
||||
private resetIfRetryingFailed(): void {
|
||||
if (this.failed) {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private loadErrorCallback(e: ErrorEvent): void {
|
||||
this.errors.push(e);
|
||||
|
||||
if (this.errors.length <= this.retries) {
|
||||
const delay = this.errors.length * 2 ** this.errors.length;
|
||||
|
||||
console.error(
|
||||
`Failed to load Google Maps script, retrying in ${delay} ms.`
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
this.deleteScript();
|
||||
this.setScript();
|
||||
}, delay);
|
||||
} else {
|
||||
this.onerrorEvent = e;
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
|
||||
private callback(): void {
|
||||
this.done = true;
|
||||
this.loading = false;
|
||||
|
||||
this.callbacks.forEach((cb) => {
|
||||
cb(this.onerrorEvent);
|
||||
});
|
||||
|
||||
this.callbacks = [];
|
||||
}
|
||||
|
||||
private execute(): void {
|
||||
this.resetIfRetryingFailed();
|
||||
|
||||
if (this.loading) {
|
||||
// do nothing but wait
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.done) {
|
||||
this.callback();
|
||||
} else {
|
||||
// short circuit and warn if google.maps is already loaded
|
||||
if (window.google && window.google.maps && window.google.maps.version) {
|
||||
console.warn(
|
||||
"Google Maps already loaded outside @googlemaps/js-api-loader. " +
|
||||
"This may result in undesirable behavior as options and script parameters may not match."
|
||||
);
|
||||
this.callback();
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.setScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user