diff --git a/README.md b/README.md index db6a9d9..a5aedae 100755 --- a/README.md +++ b/README.md @@ -48,12 +48,13 @@ using top level options { // ... recaptcha: { - hideBadge: Boolean, // Hide badge element (v3 & v2 via size=invisible) - language: String, // Recaptcha language (v2) - mode: String, // Mode: 'base', 'enterprise' - siteKey: String, // Site key for requests - version: Number, // Version - size: String // Size: 'compact', 'normal', 'invisible' (v2) + hideBadge: Boolean, // Hide badge element (v3 & v2 via size=invisible) + language: String, // Recaptcha language (v2) + onloadCallback: Function, // Fonction de rappel à exécuter lorsque reCAPTCHA est complètement chargé + mode: String, // Mode: 'base', 'enterprise' + siteKey: String, // Site key for requests + version: Number, // Version + size: String // Size: 'compact', 'normal', 'invisible' (v2) }, // ... } diff --git a/example/base/v2/pages/index.vue b/example/base/v2/pages/index.vue index 1606326..7cbb3ed 100644 --- a/example/base/v2/pages/index.vue +++ b/example/base/v2/pages/index.vue @@ -21,6 +21,7 @@ @error="onError" @success="onSuccess" @expired="onExpired" + @load="onLoad" /> @@ -68,6 +69,10 @@ export default { onExpired () { console.log('Expired') + }, + + onLoad () { + console.log('Loaded') } }, } diff --git a/example/base/v3/pages/index.vue b/example/base/v3/pages/index.vue index d1f9e7e..b71ab28 100644 --- a/example/base/v3/pages/index.vue +++ b/example/base/v3/pages/index.vue @@ -32,6 +32,7 @@ export default { async mounted() { try { await this.$recaptcha.init() + this.$recaptcha.on('load', () => console.log('loaded')) } catch (e) { console.log(e); } diff --git a/example/enterprise/v2/pages/index.vue b/example/enterprise/v2/pages/index.vue index 1606326..7cbb3ed 100644 --- a/example/enterprise/v2/pages/index.vue +++ b/example/enterprise/v2/pages/index.vue @@ -21,6 +21,7 @@ @error="onError" @success="onSuccess" @expired="onExpired" + @load="onLoad" /> @@ -68,6 +69,10 @@ export default { onExpired () { console.log('Expired') + }, + + onLoad () { + console.log('Loaded') } }, } diff --git a/example/enterprise/v3/pages/index.vue b/example/enterprise/v3/pages/index.vue index d1f9e7e..b71ab28 100644 --- a/example/enterprise/v3/pages/index.vue +++ b/example/enterprise/v3/pages/index.vue @@ -32,6 +32,7 @@ export default { async mounted() { try { await this.$recaptcha.init() + this.$recaptcha.on('load', () => console.log('loaded')) } catch (e) { console.log(e); } diff --git a/lib/plugin.js b/lib/plugin.js index 5f773f4..4965ed2 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -4,7 +4,11 @@ import Vue from 'vue' const API_URL = 'https://www.recaptcha.net/recaptcha/api.js' class ReCaptcha { - constructor ({ hideBadge, language, mode, siteKey, version, size }) { + constructor ({ hideBadge, language, onloadCallback, mode, siteKey, version, size }) { + if (onloadCallback && typeof onloadCallback !== 'function') { + throw new Error('ReCaptcha error: onloadCallback must be a function') + } + if (!siteKey) { throw new Error('ReCaptcha error: No key provided') } @@ -21,6 +25,7 @@ class ReCaptcha { this.hideBadge = hideBadge this.language = language + this.onloadCallback = onloadCallback this.siteKey = siteKey this.version = version @@ -122,6 +127,11 @@ class ReCaptcha { const params = [] if (this.version === 3) { params.push('render=' + this.siteKey) } if (this.language) { params.push('hl=' + this.language) } + if (this.onloadCallback && typeof this.onloadCallback === 'function') { + if (this.version === 2) { + params.push('onload=recaptchaOnloadCallbackFunction') + } + } let scriptUrl = API_URL @@ -135,6 +145,13 @@ class ReCaptcha { window.recaptchaSuccessCallback = (token) => this._eventBus.emit('recaptcha-success', token) window.recaptchaExpiredCallback = () => this._eventBus.emit('recaptcha-expired') window.recaptchaErrorCallback = () => this._eventBus.emit('recaptcha-error', 'Failed to execute') + window.recaptchaOnloadCallback = () => this._eventBus.emit('recaptcha-onload') + window.recaptchaOnloadCallbackFunction = () => { + this._eventBus.emit('recaptcha-onload') + if (typeof this.onloadCallback === 'function') { + this.onloadCallback(); + } + } this._ready = new Promise((resolve, reject) => { script.addEventListener('load', () => { @@ -149,7 +166,10 @@ class ReCaptcha { } this._grecaptcha = window.grecaptcha.enterprise || window.grecaptcha - this._grecaptcha.ready(resolve) + this._grecaptcha.ready(() => { + windows.recaptchaOnloadCallbackFunction() + resolve() + }); }) script.addEventListener('error', () => { diff --git a/lib/recaptcha.vue b/lib/recaptcha.vue index fc0f7f3..470de0b 100644 --- a/lib/recaptcha.vue +++ b/lib/recaptcha.vue @@ -9,6 +9,7 @@ data-callback="recaptchaSuccessCallback" data-expired-callback="recaptchaExpiredCallback" data-error-callback="recaptchaErrorCallback" + data-onload-callback="recaptchaOnloadCallback" class="g-recaptcha" /> @@ -63,6 +64,7 @@ export default { this.$recaptcha.on('recaptcha-error', this.onError) this.$recaptcha.on('recaptcha-success', this.onSuccess) this.$recaptcha.on('recaptcha-expired', this.onExpired) + this.$recaptcha.on('recaptcha-onload', this.onLoad) }, computed: { @@ -83,6 +85,10 @@ export default { onExpired() { return this.$emit('expired') } + + onLoad() { + return this.$emit('load') + } } } diff --git a/types/index.d.ts b/types/index.d.ts index 05459ac..df64241 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -11,6 +11,11 @@ export interface ReCaptchaOptions { */ language?: string + /** + * Callback executed when ReCaptcha fully loads + */ + onloadCallback?: Function; + /** * ReCaptcha mode. */