1/** 2 * @license angular-recaptcha build:2018-05-09 3 * https://github.com/vividcortex/angular-recaptcha 4 * Copyright (c) 2018 VividCortex 5 **/ 6 7/*global angular, Recaptcha */ (function (ng) { 8 'use strict'; 9 10 ng.module('vcRecaptcha', []); 11 12}(angular)); 13 14/*global angular */ (function (ng) { 15 'use strict'; 16 17 function throwNoKeyException() { 18 throw new Error('You need to set the "key" attribute to your public reCaptcha key. If you don\'t have a key, please get one from https://www.google.com/recaptcha/admin/create'); 19 } 20 21 var app = ng.module('vcRecaptcha'); 22 23 /** 24 * An angular service to wrap the reCaptcha API 25 */ 26 app.provider('vcRecaptchaService', function () { 27 var provider = this; 28 var config = {}; 29 provider.onLoadFunctionName = 'vcRecaptchaApiLoaded'; 30 31 /** 32 * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. 33 * 34 * @since 2.5.0 35 * @param defaults object which overrides the current defaults object. 36 */ 37 provider.setDefaults = function (defaults) { 38 ng.copy(defaults, config); 39 }; 40 41 /** 42 * Sets the reCaptcha key which will be used by default is not specified in a specific directive instance. 43 * 44 * @since 2.5.0 45 * @param siteKey the reCaptcha public key (refer to the README file if you don't know what this is). 46 */ 47 provider.setSiteKey = function (siteKey) { 48 config.key = siteKey; 49 }; 50 51 /** 52 * Sets the reCaptcha theme which will be used by default is not specified in a specific directive instance. 53 * 54 * @since 2.5.0 55 * @param theme The reCaptcha theme. 56 */ 57 provider.setTheme = function (theme) { 58 config.theme = theme; 59 }; 60 61 /** 62 * Sets the reCaptcha stoken which will be used by default is not specified in a specific directive instance. 63 * 64 * @since 2.5.0 65 * @param stoken The reCaptcha stoken. 66 */ 67 provider.setStoken = function (stoken) { 68 config.stoken = stoken; 69 }; 70 71 /** 72 * Sets the reCaptcha size which will be used by default is not specified in a specific directive instance. 73 * 74 * @since 2.5.0 75 * @param size The reCaptcha size. 76 */ 77 provider.setSize = function (size) { 78 config.size = size; 79 }; 80 81 /** 82 * Sets the reCaptcha type which will be used by default is not specified in a specific directive instance. 83 * 84 * @since 2.5.0 85 * @param type The reCaptcha type. 86 */ 87 provider.setType = function (type) { 88 config.type = type; 89 }; 90 91 /** 92 * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. 93 * 94 * @param lang The reCaptcha language. 95 */ 96 provider.setLang = function (lang) { 97 config.lang = lang; 98 }; 99 100 /** 101 * Sets the reCaptcha badge position which will be used by default if not specified in a specific directive instance. 102 * 103 * @param badge The reCaptcha badge position. 104 */ 105 provider.setBadge = function (badge) { 106 config.badge = badge; 107 }; 108 109 /** 110 * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. 111 * 112 * @since 2.5.0 113 * @param onLoadFunctionName string name which overrides the name of the onload function. Should match what is in the recaptcha script querystring onload value. 114 */ 115 provider.setOnLoadFunctionName = function (onLoadFunctionName) { 116 provider.onLoadFunctionName = onLoadFunctionName; 117 }; 118 119 provider.$get = ['$rootScope', '$window', '$q', '$document', '$interval', function ($rootScope, $window, $q, $document, $interval) { 120 var deferred = $q.defer(), 121 promise = deferred.promise, 122 instances = {}, recaptcha; 123 124 $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; 125 126 var callback = function () { 127 recaptcha = $window.grecaptcha; 128 deferred.resolve(recaptcha); 129 }; 130 131 $window.vcRecaptchaApiLoadedCallback.push(callback); 132 133 $window[provider.onLoadFunctionName] = function () { 134 $window.vcRecaptchaApiLoadedCallback.forEach(function (callback) { 135 callback(); 136 }); 137 }; 138 139 140 function getRecaptcha() { 141 if (!!recaptcha) { 142 return $q.when(recaptcha); 143 } 144 145 return promise; 146 } 147 148 function validateRecaptchaInstance() { 149 if (!recaptcha) { 150 throw new Error('reCaptcha has not been loaded yet.'); 151 } 152 } 153 154 function isRenderFunctionAvailable() { 155 return ng.isFunction(($window.grecaptcha || {}).render); 156 } 157 158 159 // Check if grecaptcha.render is not defined already. 160 if (isRenderFunctionAvailable()) { 161 callback(); 162 } else if ($window.document.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]')) { 163 // wait for script to be loaded. 164 var intervalWait = $interval(function () { 165 if (isRenderFunctionAvailable()) { 166 $interval.cancel(intervalWait); 167 callback(); 168 } 169 }, 25); 170 } else { 171 // Generate link on demand 172 var script = $window.document.createElement('script'); 173 script.async = true; 174 script.defer = true; 175 script.src = 'https://www.google.com/recaptcha/api.js?onload=' + provider.onLoadFunctionName + '&render=explicit'; 176 $document.find('body')[0].appendChild(script); 177 } 178 179 return { 180 181 /** 182 * Creates a new reCaptcha object 183 * 184 * @param elm the DOM element where to put the captcha
185 * @param conf the captcha object configuration 186 * @throws NoKeyException if no key is provided in the provider config or the directive instance (via attribute) 187 */ 188 create: function (elm, conf) { 189 190 conf.sitekey = conf.key || config.key; 191 conf.theme = conf.theme || config.theme; 192 conf.stoken = conf.stoken || config.stoken; 193 conf.size = conf.size || config.size; 194 conf.type = conf.type || config.type; 195 conf.hl = conf.lang || config.lang; 196 conf.badge = conf.badge || config.badge; 197 198 if (!conf.sitekey) { 199 throwNoKeyException(); 200 } 201 return getRecaptcha().then(function (recaptcha) { 202 var widgetId = recaptcha.render(elm, conf); 203 instances[widgetId] = elm; 204 return widgetId; 205 }); 206 }, 207 208 /** 209 * Reloads the reCaptcha 210 */ 211 reload: function (widgetId) { 212 validateRecaptchaInstance(); 213 214 recaptcha.reset(widgetId); 215 216 // Let everyone know this widget has been reset. 217 $rootScope.$broadcast('reCaptchaReset', widgetId); 218 }, 219 220 /** 221 * Executes the reCaptcha 222 */ 223 execute: function (widgetId) { 224 validateRecaptchaInstance(); 225 console.log(new Date()); 226 recaptcha.execute(widgetId); 227 }, 228 229 /** 230 * Get/Set reCaptcha language 231 */ 232 useLang: function (widgetId, lang) { 233 var instance = instances[widgetId]; 234 235 if (instance) { 236 var iframe = instance.querySelector('iframe'); 237 if (lang) { 238 // Setter 239 if (iframe && iframe.src) { 240 var s = iframe.src; 241 if (/[?&]hl=/.test(s)) { 242 s = s.replace(/([?&]hl=)\w+/, '$1' + lang); 243 } else { 244 s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; 245 } 246 247 iframe.src = s; 248 } 249 } else { 250 // Getter 251 if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { 252 return iframe.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/, '$1'); 253 } else { 254 return null; 255 } 256 } 257 } else { 258 throw new Error('reCaptcha Widget ID not exists', widgetId); 259 } 260 }, 261 262 /** 263 * Gets the response from the reCaptcha widget. 264 * 265 * @see https://developers.google.com/recaptcha/docs/display#js_api 266 * 267 * @returns {String} 268 */ 269 getResponse: function (widgetId) { 270 validateRecaptchaInstance(); 271 var resp = recaptcha.getResponse(widgetId); 272 return resp; 273 }, 274 275 /** 276 * Gets reCaptcha instance and configuration 277 */ 278 getInstance: function (widgetId) { 279 return instances[widgetId]; 280 }, 281 282 /** 283 * Destroy reCaptcha instance. 284 */ 285 destroy: function (widgetId) { 286 delete instances[widgetId]; 287 } 288 }; 289 290 }]; 291 });
292 293}(angular)); 294 295/*global angular, Recaptcha */ (function (ng) { 296 'use strict'; 297 298 var app = ng.module('vcRecaptcha'); 299 300 app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', function ($document, $timeout, vcRecaptcha) { 301 302 return { 303 restrict: 'A', 304 require: "?^^form", 305 scope: { 306 response: '=?ngModel', 307 key: '=?', 308 stoken: '=?', 309 theme: '=?', 310 size: '=?', 311 type: '=?', 312 lang: '=?', 313 badge: '=?', 314 tabindex: '=?', 315 required: '=?', 316 onCreate: '&', 317 onSuccess: '&', 318 onExpire: '&' 319 }, 320 link: function (scope, elm, attrs, ctrl) { 321 scope.widgetId = null; 322 323 if (ctrl && ng.isDefined(attrs.required)) { 324 scope.$watch('required', validate); 325 } 326 327 var removeCreationListener = scope.$watch('key', function (key) { 328 var callback = function (gRecaptchaResponse) { 329 // Safe $apply 330 $timeout(function () { 331 scope.response = gRecaptchaResponse; 332 validate(); 333 334 // Notify about the response availability 335 scope.onSuccess({ 336 response: gRecaptchaResponse, 337 widgetId: scope.widgetId 338 }); 339 }); 340 }; 341 342 vcRecaptcha.create(elm[0], { 343 344 callback: callback, 345 key: key, 346 stoken: scope.stoken || attrs.stoken || null, 347 theme: scope.theme || attrs.theme || null, 348 type: scope.type || attrs.type || null, 349 lang: scope.lang || attrs.lang || null, 350 tabindex: scope.tabindex || attrs.tabindex || null, 351 size: scope.size || attrs.size || null, 352 badge: scope.badge || attrs.badge || null, 353 'expired-callback': expired 354 355 }).then(function (widgetId) { 356 // The widget has been created 357 validate(); 358 scope.widgetId = widgetId; 359 scope.onCreate({ 360 widgetId: widgetId 361 }); 362 363 scope.$on('$destroy', destroy); 364 365 scope.$on('reCaptchaReset', function (event, resetWidgetId) { 366 if (ng.isUndefined(resetWidgetId) || widgetId === resetWidgetId) { 367 scope.response = ""; 368 validate(); 369 } 370 }) 371 372 }); 373 374 // Remove this listener to avoid creating the widget more than once. 375 removeCreationListener(); 376 }); 377 378 function destroy() { 379 if (ctrl) { 380 // reset the validity of the form if we were removed 381 ctrl.$setValidity('recaptcha', null); 382 } 383 384 cleanup(); 385 } 386 387 function expired() { 388 // Safe $apply 389 $timeout(function () { 390 scope.response = ""; 391 validate(); 392 393 // Notify about the response availability 394 scope.onExpire({ 395 widgetId: scope.widgetId 396 }); 397 }); 398 } 399 400 function validate() { 401 if (ctrl) { 402 ctrl.$setValidity('recaptcha', scope.required === false ? null : Boolean(scope.response)); 403 } 404 } 405 406 function cleanup() { 407 vcRecaptcha.destroy(scope.widgetId); 408 409 // removes elements reCaptcha added. 410 ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); 411 } 412 } 413 }; 414 }]); 415 416}(angular));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.