reCaptcha.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * MageSpecialist
  3. *
  4. * NOTICE OF LICENSE
  5. *
  6. * This source file is subject to the Open Software License (OSL 3.0)
  7. * that is bundled with this package in the file LICENSE.txt.
  8. * It is also available through the world-wide-web at this URL:
  9. * http://opensource.org/licenses/osl-3.0.php
  10. * If you did not receive a copy of the license and are unable to
  11. * obtain it through the world-wide-web, please send an email
  12. * to info@magespecialist.it so we can send you a copy immediately.
  13. *
  14. * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)
  15. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  16. */
  17. 'use strict';
  18. define(
  19. [
  20. 'uiComponent',
  21. 'jquery',
  22. 'ko',
  23. 'MSP_ReCaptcha/js/registry'
  24. ],
  25. function (Component, $, ko, registry, undefined) {
  26. return Component.extend({
  27. defaults: {
  28. template: 'MSP_ReCaptcha/reCaptcha'
  29. },
  30. _isApiRegistered: undefined,
  31. initialize: function () {
  32. this._super();
  33. this._loadApi();
  34. },
  35. /**
  36. * Loads recaptchaapi API and triggers event, when loaded
  37. * @private
  38. */
  39. _loadApi: function () {
  40. var element, scriptTag;
  41. if (this._isApiRegistered !== undefined) {
  42. if (this._isApiRegistered === true) {
  43. $(window).trigger('recaptchaapiready');
  44. }
  45. return;
  46. }
  47. this._isApiRegistered = false;
  48. // global function
  49. window.globalOnRecaptchaOnLoadCallback = function() {
  50. this._isApiRegistered = true;
  51. $(window).trigger('recaptchaapiready');
  52. }.bind(this);
  53. element = document.createElement('script');
  54. scriptTag = document.getElementsByTagName('script')[0];
  55. element.async = true;
  56. element.src = 'https://www.google.com/recaptcha/api.js'
  57. + '?onload=globalOnRecaptchaOnLoadCallback&render=explicit'
  58. + (this.settings.lang ? '&hl=' + this.settings.lang : '');
  59. scriptTag.parentNode.insertBefore(element, scriptTag);
  60. },
  61. /**
  62. * Return true if reCaptcha is visible
  63. * @returns {Boolean}
  64. */
  65. getIsVisible: function () {
  66. return this.settings.enabled[this.zone];
  67. },
  68. /**
  69. * Recaptcha callback
  70. * @param {String} token
  71. */
  72. reCaptchaCallback: function (token) {
  73. if (this.settings.size === 'invisible') {
  74. this.tokenField.value = token;
  75. this.$parentForm.submit();
  76. }
  77. },
  78. /**
  79. * Initialize reCaptcha after first rendering
  80. */
  81. initCaptcha: function () {
  82. var me = this,
  83. $parentForm,
  84. $wrapper,
  85. $reCaptcha,
  86. widgetId,
  87. listeners;
  88. if (this.captchaInitialized) {
  89. return;
  90. }
  91. this.captchaInitialized = true;
  92. /*
  93. * Workaround for data-bind issue:
  94. * We cannot use data-bind to link a dynamic id to our component
  95. * See: https://stackoverflow.com/questions/46657573/recaptcha-the-bind-parameter-must-be-an-element-or-id
  96. *
  97. * We create a wrapper element with a wrapping id and we inject the real ID with jQuery.
  98. * In this way we have no data-bind attribute at all in our reCaptcha div
  99. */
  100. $wrapper = $('#' + this.getReCaptchaId() + '-wrapper');
  101. $reCaptcha = $wrapper.find('.g-recaptcha');
  102. $reCaptcha.attr('id', this.getReCaptchaId());
  103. $parentForm = $wrapper.parents('form');
  104. me = this;
  105. // eslint-disable-next-line no-undef
  106. widgetId = grecaptcha.render(this.getReCaptchaId(), {
  107. 'sitekey': this.settings.siteKey,
  108. 'theme': this.settings.theme,
  109. 'size': this.settings.size,
  110. 'badge': this.badge ? this.badge : this.settings.badge,
  111. 'callback': function (token) { // jscs:ignore jsDoc
  112. me.reCaptchaCallback(token);
  113. }
  114. });
  115. if (this.settings.size === 'invisible') {
  116. $parentForm.submit(function (event) {
  117. if (!me.tokenField.value) {
  118. // eslint-disable-next-line no-undef
  119. grecaptcha.execute(widgetId);
  120. event.preventDefault(event);
  121. event.stopImmediatePropagation();
  122. }
  123. });
  124. // Move our (last) handler topmost. We need this to avoid submit bindings with ko.
  125. listeners = $._data($parentForm[0], 'events').submit;
  126. listeners.unshift(listeners.pop());
  127. // Create a virtual token field
  128. this.tokenField = $('<input type="text" name="token" style="display: none" />')[0];
  129. this.$parentForm = $parentForm;
  130. $parentForm.append(this.tokenField);
  131. } else {
  132. this.tokenField = null;
  133. }
  134. registry.ids.push(this.getReCaptchaId());
  135. registry.captchaList.push(widgetId);
  136. registry.tokenFields.push(this.tokenField);
  137. },
  138. /**
  139. * Render reCaptcha
  140. */
  141. renderReCaptcha: function () {
  142. var me = this;
  143. if (this.getIsVisible()) {
  144. if (window.grecaptcha && window.grecaptcha.render) { // Check if recaptcha is already loaded
  145. me.initCaptcha();
  146. } else { // Wait for recaptcha to be loaded
  147. $(window).on('recaptchaapiready', function () {
  148. me.initCaptcha();
  149. });
  150. }
  151. }
  152. },
  153. /**
  154. * Get reCaptcha ID
  155. * @returns {String}
  156. */
  157. getReCaptchaId: function () {
  158. if (!this.reCaptchaId) {
  159. return 'msp-recaptcha';
  160. }
  161. return this.reCaptchaId;
  162. }
  163. });
  164. }
  165. );