@hcaptcha/react-hcaptcha 1.17.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js DELETED
@@ -1,471 +0,0 @@
1
- import * as React from 'react';
2
- import { hCaptchaLoader } from '@hcaptcha/loader';
3
-
4
- import { getFrame, getMountElement } from './utils.js';
5
-
6
-
7
- class HCaptcha extends React.Component {
8
- constructor (props) {
9
- super(props);
10
-
11
- /**
12
- * Internal reference to track hCaptcha API
13
- *
14
- * Required as window is relative to initialization in application
15
- * not where the script and iFrames have been loaded.
16
- */
17
- this._hcaptcha = undefined;
18
-
19
- // API Methods
20
- this.renderCaptcha = this.renderCaptcha.bind(this);
21
- this.resetCaptcha = this.resetCaptcha.bind(this);
22
- this.removeCaptcha = this.removeCaptcha.bind(this);
23
- this.isReady = this.isReady.bind(this);
24
- this._onReady = null;
25
-
26
- // Event Handlers
27
- this.loadCaptcha = this.loadCaptcha.bind(this);
28
- this.handleOnLoad = this.handleOnLoad.bind(this);
29
- this.handleSubmit = this.handleSubmit.bind(this);
30
- this.handleExpire = this.handleExpire.bind(this);
31
- this.handleError = this.handleError.bind(this);
32
- this.handleOpen = this.handleOpen.bind(this);
33
- this.handleClose = this.handleClose.bind(this);
34
- this.handleChallengeExpired = this.handleChallengeExpired.bind(this);
35
-
36
- this.ref = React.createRef();
37
- this.apiScriptRequested = false;
38
- this.sentryHub = null;
39
- this.captchaId = '';
40
-
41
- /**
42
- * Tracks the currently pending async execute() promise.
43
- * Stores { resolve, reject } so we can cancel on unmount/errors/etc.
44
- */
45
- this._pendingExecute = null;
46
-
47
- this.state = {
48
- isApiReady: false,
49
- isRemoved: false,
50
- elementId: props.id,
51
- }
52
- }
53
-
54
- componentDidMount () { // Once captcha is mounted intialize hCaptcha - hCaptcha
55
- const element = getMountElement(this.props.scriptLocation);
56
- const frame = getFrame(element);
57
- this._hcaptcha = frame.window.hcaptcha || undefined;
58
-
59
- const isApiReady = typeof this._hcaptcha !== 'undefined';
60
-
61
- /*
62
- * Check if hCaptcha has already been loaded,
63
- * If Yes, render the captcha
64
- * If No, create script tag and wait to render the captcha
65
- */
66
- if (isApiReady) {
67
- this.setState(
68
- {
69
- isApiReady: true
70
- },
71
- () => {
72
- this.renderCaptcha();
73
- }
74
- );
75
-
76
- return;
77
- }
78
-
79
- this.loadCaptcha();
80
- }
81
-
82
- componentWillUnmount() {
83
- const hcaptcha = this._hcaptcha;
84
- const captchaId = this.captchaId;
85
-
86
- this._cancelPendingExecute('react-component-unmounted');
87
-
88
- if (!this.isReady()) {
89
- return;
90
- }
91
-
92
- // Reset any stored variables / timers when unmounting
93
- hcaptcha.reset(captchaId);
94
- hcaptcha.remove(captchaId);
95
- }
96
-
97
- shouldComponentUpdate(nextProps, nextState) {
98
- // Prevent component re-rendering when these internal state variables are updated
99
- if (this.state.isApiReady !== nextState.isApiReady || this.state.isRemoved !== nextState.isRemoved) {
100
- return false;
101
- }
102
-
103
- return true;
104
- }
105
-
106
- componentDidUpdate(prevProps) {
107
- // Prop Keys that could change
108
- const keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint'];
109
- // See if any props changed during component update
110
- const match = keys.every( key => prevProps[key] === this.props[key]);
111
-
112
- // If they have changed, remove current captcha and render a new one
113
- if (!match) {
114
- this.removeCaptcha(() => {
115
- this.renderCaptcha();
116
- });
117
- }
118
- }
119
-
120
- loadCaptcha() {
121
- if (this.apiScriptRequested) {
122
- return;
123
- }
124
-
125
- const {
126
- apihost,
127
- assethost,
128
- endpoint,
129
- host,
130
- imghost,
131
- languageOverride: hl,
132
- reCaptchaCompat,
133
- reportapi,
134
- sentry,
135
- custom,
136
- loadAsync,
137
- scriptLocation,
138
- scriptSource,
139
- secureApi,
140
- cleanup = true,
141
- userJourneys,
142
- } = this.props;
143
-
144
- const mountParams = {
145
- render: 'explicit',
146
- apihost,
147
- assethost,
148
- endpoint,
149
- hl,
150
- host,
151
- imghost,
152
- recaptchacompat: reCaptchaCompat === false? 'off' : null,
153
- reportapi,
154
- sentry,
155
- custom,
156
- loadAsync,
157
- scriptLocation,
158
- scriptSource,
159
- secureApi,
160
- cleanup,
161
- uj: userJourneys !== undefined ? userJourneys : false,
162
- };
163
-
164
- hCaptchaLoader(mountParams)
165
- .then(this.handleOnLoad, this.handleError)
166
- .catch(this.handleError);
167
-
168
- this.apiScriptRequested = true;
169
- }
170
-
171
- renderCaptcha(onRender) {
172
- const { onReady } = this.props;
173
- const { isApiReady } = this.state;
174
- const captchaId = this.captchaId;
175
-
176
- // Prevent calling hCaptcha render on two conditions:
177
- // • API is not ready
178
- // • Component has already been mounted
179
- if (!isApiReady || captchaId) return;
180
-
181
- const renderParams = Object.assign({
182
- "open-callback" : this.handleOpen,
183
- "close-callback" : this.handleClose,
184
- "error-callback" : this.handleError,
185
- "chalexpired-callback": this.handleChallengeExpired,
186
- "expired-callback" : this.handleExpire,
187
- "callback" : this.handleSubmit,
188
- }, this.props, {
189
- hl: this.props.hl || this.props.languageOverride,
190
- languageOverride: undefined
191
- });
192
-
193
- const hcaptcha = this._hcaptcha;
194
- //Render hCaptcha widget and provide necessary callbacks - hCaptcha
195
- const id = hcaptcha.render(this.ref.current, renderParams);
196
- this.captchaId = id;
197
-
198
- this.setState({ isRemoved: false }, () => {
199
- onRender && onRender();
200
- onReady && onReady();
201
- this._onReady && this._onReady(id);
202
- });
203
- }
204
-
205
- resetCaptcha() {
206
- const hcaptcha = this._hcaptcha;
207
- const captchaId = this.captchaId;
208
-
209
- if (!this.isReady()) {
210
- return;
211
- }
212
-
213
- // Reset captcha state, removes stored token and unticks checkbox
214
- hcaptcha.reset(captchaId)
215
-
216
- this._cancelPendingExecute('hcaptcha-reset');
217
- }
218
-
219
- removeCaptcha(callback) {
220
- const hcaptcha = this._hcaptcha;
221
- const captchaId = this.captchaId;
222
-
223
- this._cancelPendingExecute('hcaptcha-removed');
224
-
225
- if (!this.isReady()) {
226
- return;
227
- }
228
-
229
- this.setState({ isRemoved: true }, () => {
230
- this.captchaId = '';
231
-
232
- hcaptcha.remove(captchaId);
233
-
234
- callback && callback()
235
- });
236
- }
237
-
238
- handleOnLoad () {
239
- this.setState({ isApiReady: true }, () => {
240
- const element = getMountElement(this.props.scriptLocation);
241
- const frame = getFrame(element);
242
-
243
- this._hcaptcha = frame.window.hcaptcha;
244
-
245
-
246
- // render captcha and wait for captcha id
247
- this.renderCaptcha(() => {
248
- // trigger onLoad if it exists
249
-
250
- const { onLoad } = this.props;
251
- if (onLoad) onLoad();
252
- });
253
- });
254
- }
255
-
256
- handleSubmit (event) {
257
- const { onVerify } = this.props;
258
- const { isRemoved } = this.state;
259
- const hcaptcha = this._hcaptcha;
260
- const captchaId = this.captchaId;
261
-
262
- if (typeof hcaptcha === 'undefined' || isRemoved) return
263
-
264
- const token = hcaptcha.getResponse(captchaId) //Get response token from hCaptcha widget
265
- const ekey = hcaptcha.getRespKey(captchaId) //Get current challenge session id from hCaptcha widget
266
- if (onVerify) onVerify(token, ekey) //Dispatch event to verify user response
267
- }
268
-
269
- handleExpire () {
270
- const { onExpire } = this.props;
271
- const hcaptcha = this._hcaptcha;
272
- const captchaId = this.captchaId;
273
-
274
- if (!this.isReady()) {
275
- return;
276
- }
277
-
278
- hcaptcha.reset(captchaId) // If hCaptcha runs into error, reset captcha - hCaptcha
279
-
280
- if (onExpire) onExpire();
281
- }
282
-
283
- handleError (event) {
284
- const { onError } = this.props;
285
- const hcaptcha = this._hcaptcha;
286
- const captchaId = this.captchaId;
287
-
288
- if (this.isReady()) {
289
- // If hCaptcha runs into error, reset captcha - hCaptcha
290
- hcaptcha.reset(captchaId);
291
- }
292
-
293
- if (onError) onError(event);
294
- }
295
-
296
- isReady () {
297
- const { isApiReady, isRemoved } = this.state;
298
-
299
- return isApiReady && !isRemoved;
300
- }
301
-
302
- /**
303
- * Cancel any pending async execute() promise
304
- * Called when the component unmounts, errors occur, resets, etc.
305
- */
306
- _cancelPendingExecute(reason) {
307
- if (!this._pendingExecute) {
308
- return;
309
- }
310
-
311
- const pending = this._pendingExecute;
312
- this._pendingExecute = null;
313
-
314
- const error = new Error(reason);
315
- pending.reject(error);
316
- }
317
-
318
- handleOpen () {
319
- if (!this.isReady() || !this.props.onOpen) {
320
- return;
321
- }
322
-
323
- this.props.onOpen();
324
- }
325
-
326
- handleClose () {
327
- if (!this.isReady() || !this.props.onClose) {
328
- return;
329
- }
330
-
331
- this.props.onClose();
332
- }
333
-
334
- handleChallengeExpired () {
335
- if (!this.isReady() || !this.props.onChalExpired) {
336
- return;
337
- }
338
-
339
- this.props.onChalExpired();
340
- }
341
-
342
- execute(opts = null) {
343
-
344
- opts = typeof opts === 'object' ? opts : null;
345
-
346
- try {
347
- const hcaptcha = this._hcaptcha;
348
- const captchaId = this.captchaId;
349
-
350
- // Is an async execute and there's already 1 pending, cancel the old one.
351
- if (opts && opts.async && this._pendingExecute) {
352
- this._cancelPendingExecute('hcaptcha-execute-replaced');
353
- }
354
-
355
- if (!this.isReady()) {
356
- if (opts && opts.async) {
357
- return new Promise((resolve, reject) => {
358
- this._pendingExecute = { resolve, reject };
359
-
360
- this._onReady = (id) => {
361
- if (!this._pendingExecute) {
362
- return;
363
- }
364
-
365
- try {
366
- const result = hcaptcha.execute(id, opts);
367
-
368
- if (result && typeof result.then === 'function') {
369
- result
370
- .then((val) => {
371
- this._pendingExecute = null;
372
- resolve(val);
373
- })
374
- .catch((err) => {
375
- this._pendingExecute = null;
376
- reject(err);
377
- });
378
- } else {
379
- this._pendingExecute = null;
380
- reject(new Error('hcaptcha-execute-no-promise'));
381
- }
382
- } catch (e) {
383
- this._pendingExecute = null;
384
- reject(e);
385
- }
386
- };
387
- });
388
- } else {
389
- // Non-async: don't return a promise.
390
- this._onReady = (id) => {
391
- hcaptcha.execute(id, opts);
392
- };
393
-
394
- return null;
395
- }
396
- }
397
-
398
- // hCaptcha is ready, execute directly.
399
- const result = hcaptcha.execute(captchaId, opts);
400
-
401
- // If it's async execute, track it.
402
- if (opts && opts.async && result && typeof result.then === 'function') {
403
- return new Promise((resolve, reject) => {
404
- this._pendingExecute = { resolve, reject };
405
-
406
- result
407
- .then((val) => {
408
- this._pendingExecute = null;
409
- resolve(val);
410
- })
411
- .catch((err) => {
412
- this._pendingExecute = null;
413
- reject(err);
414
- });
415
- });
416
- }
417
-
418
- return result;
419
- } catch (error) {
420
- if (opts && opts.async) {
421
- return Promise.reject(error);
422
- }
423
- return null;
424
- }
425
- }
426
-
427
- close() {
428
- const hcaptcha = this._hcaptcha;
429
- const captchaId = this.captchaId;
430
-
431
- this._cancelPendingExecute('hcaptcha-closed');
432
-
433
- if (!this.isReady()) {
434
- return;
435
- }
436
-
437
- return hcaptcha.close(captchaId);
438
- }
439
-
440
- setData (data) {
441
- const hcaptcha = this._hcaptcha;
442
- const captchaId = this.captchaId;
443
-
444
- if (!this.isReady()) {
445
- return;
446
- }
447
-
448
- if (data && typeof data !== "object") {
449
- data = null;
450
- }
451
-
452
- hcaptcha.setData(captchaId, data);
453
- }
454
-
455
- getResponse() {
456
- const hcaptcha = this._hcaptcha;
457
- return hcaptcha.getResponse(this.captchaId);
458
- }
459
-
460
- getRespKey() {
461
- const hcaptcha = this._hcaptcha;
462
- return hcaptcha.getRespKey(this.captchaId)
463
- }
464
-
465
- render () {
466
- const { elementId } = this.state;
467
- return <div ref={this.ref} id={elementId}></div>;
468
- }
469
- }
470
-
471
- export default HCaptcha;
package/src/utils.js DELETED
@@ -1,15 +0,0 @@
1
- function getFrame(element) {
2
- const doc = (element && element.ownerDocument) || document;
3
- const win = doc.defaultView || doc.parentWindow || window;
4
-
5
- return { document: doc, window: win };
6
- }
7
-
8
- function getMountElement(element) {
9
- return element || document.head;
10
- }
11
-
12
- export {
13
- getFrame,
14
- getMountElement
15
- };