summaryrefslogtreecommitdiff
path: root/src/reCAPTCHA.ts
blob: 620ba5e56f7aeb02f4f85816fa515e882295bce7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// handles dynamic reCAPTCHA loading

const loadHandler = "onRecaptchaLoad";
const script = `https://www.google.com/recaptcha/api.js?onload=${loadHandler}`;

// eslint-disable-next-line
type gRecaptchaInstance = any;

export default class ReCaptchaLoader {
	/**
	 * asynchronously injects reCAPTCHA and resolves once fully loaded
	 *
	 * @return {Promise<gRecaptchaInstance>} the grecaptcha inferface
	 */
	static load (): Promise<gRecaptchaInstance> {
		return new Promise((resolve, reject) => {
			if (Reflect.has(self, "grecaptcha")) {
				// we already have it loaded: reset it
				this.instance.reset();
				return resolve(this.instance);
			}

			let el: HTMLScriptElement|null = document.querySelector(`script[src="${script}"]`);

			if (el) {
				// already loading: don't attempt another load
				return;
			} else {
				el = document.createElement("script");
				el.type = "text/javascript";
				el.async = true;
				el.src = script;
			}

			// create a load handler:
			Reflect.set(self, loadHandler, () => {
				resolve(this.instance);
				Reflect.deleteProperty(self, loadHandler);
			});

			// attach the handlers:
			el.addEventListener("error", reject);
			el.addEventListener("abort", reject);
			// no listener for "load": we rely on reCAPTCHA to self-report loading

			// inject the tag and begin loading
			document.head.appendChild(el);
		});
	}

	/**
	 * checks whether reCAPTCHA is ready to use
	 */
	static get isReady (): boolean {
		return Reflect.has(self, "grecaptcha");
	}

	static get instance (): gRecaptchaInstance {
		return this.isReady ? Reflect.get(self, "grecaptcha"): null;
	}
}