@hcaptcha/react-hcaptcha 1.7.0 → 1.8.1

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/dist/esm/index.js CHANGED
@@ -1,49 +1,54 @@
1
1
  import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
2
2
  import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
3
3
  import * as React from 'react';
4
- import { generateQuery } from "./utils.js";
4
+ import { generateQuery, getFrame, getMountElement } from './utils.js';
5
5
  var SCRIPT_ID = 'hcaptcha-api-script-id';
6
6
  var HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad'; // Prevent loading API script multiple times
7
7
 
8
- var resolveFn;
9
- var rejectFn;
10
- var mountPromise = new Promise(function (resolve, reject) {
11
- resolveFn = resolve;
12
- rejectFn = reject;
13
- }); // Generate hCaptcha API script
8
+ var scripts = []; // Generate hCaptcha API script
14
9
 
15
10
  var mountCaptchaScript = function mountCaptchaScript(params) {
16
11
  if (params === void 0) {
17
12
  params = {};
18
13
  }
19
14
 
20
- var parent = params.scriptLocation || document.head;
15
+ var element = getMountElement(params.scriptLocation);
21
16
  delete params.scriptLocation;
22
- var doc = parent.ownerDocument || document;
17
+ var frame = getFrame(element);
18
+ var script = scripts.find(function (_ref) {
19
+ var scope = _ref.scope;
20
+ return scope === frame.window;
21
+ });
23
22
 
24
- if (doc.getElementById(SCRIPT_ID)) {
23
+ if (frame.document.getElementById(SCRIPT_ID) && script) {
25
24
  // API was already requested
26
- return mountPromise;
27
- } // Create global onload callback
28
-
29
-
30
- window[HCAPTCHA_LOAD_FN_NAME] = resolveFn;
31
- var domain = params.apihost || "https://js.hcaptcha.com";
32
- delete params.apihost;
33
- var script = doc.createElement("script");
34
- script.id = SCRIPT_ID;
35
- script.src = domain + "/1/api.js?render=explicit&onload=" + HCAPTCHA_LOAD_FN_NAME;
36
- script.async = params.loadAsync !== undefined ? params.loadAsync : true;
37
- delete params.loadAsync;
25
+ return script.promise;
26
+ }
38
27
 
39
- script.onerror = function (event) {
40
- return rejectFn('script-error');
41
- };
28
+ var promise = new Promise(function (resolve, reject) {
29
+ // Create global onload callback
30
+ frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
31
+ var domain = params.apihost || "https://js.hcaptcha.com";
32
+ delete params.apihost;
33
+ var script = frame.document.createElement("script");
34
+ script.id = SCRIPT_ID;
35
+ script.src = domain + "/1/api.js?render=explicit&onload=" + HCAPTCHA_LOAD_FN_NAME;
36
+ script.async = params.loadAsync !== undefined ? params.loadAsync : true;
37
+ delete params.loadAsync;
38
+
39
+ script.onerror = function (event) {
40
+ return reject('script-error');
41
+ };
42
42
 
43
- var query = generateQuery(params);
44
- script.src += query !== "" ? "&" + query : "";
45
- parent.appendChild(script);
46
- return mountPromise;
43
+ var query = generateQuery(params);
44
+ script.src += query !== "" ? "&" + query : "";
45
+ element.appendChild(script);
46
+ });
47
+ scripts.push({
48
+ promise: promise,
49
+ scope: frame.window
50
+ });
51
+ return promise;
47
52
  };
48
53
 
49
54
  var HCaptcha = /*#__PURE__*/function (_React$Component) {
@@ -52,7 +57,15 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
52
57
  function HCaptcha(props) {
53
58
  var _this;
54
59
 
55
- _this = _React$Component.call(this, props) || this; // API Methods
60
+ _this = _React$Component.call(this, props) || this;
61
+ /**
62
+ * Internal reference to track hCaptcha API
63
+ *
64
+ * Required as window is relative to initialization in application
65
+ * not where the script and iFrames have been loaded.
66
+ */
67
+
68
+ _this._hcaptcha = undefined; // API Methods
56
69
 
57
70
  _this.renderCaptcha = _this.renderCaptcha.bind(_assertThisInitialized(_this));
58
71
  _this.resetCaptcha = _this.resetCaptcha.bind(_assertThisInitialized(_this));
@@ -67,11 +80,10 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
67
80
  _this.handleOpen = _this.handleOpen.bind(_assertThisInitialized(_this));
68
81
  _this.handleClose = _this.handleClose.bind(_assertThisInitialized(_this));
69
82
  _this.handleChallengeExpired = _this.handleChallengeExpired.bind(_assertThisInitialized(_this));
70
- var isApiReady = typeof hcaptcha !== 'undefined';
71
83
  _this.ref = /*#__PURE__*/React.createRef();
72
84
  _this.apiScriptRequested = false;
73
85
  _this.state = {
74
- isApiReady: isApiReady,
86
+ isApiReady: false,
75
87
  isRemoved: false,
76
88
  elementId: props.id,
77
89
  captchaId: ''
@@ -82,8 +94,13 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
82
94
  var _proto = HCaptcha.prototype;
83
95
 
84
96
  _proto.componentDidMount = function componentDidMount() {
97
+ var _this2 = this;
98
+
85
99
  // Once captcha is mounted intialize hCaptcha - hCaptcha
86
- var isApiReady = this.state.isApiReady;
100
+ var element = getMountElement(this.props.scriptLocation);
101
+ var frame = getFrame(element);
102
+ this._hcaptcha = frame.window.hcaptcha || undefined;
103
+ var isApiReady = typeof this._hcaptcha !== 'undefined';
87
104
  /*
88
105
  * Check if hCaptcha has already been loaded,
89
106
  * If Yes, render the captcha
@@ -91,7 +108,11 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
91
108
  */
92
109
 
93
110
  if (isApiReady) {
94
- this.renderCaptcha();
111
+ this.setState({
112
+ isApiReady: true
113
+ }, function () {
114
+ _this2.renderCaptcha();
115
+ });
95
116
  return;
96
117
  }
97
118
 
@@ -100,6 +121,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
100
121
 
101
122
  _proto.componentWillUnmount = function componentWillUnmount() {
102
123
  var captchaId = this.state.captchaId;
124
+ var hcaptcha = this._hcaptcha;
103
125
 
104
126
  if (!this.isReady()) {
105
127
  return;
@@ -120,18 +142,18 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
120
142
  };
121
143
 
122
144
  _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
123
- var _this2 = this;
145
+ var _this3 = this;
124
146
 
125
147
  // Prop Keys that could change
126
148
  var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint']; // See if any props changed during component update
127
149
 
128
150
  var match = keys.every(function (key) {
129
- return prevProps[key] === _this2.props[key];
151
+ return prevProps[key] === _this3.props[key];
130
152
  }); // If they have changed, remove current captcha and render a new one
131
153
 
132
154
  if (!match) {
133
155
  this.removeCaptcha(function () {
134
- _this2.renderCaptcha();
156
+ _this3.renderCaptcha();
135
157
  });
136
158
  }
137
159
  };
@@ -185,7 +207,8 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
185
207
  }, this.props, {
186
208
  hl: this.props.hl || this.props.languageOverride,
187
209
  languageOverride: undefined
188
- }); //Render hCaptcha widget and provide necessary callbacks - hCaptcha
210
+ });
211
+ var hcaptcha = this._hcaptcha; //Render hCaptcha widget and provide necessary callbacks - hCaptcha
189
212
 
190
213
  var captchaId = hcaptcha.render(this.ref.current, renderParams);
191
214
  this.setState({
@@ -198,6 +221,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
198
221
 
199
222
  _proto.resetCaptcha = function resetCaptcha() {
200
223
  var captchaId = this.state.captchaId;
224
+ var hcaptcha = this._hcaptcha;
201
225
 
202
226
  if (!this.isReady()) {
203
227
  return;
@@ -209,6 +233,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
209
233
 
210
234
  _proto.removeCaptcha = function removeCaptcha(callback) {
211
235
  var captchaId = this.state.captchaId;
236
+ var hcaptcha = this._hcaptcha;
212
237
 
213
238
  if (!this.isReady()) {
214
239
  return;
@@ -223,15 +248,18 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
223
248
  };
224
249
 
225
250
  _proto.handleOnLoad = function handleOnLoad() {
226
- var _this3 = this;
251
+ var _this4 = this;
227
252
 
228
253
  this.setState({
229
254
  isApiReady: true
230
255
  }, function () {
231
- // render captcha and wait for captcha id
232
- _this3.renderCaptcha(function () {
256
+ var element = getMountElement(_this4.props.scriptLocation);
257
+ var frame = getFrame(element);
258
+ _this4._hcaptcha = frame.window.hcaptcha; // render captcha and wait for captcha id
259
+
260
+ _this4.renderCaptcha(function () {
233
261
  // trigger onLoad if it exists
234
- var onLoad = _this3.props.onLoad;
262
+ var onLoad = _this4.props.onLoad;
235
263
  if (onLoad) onLoad();
236
264
  });
237
265
  });
@@ -242,6 +270,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
242
270
  var _this$state = this.state,
243
271
  isRemoved = _this$state.isRemoved,
244
272
  captchaId = _this$state.captchaId;
273
+ var hcaptcha = this._hcaptcha;
245
274
  if (typeof hcaptcha === 'undefined' || isRemoved) return;
246
275
  var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
247
276
 
@@ -253,6 +282,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
253
282
  _proto.handleExpire = function handleExpire() {
254
283
  var onExpire = this.props.onExpire;
255
284
  var captchaId = this.state.captchaId;
285
+ var hcaptcha = this._hcaptcha;
256
286
 
257
287
  if (!this.isReady()) {
258
288
  return;
@@ -266,6 +296,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
266
296
  _proto.handleError = function handleError(event) {
267
297
  var onError = this.props.onError;
268
298
  var captchaId = this.state.captchaId;
299
+ var hcaptcha = this._hcaptcha;
269
300
 
270
301
  if (this.isReady()) {
271
302
  // If hCaptcha runs into error, reset captcha - hCaptcha
@@ -312,6 +343,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
312
343
  }
313
344
 
314
345
  var captchaId = this.state.captchaId;
346
+ var hcaptcha = this._hcaptcha;
315
347
 
316
348
  if (!this.isReady()) {
317
349
  return;
@@ -326,6 +358,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
326
358
 
327
359
  _proto.setData = function setData(data) {
328
360
  var captchaId = this.state.captchaId;
361
+ var hcaptcha = this._hcaptcha;
329
362
 
330
363
  if (!this.isReady()) {
331
364
  return;
@@ -339,10 +372,12 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
339
372
  };
340
373
 
341
374
  _proto.getResponse = function getResponse() {
375
+ var hcaptcha = this._hcaptcha;
342
376
  return hcaptcha.getResponse(this.state.captchaId);
343
377
  };
344
378
 
345
379
  _proto.getRespKey = function getRespKey() {
380
+ var hcaptcha = this._hcaptcha;
346
381
  return hcaptcha.getRespKey(this.state.captchaId);
347
382
  };
348
383
 
package/dist/esm/utils.js CHANGED
@@ -11,4 +11,18 @@ function generateQuery(params) {
11
11
  }
12
12
 
13
13
  ;
14
- export { generateQuery };
14
+
15
+ function getFrame(element) {
16
+ var doc = element && element.ownerDocument || document;
17
+ var win = doc.defaultView || doc.parentWindow || window;
18
+ return {
19
+ document: doc,
20
+ window: win
21
+ };
22
+ }
23
+
24
+ function getMountElement(element) {
25
+ return element || document.head;
26
+ }
27
+
28
+ export { generateQuery, getFrame, getMountElement };
package/dist/index.js CHANGED
@@ -34,42 +34,47 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
34
34
  var SCRIPT_ID = 'hcaptcha-api-script-id';
35
35
  var HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad'; // Prevent loading API script multiple times
36
36
 
37
- var resolveFn;
38
- var rejectFn;
39
- var mountPromise = new Promise(function (resolve, reject) {
40
- resolveFn = resolve;
41
- rejectFn = reject;
42
- }); // Generate hCaptcha API script
37
+ var scripts = []; // Generate hCaptcha API script
43
38
 
44
39
  var mountCaptchaScript = function mountCaptchaScript() {
45
40
  var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
46
- var parent = params.scriptLocation || document.head;
41
+ var element = (0, _utils.getMountElement)(params.scriptLocation);
47
42
  delete params.scriptLocation;
48
- var doc = parent.ownerDocument || document;
43
+ var frame = (0, _utils.getFrame)(element);
44
+ var script = scripts.find(function (_ref) {
45
+ var scope = _ref.scope;
46
+ return scope === frame.window;
47
+ });
49
48
 
50
- if (doc.getElementById(SCRIPT_ID)) {
49
+ if (frame.document.getElementById(SCRIPT_ID) && script) {
51
50
  // API was already requested
52
- return mountPromise;
53
- } // Create global onload callback
54
-
55
-
56
- window[HCAPTCHA_LOAD_FN_NAME] = resolveFn;
57
- var domain = params.apihost || "https://js.hcaptcha.com";
58
- delete params.apihost;
59
- var script = doc.createElement("script");
60
- script.id = SCRIPT_ID;
61
- script.src = "".concat(domain, "/1/api.js?render=explicit&onload=").concat(HCAPTCHA_LOAD_FN_NAME);
62
- script.async = params.loadAsync !== undefined ? params.loadAsync : true;
63
- delete params.loadAsync;
64
-
65
- script.onerror = function (event) {
66
- return rejectFn('script-error');
67
- };
68
-
69
- var query = (0, _utils.generateQuery)(params);
70
- script.src += query !== "" ? "&".concat(query) : "";
71
- parent.appendChild(script);
72
- return mountPromise;
51
+ return script.promise;
52
+ }
53
+
54
+ var promise = new Promise(function (resolve, reject) {
55
+ // Create global onload callback
56
+ frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
57
+ var domain = params.apihost || "https://js.hcaptcha.com";
58
+ delete params.apihost;
59
+ var script = frame.document.createElement("script");
60
+ script.id = SCRIPT_ID;
61
+ script.src = "".concat(domain, "/1/api.js?render=explicit&onload=").concat(HCAPTCHA_LOAD_FN_NAME);
62
+ script.async = params.loadAsync !== undefined ? params.loadAsync : true;
63
+ delete params.loadAsync;
64
+
65
+ script.onerror = function (event) {
66
+ return reject('script-error');
67
+ };
68
+
69
+ var query = (0, _utils.generateQuery)(params);
70
+ script.src += query !== "" ? "&".concat(query) : "";
71
+ element.appendChild(script);
72
+ });
73
+ scripts.push({
74
+ promise: promise,
75
+ scope: frame.window
76
+ });
77
+ return promise;
73
78
  };
74
79
 
75
80
  var HCaptcha = /*#__PURE__*/function (_React$Component) {
@@ -81,7 +86,15 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
81
86
  var _this;
82
87
 
83
88
  (0, _classCallCheck2["default"])(this, HCaptcha);
84
- _this = _super.call(this, props); // API Methods
89
+ _this = _super.call(this, props);
90
+ /**
91
+ * Internal reference to track hCaptcha API
92
+ *
93
+ * Required as window is relative to initialization in application
94
+ * not where the script and iFrames have been loaded.
95
+ */
96
+
97
+ _this._hcaptcha = undefined; // API Methods
85
98
 
86
99
  _this.renderCaptcha = _this.renderCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
87
100
  _this.resetCaptcha = _this.resetCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
@@ -96,11 +109,10 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
96
109
  _this.handleOpen = _this.handleOpen.bind((0, _assertThisInitialized2["default"])(_this));
97
110
  _this.handleClose = _this.handleClose.bind((0, _assertThisInitialized2["default"])(_this));
98
111
  _this.handleChallengeExpired = _this.handleChallengeExpired.bind((0, _assertThisInitialized2["default"])(_this));
99
- var isApiReady = typeof hcaptcha !== 'undefined';
100
112
  _this.ref = /*#__PURE__*/React.createRef();
101
113
  _this.apiScriptRequested = false;
102
114
  _this.state = {
103
- isApiReady: isApiReady,
115
+ isApiReady: false,
104
116
  isRemoved: false,
105
117
  elementId: props.id,
106
118
  captchaId: ''
@@ -111,8 +123,13 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
111
123
  (0, _createClass2["default"])(HCaptcha, [{
112
124
  key: "componentDidMount",
113
125
  value: function componentDidMount() {
126
+ var _this2 = this;
127
+
114
128
  // Once captcha is mounted intialize hCaptcha - hCaptcha
115
- var isApiReady = this.state.isApiReady;
129
+ var element = (0, _utils.getMountElement)(this.props.scriptLocation);
130
+ var frame = (0, _utils.getFrame)(element);
131
+ this._hcaptcha = frame.window.hcaptcha || undefined;
132
+ var isApiReady = typeof this._hcaptcha !== 'undefined';
116
133
  /*
117
134
  * Check if hCaptcha has already been loaded,
118
135
  * If Yes, render the captcha
@@ -120,7 +137,11 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
120
137
  */
121
138
 
122
139
  if (isApiReady) {
123
- this.renderCaptcha();
140
+ this.setState({
141
+ isApiReady: true
142
+ }, function () {
143
+ _this2.renderCaptcha();
144
+ });
124
145
  return;
125
146
  }
126
147
 
@@ -130,6 +151,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
130
151
  key: "componentWillUnmount",
131
152
  value: function componentWillUnmount() {
132
153
  var captchaId = this.state.captchaId;
154
+ var hcaptcha = this._hcaptcha;
133
155
 
134
156
  if (!this.isReady()) {
135
157
  return;
@@ -152,18 +174,18 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
152
174
  }, {
153
175
  key: "componentDidUpdate",
154
176
  value: function componentDidUpdate(prevProps) {
155
- var _this2 = this;
177
+ var _this3 = this;
156
178
 
157
179
  // Prop Keys that could change
158
180
  var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint']; // See if any props changed during component update
159
181
 
160
182
  var match = keys.every(function (key) {
161
- return prevProps[key] === _this2.props[key];
183
+ return prevProps[key] === _this3.props[key];
162
184
  }); // If they have changed, remove current captcha and render a new one
163
185
 
164
186
  if (!match) {
165
187
  this.removeCaptcha(function () {
166
- _this2.renderCaptcha();
188
+ _this3.renderCaptcha();
167
189
  });
168
190
  }
169
191
  }
@@ -219,7 +241,8 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
219
241
  }, this.props, {
220
242
  hl: this.props.hl || this.props.languageOverride,
221
243
  languageOverride: undefined
222
- }); //Render hCaptcha widget and provide necessary callbacks - hCaptcha
244
+ });
245
+ var hcaptcha = this._hcaptcha; //Render hCaptcha widget and provide necessary callbacks - hCaptcha
223
246
 
224
247
  var captchaId = hcaptcha.render(this.ref.current, renderParams);
225
248
  this.setState({
@@ -233,6 +256,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
233
256
  key: "resetCaptcha",
234
257
  value: function resetCaptcha() {
235
258
  var captchaId = this.state.captchaId;
259
+ var hcaptcha = this._hcaptcha;
236
260
 
237
261
  if (!this.isReady()) {
238
262
  return;
@@ -245,6 +269,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
245
269
  key: "removeCaptcha",
246
270
  value: function removeCaptcha(callback) {
247
271
  var captchaId = this.state.captchaId;
272
+ var hcaptcha = this._hcaptcha;
248
273
 
249
274
  if (!this.isReady()) {
250
275
  return;
@@ -260,15 +285,18 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
260
285
  }, {
261
286
  key: "handleOnLoad",
262
287
  value: function handleOnLoad() {
263
- var _this3 = this;
288
+ var _this4 = this;
264
289
 
265
290
  this.setState({
266
291
  isApiReady: true
267
292
  }, function () {
268
- // render captcha and wait for captcha id
269
- _this3.renderCaptcha(function () {
293
+ var element = (0, _utils.getMountElement)(_this4.props.scriptLocation);
294
+ var frame = (0, _utils.getFrame)(element);
295
+ _this4._hcaptcha = frame.window.hcaptcha; // render captcha and wait for captcha id
296
+
297
+ _this4.renderCaptcha(function () {
270
298
  // trigger onLoad if it exists
271
- var onLoad = _this3.props.onLoad;
299
+ var onLoad = _this4.props.onLoad;
272
300
  if (onLoad) onLoad();
273
301
  });
274
302
  });
@@ -280,6 +308,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
280
308
  var _this$state = this.state,
281
309
  isRemoved = _this$state.isRemoved,
282
310
  captchaId = _this$state.captchaId;
311
+ var hcaptcha = this._hcaptcha;
283
312
  if (typeof hcaptcha === 'undefined' || isRemoved) return;
284
313
  var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
285
314
 
@@ -292,6 +321,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
292
321
  value: function handleExpire() {
293
322
  var onExpire = this.props.onExpire;
294
323
  var captchaId = this.state.captchaId;
324
+ var hcaptcha = this._hcaptcha;
295
325
 
296
326
  if (!this.isReady()) {
297
327
  return;
@@ -306,6 +336,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
306
336
  value: function handleError(event) {
307
337
  var onError = this.props.onError;
308
338
  var captchaId = this.state.captchaId;
339
+ var hcaptcha = this._hcaptcha;
309
340
 
310
341
  if (this.isReady()) {
311
342
  // If hCaptcha runs into error, reset captcha - hCaptcha
@@ -354,6 +385,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
354
385
  value: function execute() {
355
386
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
356
387
  var captchaId = this.state.captchaId;
388
+ var hcaptcha = this._hcaptcha;
357
389
 
358
390
  if (!this.isReady()) {
359
391
  return;
@@ -369,6 +401,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
369
401
  key: "setData",
370
402
  value: function setData(data) {
371
403
  var captchaId = this.state.captchaId;
404
+ var hcaptcha = this._hcaptcha;
372
405
 
373
406
  if (!this.isReady()) {
374
407
  return;
@@ -383,11 +416,13 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
383
416
  }, {
384
417
  key: "getResponse",
385
418
  value: function getResponse() {
419
+ var hcaptcha = this._hcaptcha;
386
420
  return hcaptcha.getResponse(this.state.captchaId);
387
421
  }
388
422
  }, {
389
423
  key: "getRespKey",
390
424
  value: function getRespKey() {
425
+ var hcaptcha = this._hcaptcha;
391
426
  return hcaptcha.getRespKey(this.state.captchaId);
392
427
  }
393
428
  }, {
package/dist/utils.js CHANGED
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.generateQuery = generateQuery;
9
+ exports.getFrame = getFrame;
10
+ exports.getMountElement = getMountElement;
9
11
 
10
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
13
 
@@ -25,4 +27,17 @@ function generateQuery(params) {
25
27
  }).join("&");
26
28
  }
27
29
 
28
- ;
30
+ ;
31
+
32
+ function getFrame(element) {
33
+ var doc = element && element.ownerDocument || document;
34
+ var win = doc.defaultView || doc.parentWindow || window;
35
+ return {
36
+ document: doc,
37
+ window: win
38
+ };
39
+ }
40
+
41
+ function getMountElement(element) {
42
+ return element || document.head;
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hcaptcha/react-hcaptcha",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "types": "types/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "description": "A React library for hCaptcha",
13
13
  "scripts": {
14
- "start": "webpack serve",
14
+ "start": "webpack serve -c ./demo/webpack.config.mjs",
15
15
  "test": "jest",
16
16
  "watch": "babel src -d dist --copy-files --watch",
17
17
  "transpile": "babel src -d dist --copy-files",
package/src/index.js CHANGED
@@ -1,49 +1,50 @@
1
1
  import * as React from 'react';
2
- import { generateQuery } from "./utils.js";
2
+ import { generateQuery, getFrame, getMountElement } from './utils.js';
3
3
 
4
4
  const SCRIPT_ID = 'hcaptcha-api-script-id';
5
5
  const HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad';
6
6
 
7
7
  // Prevent loading API script multiple times
8
- let resolveFn;
9
- let rejectFn;
10
- const mountPromise = new Promise((resolve, reject) => {
11
- resolveFn = resolve;
12
- rejectFn = reject;
13
- });
8
+ const scripts = [];
14
9
 
15
10
  // Generate hCaptcha API script
16
- const mountCaptchaScript = (params={}) => {
17
- const parent = params.scriptLocation || document.head;
11
+ const mountCaptchaScript = (params = {}) => {
12
+ const element = getMountElement(params.scriptLocation);
18
13
  delete params.scriptLocation;
19
14
 
20
- const doc = parent.ownerDocument || document;
15
+ const frame = getFrame(element);
16
+ const script = scripts.find(({ scope }) => scope === frame.window);
21
17
 
22
- if (doc.getElementById(SCRIPT_ID)) {
18
+ if (frame.document.getElementById(SCRIPT_ID) && script) {
23
19
  // API was already requested
24
- return mountPromise;
20
+ return script.promise;
25
21
  }
26
22
 
27
- // Create global onload callback
28
- window[HCAPTCHA_LOAD_FN_NAME] = resolveFn;
23
+ const promise = new Promise((resolve, reject) => {
24
+ // Create global onload callback
25
+ frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
29
26
 
30
- const domain = params.apihost || "https://js.hcaptcha.com";
31
- delete params.apihost;
27
+ const domain = params.apihost || "https://js.hcaptcha.com";
28
+ delete params.apihost;
32
29
 
33
- const script = doc.createElement("script");
34
- script.id = SCRIPT_ID;
35
- script.src = `${domain}/1/api.js?render=explicit&onload=${HCAPTCHA_LOAD_FN_NAME}`;
30
+ const script = frame.document.createElement("script");
31
+ script.id = SCRIPT_ID;
32
+ script.src = `${domain}/1/api.js?render=explicit&onload=${HCAPTCHA_LOAD_FN_NAME}`;
36
33
 
37
- script.async = params.loadAsync !== undefined? params.loadAsync : true;
38
- delete params.loadAsync;
34
+ script.async = params.loadAsync !== undefined? params.loadAsync : true;
35
+ delete params.loadAsync;
39
36
 
40
- script.onerror = (event) => rejectFn('script-error');
37
+ script.onerror = (event) => reject('script-error');
41
38
 
42
- const query = generateQuery(params);
43
- script.src += query !== ""? `&${query}` : "";
39
+ const query = generateQuery(params);
40
+ script.src += query !== ""? `&${query}` : "";
44
41
 
45
- parent.appendChild(script);
46
- return mountPromise;
42
+ element.appendChild(script);
43
+ });
44
+
45
+ scripts.push({ promise, scope: frame.window });
46
+
47
+ return promise;
47
48
  };
48
49
 
49
50
 
@@ -51,6 +52,14 @@ class HCaptcha extends React.Component {
51
52
  constructor (props) {
52
53
  super(props);
53
54
 
55
+ /**
56
+ * Internal reference to track hCaptcha API
57
+ *
58
+ * Required as window is relative to initialization in application
59
+ * not where the script and iFrames have been loaded.
60
+ */
61
+ this._hcaptcha = undefined;
62
+
54
63
  // API Methods
55
64
  this.renderCaptcha = this.renderCaptcha.bind(this);
56
65
  this.resetCaptcha = this.resetCaptcha.bind(this);
@@ -67,13 +76,11 @@ class HCaptcha extends React.Component {
67
76
  this.handleClose = this.handleClose.bind(this);
68
77
  this.handleChallengeExpired = this.handleChallengeExpired.bind(this);
69
78
 
70
- const isApiReady = typeof hcaptcha !== 'undefined';
71
-
72
79
  this.ref = React.createRef();
73
80
  this.apiScriptRequested = false;
74
81
 
75
82
  this.state = {
76
- isApiReady,
83
+ isApiReady: false,
77
84
  isRemoved: false,
78
85
  elementId: props.id,
79
86
  captchaId: ''
@@ -81,7 +88,11 @@ class HCaptcha extends React.Component {
81
88
  }
82
89
 
83
90
  componentDidMount () { // Once captcha is mounted intialize hCaptcha - hCaptcha
84
- const { isApiReady } = this.state;
91
+ const element = getMountElement(this.props.scriptLocation);
92
+ const frame = getFrame(element);
93
+ this._hcaptcha = frame.window.hcaptcha || undefined;
94
+
95
+ const isApiReady = typeof this._hcaptcha !== 'undefined';
85
96
 
86
97
  /*
87
98
  * Check if hCaptcha has already been loaded,
@@ -89,7 +100,14 @@ class HCaptcha extends React.Component {
89
100
  * If No, create script tag and wait to render the captcha
90
101
  */
91
102
  if (isApiReady) {
92
- this.renderCaptcha();
103
+ this.setState(
104
+ {
105
+ isApiReady: true
106
+ },
107
+ () => {
108
+ this.renderCaptcha();
109
+ }
110
+ );
93
111
 
94
112
  return;
95
113
  }
@@ -99,6 +117,7 @@ class HCaptcha extends React.Component {
99
117
 
100
118
  componentWillUnmount() {
101
119
  const { captchaId } = this.state;
120
+ const hcaptcha = this._hcaptcha;
102
121
 
103
122
  if (!this.isReady()) {
104
123
  return;
@@ -163,7 +182,7 @@ class HCaptcha extends React.Component {
163
182
  sentry,
164
183
  custom,
165
184
  loadAsync,
166
- scriptLocation
185
+ scriptLocation,
167
186
  };
168
187
 
169
188
  mountCaptchaScript(mountParams)
@@ -188,6 +207,7 @@ class HCaptcha extends React.Component {
188
207
  languageOverride: undefined
189
208
  });
190
209
 
210
+ const hcaptcha = this._hcaptcha;
191
211
  //Render hCaptcha widget and provide necessary callbacks - hCaptcha
192
212
  const captchaId = hcaptcha.render(this.ref.current, renderParams);
193
213
 
@@ -198,6 +218,7 @@ class HCaptcha extends React.Component {
198
218
 
199
219
  resetCaptcha() {
200
220
  const { captchaId } = this.state;
221
+ const hcaptcha = this._hcaptcha;
201
222
 
202
223
  if (!this.isReady()) {
203
224
  return;
@@ -208,6 +229,7 @@ class HCaptcha extends React.Component {
208
229
 
209
230
  removeCaptcha(callback) {
210
231
  const { captchaId } = this.state;
232
+ const hcaptcha = this._hcaptcha;
211
233
 
212
234
  if (!this.isReady()) {
213
235
  return;
@@ -221,6 +243,10 @@ class HCaptcha extends React.Component {
221
243
 
222
244
  handleOnLoad () {
223
245
  this.setState({ isApiReady: true }, () => {
246
+ const element = getMountElement(this.props.scriptLocation);
247
+ const frame = getFrame(element);
248
+
249
+ this._hcaptcha = frame.window.hcaptcha;
224
250
 
225
251
  // render captcha and wait for captcha id
226
252
  this.renderCaptcha(() => {
@@ -234,6 +260,7 @@ class HCaptcha extends React.Component {
234
260
  handleSubmit (event) {
235
261
  const { onVerify } = this.props;
236
262
  const { isRemoved, captchaId } = this.state;
263
+ const hcaptcha = this._hcaptcha;
237
264
 
238
265
  if (typeof hcaptcha === 'undefined' || isRemoved) return
239
266
 
@@ -245,6 +272,7 @@ class HCaptcha extends React.Component {
245
272
  handleExpire () {
246
273
  const { onExpire } = this.props;
247
274
  const { captchaId } = this.state;
275
+ const hcaptcha = this._hcaptcha;
248
276
 
249
277
  if (!this.isReady()) {
250
278
  return;
@@ -258,6 +286,7 @@ class HCaptcha extends React.Component {
258
286
  handleError (event) {
259
287
  const { onError } = this.props;
260
288
  const { captchaId } = this.state;
289
+ const hcaptcha = this._hcaptcha;
261
290
 
262
291
  if (this.isReady()) {
263
292
  // If hCaptcha runs into error, reset captcha - hCaptcha
@@ -299,6 +328,7 @@ class HCaptcha extends React.Component {
299
328
 
300
329
  execute (opts = null) {
301
330
  const { captchaId } = this.state;
331
+ const hcaptcha = this._hcaptcha;
302
332
 
303
333
  if (!this.isReady()) {
304
334
  return;
@@ -313,6 +343,7 @@ class HCaptcha extends React.Component {
313
343
 
314
344
  setData (data) {
315
345
  const { captchaId } = this.state;
346
+ const hcaptcha = this._hcaptcha;
316
347
 
317
348
  if (!this.isReady()) {
318
349
  return;
@@ -326,10 +357,12 @@ class HCaptcha extends React.Component {
326
357
  }
327
358
 
328
359
  getResponse() {
360
+ const hcaptcha = this._hcaptcha;
329
361
  return hcaptcha.getResponse(this.state.captchaId);
330
362
  }
331
363
 
332
364
  getRespKey() {
365
+ const hcaptcha = this._hcaptcha;
333
366
  return hcaptcha.getRespKey(this.state.captchaId)
334
367
  }
335
368
 
package/src/utils.js CHANGED
@@ -1,9 +1,24 @@
1
1
  function generateQuery(params) {
2
- return Object.entries(params)
3
- .filter(([key, value]) => value || value === false)
4
- .map(([key, value]) => {
5
- return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
6
- }).join("&");
2
+ return Object.entries(params)
3
+ .filter(([key, value]) => value || value === false)
4
+ .map(([key, value]) => {
5
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
6
+ }).join("&");
7
7
  };
8
8
 
9
- export { generateQuery };
9
+ function getFrame(element) {
10
+ const doc = (element && element.ownerDocument) || document;
11
+ const win = doc.defaultView || doc.parentWindow || window;
12
+
13
+ return { document: doc, window: win };
14
+ }
15
+
16
+ function getMountElement(element) {
17
+ return element || document.head;
18
+ }
19
+
20
+ export {
21
+ generateQuery,
22
+ getFrame,
23
+ getMountElement
24
+ };