From aa95bdffe0244e4b9870935a1e808efaca3c8289 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 13:49:32 -0300 Subject: [PATCH 01/31] Language setter --- README.md | 9 ++++++++- src/directive.js | 2 ++ src/service.js | 25 +++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65e820b..cddf4fc 100644 --- a/README.md +++ b/README.md @@ -131,9 +131,12 @@ You can optionally pass a __theme__ the captcha should use, as an html attribute size="---- compact or normal ----" type="'---- audio or image ----'" key="'---- YOUR PUBLIC KEY GOES HERE ----'" + lang="---- language code ----" > ``` +**Language Codes**: https://developers.google.com/recaptcha/docs/language + In this case we are specifying that the captcha should use the theme named _light_. Listeners @@ -205,9 +208,12 @@ myApp.config(function(vcRecaptchaServiceProvider){ vcRecaptchaServiceProvider.setStoken('--- YOUR GENERATED SECURE TOKEN ---') vcRecaptchaServiceProvider.setSize('---- compact or normal ----') vcRecaptchaServiceProvider.setType('---- audio or image ----') + vcRecaptchaServiceProvider.setLang('---- language code ----') }); ``` +**Language Codes**: https://developers.google.com/recaptcha/docs/language + You can also set all of the values at once. ```javascript @@ -217,7 +223,8 @@ myApp.config(function(vcRecaptchaServiceProvider){ theme: '---- light or dark ----', stoken: '--- YOUR GENERATED SECURE TOKEN ---', size: '---- compact or normal ----', - type: '---- audio or image ----' + type: '---- audio or image ----', + lang: '---- language code ----' }); ``` Note: any value omitted will be undefined, even if previously set. diff --git a/src/directive.js b/src/directive.js index 66b81ea..941bb1a 100644 --- a/src/directive.js +++ b/src/directive.js @@ -16,6 +16,7 @@ theme: '=?', size: '=?', type: '=?', + lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -48,6 +49,7 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, + lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired diff --git a/src/service.js b/src/service.js index 6f3aa0e..7ce9ad8 100644 --- a/src/service.js +++ b/src/service.js @@ -76,6 +76,15 @@ config.type = type; }; + /** + * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. + * + * @param lang The reCaptcha language. + */ + provider.setLang = function(lang){ + config.lang = lang; + }; + /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -115,7 +124,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha) { + if (!recaptcha || !window.___grecaptcha_cfg) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -142,6 +151,7 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; + conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); @@ -157,13 +167,24 @@ reload: function (widgetId) { validateRecaptchaInstance(); - // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, + /** + * Set reCaptcha language + */ + setLang: function (widgetId, lang) { + var cli = window.___grecaptcha_cfg.clients[widgetId]; + if (cli) { + var wk = cli.W.wk; + wk.lang = wk.hl = lang; + this.reload(widgetId); + } + }, + /** * Gets the response from the reCaptcha widget. * From 4e480d83fd9cc37c789bfeba6f0203f57400bb72 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 13:58:57 -0300 Subject: [PATCH 02/31] Set Language - Compile. --- package.json | 7 +++++++ release/angular-recaptcha.js | 33 +++++++++++++++++++++++++++----- release/angular-recaptcha.min.js | 8 ++++---- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index ea064cd..82dd357 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,13 @@ "author": "VividCortex", "license": "MIT", "homepage": "https://github.com/vividcortex/angular-recaptcha", + "contributors": [ + { + "name" : "Eduardo Daniel Cuomo", + "email" : "reduardo7@gmail.com", + "url" : "https://github.com/reduardo7/angular-recaptcha" + } + ], "main": "release/angular-recaptcha.js", "repository": { "type": "git", diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index d90dd39..c562c9e 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-11-30 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ /*global angular, Recaptcha */ @@ -90,6 +90,15 @@ config.type = type; }; + /** + * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. + * + * @param lang The reCaptcha language. + */ + provider.setLang = function(lang){ + config.lang = lang; + }; + /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -129,7 +138,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha) { + if (!recaptcha || !window.___grecaptcha_cfg) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -156,6 +165,7 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; + conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); @@ -171,13 +181,24 @@ reload: function (widgetId) { validateRecaptchaInstance(); - // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, + /** + * Set reCaptcha language + */ + setLang: function (widgetId, lang) { + var cli = window.___grecaptcha_cfg.clients[widgetId]; + if (cli) { + var wk = cli.W.wk; + wk.lang = wk.hl = lang; + this.reload(widgetId); + } + }, + /** * Gets the response from the reCaptcha widget. * @@ -215,6 +236,7 @@ theme: '=?', size: '=?', type: '=?', + lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -247,6 +269,7 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, + lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index b7761aa..8fd7879 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-11-30 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j||!window.___grecaptcha_cfg)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},setLang:function(a,b){var c=window.___grecaptcha_cfg.clients[a];if(c){var d=c.W.wk;d.lang=d.hl=b,this.reload(a)}},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 82da69633b289e221cef1fc01864dd5a45a98a6b Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:13:43 -0300 Subject: [PATCH 03/31] Language setter and getter --- README.md | 14 ++++---- release/angular-recaptcha.js | 59 ++++++++++++++++++++++++++------ release/angular-recaptcha.min.js | 2 +- src/directive.js | 2 ++ src/service.js | 57 ++++++++++++++++++++++++------ 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index cddf4fc..d0ce05b 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ There are three listeners you can use with the directive, `on-create`, `on-succe on-create="setWidgetId(widgetId)" on-success="setResponse(response)" on-expire="cbExpiration()" + lang="" > ``` @@ -219,12 +220,13 @@ You can also set all of the values at once. ```javascript myApp.config(function(vcRecaptchaServiceProvider){ vcRecaptchaServiceProvider.setDefaults({ - key: '---- YOUR PUBLIC KEY GOES HERE ----', - theme: '---- light or dark ----', - stoken: '--- YOUR GENERATED SECURE TOKEN ---', - size: '---- compact or normal ----', - type: '---- audio or image ----', - lang: '---- language code ----' + key: '---- YOUR PUBLIC KEY GOES HERE ----', + theme: '---- light or dark ----', + stoken: '--- YOUR GENERATED SECURE TOKEN ---', + size: '---- compact or normal ----', + type: '---- audio or image ----', + lang: '---- language code ----' + }); }); ``` Note: any value omitted will be undefined, even if previously set. diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index c562c9e..925fc40 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -110,7 +110,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -138,7 +138,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha || !window.___grecaptcha_cfg) { + if (!recaptcha) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -171,7 +171,9 @@ throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - return recaptcha.render(elm, conf); + var widgetId = recaptcha.render(elm, conf); + instances[widgetId] = elm; + return widgetId; }); }, @@ -188,14 +190,35 @@ }, /** - * Set reCaptcha language + * Get/Set reCaptcha language */ - setLang: function (widgetId, lang) { - var cli = window.___grecaptcha_cfg.clients[widgetId]; - if (cli) { - var wk = cli.W.wk; - wk.lang = wk.hl = lang; - this.reload(widgetId); + useLang: function (widgetId, lang) { + var instance = instances[widgetId]; + + if (instance) { + var iframe = instance.querySelector('iframe'); + if (lang) { + // Setter + if (iframe && iframe.src) { + var s = iframe.src; + if (/[?&]hl=/.test(s)) { + s = s.replace(/([?&]hl=)\w+/, '$1' + lang); + } else { + s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; + } + + iframe.src = s; + } + } else { + // Getter + if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { + return iframe.src.replace(/[?&]hl=(\w+)/, '$1'); + } else { + return null; + } + } + } else { + throw new Error('reCaptcha Widget ID not exists', widgetId); } }, @@ -210,6 +233,20 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); + }, + + /** + * Gets reCaptcha instance and configuration + */ + getInstance: function (widgetId) { + return instances[widgetId]; + }, + + /** + * Destroy reCaptcha instance. + */ + destroy: function (widgetId) { + delete instances[widgetId]; } }; @@ -322,6 +359,8 @@ } function cleanup(){ + vcRecaptcha.destroy(scope.widgetId); + // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 8fd7879..64dfc43 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -4,4 +4,4 @@ * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j||!window.___grecaptcha_cfg)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},setLang:function(a,b){var c=window.___grecaptcha_cfg.clients[a];if(c){var d=c.W.wk;d.lang=d.hl=b,this.reload(a)}},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/[?&]hl=(\w+)/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file diff --git a/src/directive.js b/src/directive.js index 941bb1a..1bc9d50 100644 --- a/src/directive.js +++ b/src/directive.js @@ -102,6 +102,8 @@ } function cleanup(){ + vcRecaptcha.destroy(scope.widgetId); + // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/src/service.js b/src/service.js index 7ce9ad8..0d6bb33 100644 --- a/src/service.js +++ b/src/service.js @@ -96,7 +96,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -124,7 +124,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha || !window.___grecaptcha_cfg) { + if (!recaptcha) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -157,7 +157,9 @@ throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - return recaptcha.render(elm, conf); + var widgetId = recaptcha.render(elm, conf); + instances[widgetId] = elm; + return widgetId; }); }, @@ -174,14 +176,35 @@ }, /** - * Set reCaptcha language + * Get/Set reCaptcha language */ - setLang: function (widgetId, lang) { - var cli = window.___grecaptcha_cfg.clients[widgetId]; - if (cli) { - var wk = cli.W.wk; - wk.lang = wk.hl = lang; - this.reload(widgetId); + useLang: function (widgetId, lang) { + var instance = instances[widgetId]; + + if (instance) { + var iframe = instance.querySelector('iframe'); + if (lang) { + // Setter + if (iframe && iframe.src) { + var s = iframe.src; + if (/[?&]hl=/.test(s)) { + s = s.replace(/([?&]hl=)\w+/, '$1' + lang); + } else { + s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; + } + + iframe.src = s; + } + } else { + // Getter + if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { + return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); + } else { + return null; + } + } + } else { + throw new Error('reCaptcha Widget ID not exists', widgetId); } }, @@ -196,6 +219,20 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); + }, + + /** + * Gets reCaptcha instance and configuration + */ + getInstance: function (widgetId) { + return instances[widgetId]; + }, + + /** + * Destroy reCaptcha instance. + */ + destroy: function (widgetId) { + delete instances[widgetId]; } }; From df557290c7d6e1c827d1c3acc76668e18fd19671 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:14:50 -0300 Subject: [PATCH 04/31] Language setter and getter - Compile --- release/angular-recaptcha.js | 2 +- release/angular-recaptcha.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index 925fc40..fea5ad9 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -212,7 +212,7 @@ } else { // Getter if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/[?&]hl=(\w+)/, '$1'); + return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); } else { return null; } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 64dfc43..96fdc41 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -4,4 +4,4 @@ * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/[?&]hl=(\w+)/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+).+/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 99ea150c8449c49f703adb1cb36df8a7d10555cd Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:23:51 -0300 Subject: [PATCH 05/31] Revert release. --- release/angular-recaptcha.js | 74 +++----------------------------- release/angular-recaptcha.min.js | 8 ++-- 2 files changed, 10 insertions(+), 72 deletions(-) diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index fea5ad9..d90dd39 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-11-30 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * angular-recaptcha build:2016-07-19 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ /*global angular, Recaptcha */ @@ -90,15 +90,6 @@ config.type = type; }; - /** - * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. - * - * @param lang The reCaptcha language. - */ - provider.setLang = function(lang){ - config.lang = lang; - }; - /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -110,7 +101,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -165,15 +156,12 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; - conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - var widgetId = recaptcha.render(elm, conf); - instances[widgetId] = elm; - return widgetId; + return recaptcha.render(elm, conf); }); }, @@ -183,45 +171,13 @@ reload: function (widgetId) { validateRecaptchaInstance(); + // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, - /** - * Get/Set reCaptcha language - */ - useLang: function (widgetId, lang) { - var instance = instances[widgetId]; - - if (instance) { - var iframe = instance.querySelector('iframe'); - if (lang) { - // Setter - if (iframe && iframe.src) { - var s = iframe.src; - if (/[?&]hl=/.test(s)) { - s = s.replace(/([?&]hl=)\w+/, '$1' + lang); - } else { - s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; - } - - iframe.src = s; - } - } else { - // Getter - if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); - } else { - return null; - } - } - } else { - throw new Error('reCaptcha Widget ID not exists', widgetId); - } - }, - /** * Gets the response from the reCaptcha widget. * @@ -233,20 +189,6 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); - }, - - /** - * Gets reCaptcha instance and configuration - */ - getInstance: function (widgetId) { - return instances[widgetId]; - }, - - /** - * Destroy reCaptcha instance. - */ - destroy: function (widgetId) { - delete instances[widgetId]; } }; @@ -273,7 +215,6 @@ theme: '=?', size: '=?', type: '=?', - lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -306,7 +247,6 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, - lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired @@ -359,8 +299,6 @@ } function cleanup(){ - vcRecaptcha.destroy(scope.widgetId); - // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 96fdc41..b7761aa 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-11-30 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * angular-recaptcha build:2016-07-19 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+).+/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 4764a4d836622534470796868710b557dcf2aa1e Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Thu, 1 Dec 2016 13:45:47 -0300 Subject: [PATCH 06/31] Language tests --- src/service.js | 2 +- tests/directive_test.js | 27 +++++++++++++++++++++++++++ tests/service_test.js | 24 +++++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/service.js b/src/service.js index 0d6bb33..27956dd 100644 --- a/src/service.js +++ b/src/service.js @@ -198,7 +198,7 @@ } else { // Getter if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); + return iframe.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/, '$1'); } else { return null; } diff --git a/tests/directive_test.js b/tests/directive_test.js index 6191ad9..accc812 100644 --- a/tests/directive_test.js +++ b/tests/directive_test.js @@ -186,5 +186,32 @@ describe('directive: vcRecaptcha', function () { widgetId: undefined }); }); + + it('the widget should be using the setted language', function () { + var element = angular.element('
' + + '' + + '
' + + ''), + + _fakeCreate = function (element, config) { + config.callback(config.lang); + return { + then: function (cb) { + cb(); + } + }; + }; + + spyOn(vcRecaptchaService, 'create').and.callFake(_fakeCreate); + + $compile(element)($scope); + $scope.$digest(); + $timeout.flush(); + + expect($scope.onSuccess).toHaveBeenCalledWith({ + response: 'es', + widgetId: undefined + }); + }); }); }); diff --git a/tests/service_test.js b/tests/service_test.js index 62ca9d1..4f69153 100644 --- a/tests/service_test.js +++ b/tests/service_test.js @@ -28,7 +28,8 @@ describe('service', function () { theme: undefined, stoken: undefined, size: undefined, - type: undefined + type: undefined, + hl: undefined }; $window.vcRecaptchaApiLoaded(); @@ -63,4 +64,25 @@ describe('service', function () { expect($window.grecaptcha.getResponse).toHaveBeenCalledWith(_widgetId); }); }); + + describe('useLang', function () { + it('should call useLang', inject(function ($rootScope) { + var _element = angular.element('
')[0], + _key = '1234567890123456789012345678901234567890'; + + $window.vcRecaptchaApiLoaded(); + + vcRecaptchaService.create(_element, { + key: _key + }).then(function (widgetId) { + var instance = vcRecaptchaService.getInstance(widgetId); + expect(instance).toEqual(_element); + + vcRecaptchaService.useLang(widgetId, 'es'); + expect(vcRecaptchaService.useLang(widgetId)).toEqual('es'); + }) + + $rootScope.$digest(); + })); + }); }); From 19508e05b95c6a1b585c2fb45325bd6241ea153a Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Thu, 1 Dec 2016 15:03:01 -0500 Subject: [PATCH 07/31] chore(deps): update devDeps --- package.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index ea064cd..381bba6 100644 --- a/package.json +++ b/package.json @@ -14,23 +14,23 @@ "test": "grunt test" }, "devDependencies": { - "bower": "^1.3.3", - "grunt": "~0.4.2", - "grunt-bump": "0.0.13", - "grunt-cli": "~0.1.11", - "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-jshint": "~0.8.0", - "grunt-contrib-uglify": "~0.4.0", - "grunt-karma": "^0.10.1", - "grunt-karma-coveralls": "^2.5.3", - "jasmine-core": "^2.2.0", - "karma": "^0.12.31", - "karma-chrome-launcher": "^0.1.7", - "karma-coverage": "^0.2.7", - "karma-firefox-launcher": "^0.1.4", - "karma-ie-launcher": "^0.1.5", - "karma-jasmine": "^0.3.5", - "karma-phantomjs-launcher": "^0.1.4", - "karma-safari-launcher": "^0.1.1" + "bower": "^1.8.0", + "grunt": "^1.0.1", + "grunt-bump": "^0.8.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-jshint": "^1.1.0", + "grunt-contrib-uglify": "^2.0.0", + "grunt-karma": "^2.0.0", + "grunt-karma-coveralls": "^2.5.4", + "jasmine-core": "^2.5.2", + "karma": "^1.3.0", + "karma-chrome-launcher": "^2.0.0", + "karma-coverage": "^1.1.1", + "karma-firefox-launcher": "^1.0.0", + "karma-ie-launcher": "^1.0.0", + "karma-jasmine": "^1.0.2", + "karma-phantomjs-launcher": "^1.0.2", + "karma-safari-launcher": "^1.0.0" } } From 79c19e0f953aad027ccc50405385f30c7d9084c5 Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Thu, 1 Dec 2016 15:18:40 -0500 Subject: [PATCH 08/31] fix(ci): fix chrome in travis --- .travis.yml | 1 + Gruntfile.js | 2 +- karma.conf.js | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3d72df1..5c60988 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ node_js: before_script: - npm install -g grunt-cli bower - bower install + - "export CHROME_BIN=chromium-browser" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" diff --git a/Gruntfile.js b/Gruntfile.js index 82606fd..54762f3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,7 +52,7 @@ module.exports = function (grunt) { }, ci: { configFile: 'karma.conf.js', - browsers: ['Chrome', 'Firefox', 'FirefoxNightly'], + browsers: ['Chrome_travis_ci', 'Firefox', 'FirefoxNightly'], singleRun: true } }, diff --git a/karma.conf.js b/karma.conf.js index 33425f1..5b8e794 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -70,6 +70,12 @@ module.exports = function (config) { // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['PhantomJS', 'Chrome', 'IE', 'Safari', 'Firefox', 'FirefoxNightly', 'ChromeCanary'], + customLaunchers: { + Chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits From db8c090e535f5688de87f00490b4cd65658d2bb5 Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Thu, 1 Dec 2016 15:33:16 -0500 Subject: [PATCH 09/31] chore(coverage): report coverage on CI build --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5c60988..fe4a4eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,6 @@ before_script: script: - grunt karma:ci + +after_script: + - grunt coverage From 973351197d837e2e7d5b04b1f62c8814718939c9 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 13:49:32 -0300 Subject: [PATCH 10/31] Language setter --- README.md | 9 ++++++++- src/directive.js | 2 ++ src/service.js | 25 +++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65e820b..cddf4fc 100644 --- a/README.md +++ b/README.md @@ -131,9 +131,12 @@ You can optionally pass a __theme__ the captcha should use, as an html attribute size="---- compact or normal ----" type="'---- audio or image ----'" key="'---- YOUR PUBLIC KEY GOES HERE ----'" + lang="---- language code ----" >
``` +**Language Codes**: https://developers.google.com/recaptcha/docs/language + In this case we are specifying that the captcha should use the theme named _light_. Listeners @@ -205,9 +208,12 @@ myApp.config(function(vcRecaptchaServiceProvider){ vcRecaptchaServiceProvider.setStoken('--- YOUR GENERATED SECURE TOKEN ---') vcRecaptchaServiceProvider.setSize('---- compact or normal ----') vcRecaptchaServiceProvider.setType('---- audio or image ----') + vcRecaptchaServiceProvider.setLang('---- language code ----') }); ``` +**Language Codes**: https://developers.google.com/recaptcha/docs/language + You can also set all of the values at once. ```javascript @@ -217,7 +223,8 @@ myApp.config(function(vcRecaptchaServiceProvider){ theme: '---- light or dark ----', stoken: '--- YOUR GENERATED SECURE TOKEN ---', size: '---- compact or normal ----', - type: '---- audio or image ----' + type: '---- audio or image ----', + lang: '---- language code ----' }); ``` Note: any value omitted will be undefined, even if previously set. diff --git a/src/directive.js b/src/directive.js index 66b81ea..941bb1a 100644 --- a/src/directive.js +++ b/src/directive.js @@ -16,6 +16,7 @@ theme: '=?', size: '=?', type: '=?', + lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -48,6 +49,7 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, + lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired diff --git a/src/service.js b/src/service.js index 6f3aa0e..7ce9ad8 100644 --- a/src/service.js +++ b/src/service.js @@ -76,6 +76,15 @@ config.type = type; }; + /** + * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. + * + * @param lang The reCaptcha language. + */ + provider.setLang = function(lang){ + config.lang = lang; + }; + /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -115,7 +124,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha) { + if (!recaptcha || !window.___grecaptcha_cfg) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -142,6 +151,7 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; + conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); @@ -157,13 +167,24 @@ reload: function (widgetId) { validateRecaptchaInstance(); - // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, + /** + * Set reCaptcha language + */ + setLang: function (widgetId, lang) { + var cli = window.___grecaptcha_cfg.clients[widgetId]; + if (cli) { + var wk = cli.W.wk; + wk.lang = wk.hl = lang; + this.reload(widgetId); + } + }, + /** * Gets the response from the reCaptcha widget. * From 06e4c477ff6121414187dc6a28c6bc7eb9e14e7f Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 13:58:57 -0300 Subject: [PATCH 11/31] Set Language - Compile. --- package.json | 7 +++++++ release/angular-recaptcha.js | 33 +++++++++++++++++++++++++++----- release/angular-recaptcha.min.js | 8 ++++---- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 381bba6..d3f36d9 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,13 @@ "author": "VividCortex", "license": "MIT", "homepage": "https://github.com/vividcortex/angular-recaptcha", + "contributors": [ + { + "name" : "Eduardo Daniel Cuomo", + "email" : "reduardo7@gmail.com", + "url" : "https://github.com/reduardo7/angular-recaptcha" + } + ], "main": "release/angular-recaptcha.js", "repository": { "type": "git", diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index d90dd39..c562c9e 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-11-30 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ /*global angular, Recaptcha */ @@ -90,6 +90,15 @@ config.type = type; }; + /** + * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. + * + * @param lang The reCaptcha language. + */ + provider.setLang = function(lang){ + config.lang = lang; + }; + /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -129,7 +138,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha) { + if (!recaptcha || !window.___grecaptcha_cfg) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -156,6 +165,7 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; + conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); @@ -171,13 +181,24 @@ reload: function (widgetId) { validateRecaptchaInstance(); - // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, + /** + * Set reCaptcha language + */ + setLang: function (widgetId, lang) { + var cli = window.___grecaptcha_cfg.clients[widgetId]; + if (cli) { + var wk = cli.W.wk; + wk.lang = wk.hl = lang; + this.reload(widgetId); + } + }, + /** * Gets the response from the reCaptcha widget. * @@ -215,6 +236,7 @@ theme: '=?', size: '=?', type: '=?', + lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -247,6 +269,7 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, + lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index b7761aa..8fd7879 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-11-30 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j||!window.___grecaptcha_cfg)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},setLang:function(a,b){var c=window.___grecaptcha_cfg.clients[a];if(c){var d=c.W.wk;d.lang=d.hl=b,this.reload(a)}},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 5c85bbf4ede0e92a061516f351ce9c02dd303864 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:13:43 -0300 Subject: [PATCH 12/31] Language setter and getter --- README.md | 14 ++++---- release/angular-recaptcha.js | 59 ++++++++++++++++++++++++++------ release/angular-recaptcha.min.js | 2 +- src/directive.js | 2 ++ src/service.js | 57 ++++++++++++++++++++++++------ 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index cddf4fc..d0ce05b 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ There are three listeners you can use with the directive, `on-create`, `on-succe on-create="setWidgetId(widgetId)" on-success="setResponse(response)" on-expire="cbExpiration()" + lang="" > ``` @@ -219,12 +220,13 @@ You can also set all of the values at once. ```javascript myApp.config(function(vcRecaptchaServiceProvider){ vcRecaptchaServiceProvider.setDefaults({ - key: '---- YOUR PUBLIC KEY GOES HERE ----', - theme: '---- light or dark ----', - stoken: '--- YOUR GENERATED SECURE TOKEN ---', - size: '---- compact or normal ----', - type: '---- audio or image ----', - lang: '---- language code ----' + key: '---- YOUR PUBLIC KEY GOES HERE ----', + theme: '---- light or dark ----', + stoken: '--- YOUR GENERATED SECURE TOKEN ---', + size: '---- compact or normal ----', + type: '---- audio or image ----', + lang: '---- language code ----' + }); }); ``` Note: any value omitted will be undefined, even if previously set. diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index c562c9e..925fc40 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -110,7 +110,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -138,7 +138,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha || !window.___grecaptcha_cfg) { + if (!recaptcha) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -171,7 +171,9 @@ throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - return recaptcha.render(elm, conf); + var widgetId = recaptcha.render(elm, conf); + instances[widgetId] = elm; + return widgetId; }); }, @@ -188,14 +190,35 @@ }, /** - * Set reCaptcha language + * Get/Set reCaptcha language */ - setLang: function (widgetId, lang) { - var cli = window.___grecaptcha_cfg.clients[widgetId]; - if (cli) { - var wk = cli.W.wk; - wk.lang = wk.hl = lang; - this.reload(widgetId); + useLang: function (widgetId, lang) { + var instance = instances[widgetId]; + + if (instance) { + var iframe = instance.querySelector('iframe'); + if (lang) { + // Setter + if (iframe && iframe.src) { + var s = iframe.src; + if (/[?&]hl=/.test(s)) { + s = s.replace(/([?&]hl=)\w+/, '$1' + lang); + } else { + s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; + } + + iframe.src = s; + } + } else { + // Getter + if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { + return iframe.src.replace(/[?&]hl=(\w+)/, '$1'); + } else { + return null; + } + } + } else { + throw new Error('reCaptcha Widget ID not exists', widgetId); } }, @@ -210,6 +233,20 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); + }, + + /** + * Gets reCaptcha instance and configuration + */ + getInstance: function (widgetId) { + return instances[widgetId]; + }, + + /** + * Destroy reCaptcha instance. + */ + destroy: function (widgetId) { + delete instances[widgetId]; } }; @@ -322,6 +359,8 @@ } function cleanup(){ + vcRecaptcha.destroy(scope.widgetId); + // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 8fd7879..64dfc43 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -4,4 +4,4 @@ * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j||!window.___grecaptcha_cfg)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},setLang:function(a,b){var c=window.___grecaptcha_cfg.clients[a];if(c){var d=c.W.wk;d.lang=d.hl=b,this.reload(a)}},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/[?&]hl=(\w+)/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file diff --git a/src/directive.js b/src/directive.js index 941bb1a..1bc9d50 100644 --- a/src/directive.js +++ b/src/directive.js @@ -102,6 +102,8 @@ } function cleanup(){ + vcRecaptcha.destroy(scope.widgetId); + // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/src/service.js b/src/service.js index 7ce9ad8..0d6bb33 100644 --- a/src/service.js +++ b/src/service.js @@ -96,7 +96,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -124,7 +124,7 @@ } function validateRecaptchaInstance() { - if (!recaptcha || !window.___grecaptcha_cfg) { + if (!recaptcha) { throw new Error('reCaptcha has not been loaded yet.'); } } @@ -157,7 +157,9 @@ throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - return recaptcha.render(elm, conf); + var widgetId = recaptcha.render(elm, conf); + instances[widgetId] = elm; + return widgetId; }); }, @@ -174,14 +176,35 @@ }, /** - * Set reCaptcha language + * Get/Set reCaptcha language */ - setLang: function (widgetId, lang) { - var cli = window.___grecaptcha_cfg.clients[widgetId]; - if (cli) { - var wk = cli.W.wk; - wk.lang = wk.hl = lang; - this.reload(widgetId); + useLang: function (widgetId, lang) { + var instance = instances[widgetId]; + + if (instance) { + var iframe = instance.querySelector('iframe'); + if (lang) { + // Setter + if (iframe && iframe.src) { + var s = iframe.src; + if (/[?&]hl=/.test(s)) { + s = s.replace(/([?&]hl=)\w+/, '$1' + lang); + } else { + s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; + } + + iframe.src = s; + } + } else { + // Getter + if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { + return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); + } else { + return null; + } + } + } else { + throw new Error('reCaptcha Widget ID not exists', widgetId); } }, @@ -196,6 +219,20 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); + }, + + /** + * Gets reCaptcha instance and configuration + */ + getInstance: function (widgetId) { + return instances[widgetId]; + }, + + /** + * Destroy reCaptcha instance. + */ + destroy: function (widgetId) { + delete instances[widgetId]; } }; From e13857473afd110307a1c35dda322cd1a184bf51 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:14:50 -0300 Subject: [PATCH 13/31] Language setter and getter - Compile --- release/angular-recaptcha.js | 2 +- release/angular-recaptcha.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index 925fc40..fea5ad9 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -212,7 +212,7 @@ } else { // Getter if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/[?&]hl=(\w+)/, '$1'); + return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); } else { return null; } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 64dfc43..96fdc41 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -4,4 +4,4 @@ * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/[?&]hl=(\w+)/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+).+/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 7daae281e752604c5bd0a41ca4a8eb60ae49c80e Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Wed, 30 Nov 2016 17:23:51 -0300 Subject: [PATCH 14/31] Revert release. --- release/angular-recaptcha.js | 74 +++----------------------------- release/angular-recaptcha.min.js | 8 ++-- 2 files changed, 10 insertions(+), 72 deletions(-) diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index fea5ad9..d90dd39 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-11-30 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * angular-recaptcha build:2016-07-19 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ /*global angular, Recaptcha */ @@ -90,15 +90,6 @@ config.type = type; }; - /** - * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. - * - * @param lang The reCaptcha language. - */ - provider.setLang = function(lang){ - config.lang = lang; - }; - /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -110,7 +101,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -165,15 +156,12 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; - conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - var widgetId = recaptcha.render(elm, conf); - instances[widgetId] = elm; - return widgetId; + return recaptcha.render(elm, conf); }); }, @@ -183,45 +171,13 @@ reload: function (widgetId) { validateRecaptchaInstance(); + // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, - /** - * Get/Set reCaptcha language - */ - useLang: function (widgetId, lang) { - var instance = instances[widgetId]; - - if (instance) { - var iframe = instance.querySelector('iframe'); - if (lang) { - // Setter - if (iframe && iframe.src) { - var s = iframe.src; - if (/[?&]hl=/.test(s)) { - s = s.replace(/([?&]hl=)\w+/, '$1' + lang); - } else { - s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; - } - - iframe.src = s; - } - } else { - // Getter - if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); - } else { - return null; - } - } - } else { - throw new Error('reCaptcha Widget ID not exists', widgetId); - } - }, - /** * Gets the response from the reCaptcha widget. * @@ -233,20 +189,6 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); - }, - - /** - * Gets reCaptcha instance and configuration - */ - getInstance: function (widgetId) { - return instances[widgetId]; - }, - - /** - * Destroy reCaptcha instance. - */ - destroy: function (widgetId) { - delete instances[widgetId]; } }; @@ -273,7 +215,6 @@ theme: '=?', size: '=?', type: '=?', - lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -306,7 +247,6 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, - lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired @@ -359,8 +299,6 @@ } function cleanup(){ - vcRecaptcha.destroy(scope.widgetId); - // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 96fdc41..b7761aa 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-11-30 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * angular-recaptcha build:2016-07-19 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+).+/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From 126aafb56d9e817f0f67fae4a2a66357276f7b23 Mon Sep 17 00:00:00 2001 From: Eduardo Cuomo Date: Thu, 1 Dec 2016 13:45:47 -0300 Subject: [PATCH 15/31] Language tests --- src/service.js | 2 +- tests/directive_test.js | 27 +++++++++++++++++++++++++++ tests/service_test.js | 24 +++++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/service.js b/src/service.js index 0d6bb33..27956dd 100644 --- a/src/service.js +++ b/src/service.js @@ -198,7 +198,7 @@ } else { // Getter if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { - return iframe.src.replace(/.+[?&]hl=(\w+).+/, '$1'); + return iframe.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/, '$1'); } else { return null; } diff --git a/tests/directive_test.js b/tests/directive_test.js index 6191ad9..accc812 100644 --- a/tests/directive_test.js +++ b/tests/directive_test.js @@ -186,5 +186,32 @@ describe('directive: vcRecaptcha', function () { widgetId: undefined }); }); + + it('the widget should be using the setted language', function () { + var element = angular.element('
' + + '' + + '
' + + ''), + + _fakeCreate = function (element, config) { + config.callback(config.lang); + return { + then: function (cb) { + cb(); + } + }; + }; + + spyOn(vcRecaptchaService, 'create').and.callFake(_fakeCreate); + + $compile(element)($scope); + $scope.$digest(); + $timeout.flush(); + + expect($scope.onSuccess).toHaveBeenCalledWith({ + response: 'es', + widgetId: undefined + }); + }); }); }); diff --git a/tests/service_test.js b/tests/service_test.js index 62ca9d1..4f69153 100644 --- a/tests/service_test.js +++ b/tests/service_test.js @@ -28,7 +28,8 @@ describe('service', function () { theme: undefined, stoken: undefined, size: undefined, - type: undefined + type: undefined, + hl: undefined }; $window.vcRecaptchaApiLoaded(); @@ -63,4 +64,25 @@ describe('service', function () { expect($window.grecaptcha.getResponse).toHaveBeenCalledWith(_widgetId); }); }); + + describe('useLang', function () { + it('should call useLang', inject(function ($rootScope) { + var _element = angular.element('
')[0], + _key = '1234567890123456789012345678901234567890'; + + $window.vcRecaptchaApiLoaded(); + + vcRecaptchaService.create(_element, { + key: _key + }).then(function (widgetId) { + var instance = vcRecaptchaService.getInstance(widgetId); + expect(instance).toEqual(_element); + + vcRecaptchaService.useLang(widgetId, 'es'); + expect(vcRecaptchaService.useLang(widgetId)).toEqual('es'); + }) + + $rootScope.$digest(); + })); + }); }); From 1e6e6053358f3cd20977ad21390db409a445c06c Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Fri, 2 Dec 2016 09:26:19 -0300 Subject: [PATCH 16/31] [release] --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index ff08c10..9abbb4c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.0.4", + "version": "3.1.0", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index 381bba6..8a722d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.0.4", + "version": "3.1.0", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", From dae2ea1cacd7d4ccce58ae6dce06172527681c5b Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Fri, 2 Dec 2016 09:27:42 -0300 Subject: [PATCH 17/31] [release] --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 9abbb4c..d708e60 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.1.0", + "version": "3.2.0", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index 768a32e..eef8d8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.1.0", + "version": "3.2.0", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", From c59c81e48f57a652c24758983c680a48b975af55 Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Wed, 7 Dec 2016 14:46:01 -0300 Subject: [PATCH 18/31] [release] --- bower.json | 2 +- package.json | 2 +- release/angular-recaptcha.js | 74 +++++++++++++++++++++++++++++--- release/angular-recaptcha.min.js | 8 ++-- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/bower.json b/bower.json index d708e60..5615a2e 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.2.0", + "version": "3.2.1", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index eef8d8c..be13462 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.2.0", + "version": "3.2.1", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index d90dd39..6af1bda 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-12-07 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ /*global angular, Recaptcha */ @@ -90,6 +90,15 @@ config.type = type; }; + /** + * Sets the reCaptcha language which will be used by default is not specified in a specific directive instance. + * + * @param lang The reCaptcha language. + */ + provider.setLang = function(lang){ + config.lang = lang; + }; + /** * Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance. * @@ -101,7 +110,7 @@ }; provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { - var deferred = $q.defer(), promise = deferred.promise, recaptcha; + var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -156,12 +165,15 @@ conf.stoken = conf.stoken || config.stoken; conf.size = conf.size || config.size; conf.type = conf.type || config.type; + conf.hl = conf.lang || config.lang; if (!conf.sitekey || conf.sitekey.length !== 40) { throwNoKeyException(); } return getRecaptcha().then(function (recaptcha) { - return recaptcha.render(elm, conf); + var widgetId = recaptcha.render(elm, conf); + instances[widgetId] = elm; + return widgetId; }); }, @@ -171,13 +183,45 @@ reload: function (widgetId) { validateRecaptchaInstance(); - // $log.info('Reloading captcha'); recaptcha.reset(widgetId); // Let everyone know this widget has been reset. $rootScope.$broadcast('reCaptchaReset', widgetId); }, + /** + * Get/Set reCaptcha language + */ + useLang: function (widgetId, lang) { + var instance = instances[widgetId]; + + if (instance) { + var iframe = instance.querySelector('iframe'); + if (lang) { + // Setter + if (iframe && iframe.src) { + var s = iframe.src; + if (/[?&]hl=/.test(s)) { + s = s.replace(/([?&]hl=)\w+/, '$1' + lang); + } else { + s += ((s.indexOf('?') === -1) ? '?' : '&') + 'hl=' + lang; + } + + iframe.src = s; + } + } else { + // Getter + if (iframe && iframe.src && /[?&]hl=\w+/.test(iframe.src)) { + return iframe.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/, '$1'); + } else { + return null; + } + } + } else { + throw new Error('reCaptcha Widget ID not exists', widgetId); + } + }, + /** * Gets the response from the reCaptcha widget. * @@ -189,6 +233,20 @@ validateRecaptchaInstance(); return recaptcha.getResponse(widgetId); + }, + + /** + * Gets reCaptcha instance and configuration + */ + getInstance: function (widgetId) { + return instances[widgetId]; + }, + + /** + * Destroy reCaptcha instance. + */ + destroy: function (widgetId) { + delete instances[widgetId]; } }; @@ -215,6 +273,7 @@ theme: '=?', size: '=?', type: '=?', + lang: '=?', tabindex: '=?', required: '=?', onCreate: '&', @@ -247,6 +306,7 @@ stoken: scope.stoken || attrs.stoken || null, theme: scope.theme || attrs.theme || null, type: scope.type || attrs.type || null, + lang: scope.lang || attrs.lang || null, tabindex: scope.tabindex || attrs.tabindex || null, size: scope.size || attrs.size || null, 'expired-callback': expired @@ -299,6 +359,8 @@ } function cleanup(){ + vcRecaptcha.destroy(scope.widgetId); + // removes elements reCaptcha added. ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index b7761aa..b7792ca 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * angular-recaptcha build:2016-07-19 - * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * @license angular-recaptcha build:2016-12-07 + * https://github.com/vividcortex/angular-recaptcha + * Copyright (c) 2016 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise;f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var m=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(m),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&m(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){return b.render(a,c)})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},getResponse:function(a){return i(),j.getResponse(a)}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From b296f81b91c87383fd7cb7cc94ae1f1f6e3121b5 Mon Sep 17 00:00:00 2001 From: Felix Mosheev Date: Sat, 7 Jan 2017 16:24:08 +0200 Subject: [PATCH 19/31] Improvement: Generate recaptcha script on demand. --- src/service.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/service.js b/src/service.js index 27956dd..4933593 100644 --- a/src/service.js +++ b/src/service.js @@ -95,7 +95,7 @@ provider.onLoadFunctionName = onLoadFunctionName; }; - provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { + provider.$get = ['$rootScope','$window', '$q', '$document', function ($rootScope, $window, $q, $document) { var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -133,6 +133,13 @@ // Check if grecaptcha is not defined already. if (ng.isDefined($window.grecaptcha)) { callback(); + } else { + // Generate link on demand + var script = $document.get(0).createElement('script'); + script.async = true; + script.defer = true; + script.src = 'https://www.google.com/recaptcha/api.js?onload='+provider.onLoadFunctionName+'&render=explicit'; + $document.get(0).body.appendChild(script); } return { From 6ecec83ef3479494ce219d0786e0dec2d78dc8e0 Mon Sep 17 00:00:00 2001 From: Felix Mosheev Date: Tue, 10 Jan 2017 21:44:24 +0200 Subject: [PATCH 20/31] Refactor service tests and add tests for script tag generation --- karma.conf.js | 1 + tests/service.driver.js | 46 ++++++++++++++++ tests/service_test.js | 117 +++++++++++++++++++++++++--------------- 3 files changed, 122 insertions(+), 42 deletions(-) create mode 100644 tests/service.driver.js diff --git a/karma.conf.js b/karma.conf.js index 5b8e794..19c0606 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -23,6 +23,7 @@ module.exports = function (config) { 'src/module.js', 'src/*.js', + 'tests/*.driver.js', 'tests/*_test.js' ], diff --git a/tests/service.driver.js b/tests/service.driver.js new file mode 100644 index 0000000..34e63b9 --- /dev/null +++ b/tests/service.driver.js @@ -0,0 +1,46 @@ +function ServiceDriver() { + var _this = this; + var mockModules = { + $window: {}, + $document: {} + }; + + module(mockModules); // mock all the properties + + this.given = { + apiLoaded: function (mockRecaptcha) { + mockModules.$window.grecaptcha = mockRecaptcha; + + return _this; + }, + onLoadFunctionName: function (funcName) { + module(function (vcRecaptchaServiceProvider) { + vcRecaptchaServiceProvider.setOnLoadFunctionName(funcName); + }); + return _this; + }, + mockDocument: function (mockDocument) { + mockModules.$document.get = mockDocument.get; + + return _this; + } + }; + + this.when = { + created: function () { + inject(function (vcRecaptchaService) { + _this.service = vcRecaptchaService; + }) + }, + notifyThatApiLoaded: function () { + mockModules.$window.vcRecaptchaApiLoaded(); + return _this; + } + }; +} + +ServiceDriver.prototype.applyChanges = function () { + inject(function ($rootScope) { + $rootScope.$digest(); + }); +}; \ No newline at end of file diff --git a/tests/service_test.js b/tests/service_test.js index 4f69153..b44a660 100644 --- a/tests/service_test.js +++ b/tests/service_test.js @@ -1,26 +1,27 @@ describe('service', function () { 'use strict'; - var vcRecaptchaService, $window; + var driver; - beforeEach(module('vcRecaptcha', function ($provide) { - $provide.constant('$window', { - grecaptcha: jasmine.createSpyObj('grecaptcha', ['render', 'getResponse', 'reset']) - }); - })); + beforeEach(module('vcRecaptcha')); - beforeEach(inject(function (_vcRecaptchaService_, _$window_) { - vcRecaptchaService = _vcRecaptchaService_; - $window = _$window_; + beforeEach(function () { + driver = new ServiceDriver(); + }); - $window.vcRecaptchaApiLoaded = jasmine.createSpy('vcRecaptchaApiLoaded'); - })); + describe('with loaded api', function () { + var grecaptchaMock; - describe('create', function () { - it('should call recaptcha.render', inject(function ($rootScope) { - var _element = '
', - _key = '1234567890123456789012345678901234567890', - _fn = angular.noop, + beforeEach(function () { + driver + .given.apiLoaded(grecaptchaMock = jasmine.createSpyObj('grecaptcha', ['render', 'getResponse', 'reset'])) + .when.created(); + }); + + it('should call recaptcha.render', function () { + var _element = '
', + _key = '1234567890123456789012345678901234567890', + _fn = angular.noop, _confRender = { sitekey: _key, key: _key, @@ -32,57 +33,89 @@ describe('service', function () { hl: undefined }; - $window.vcRecaptchaApiLoaded(); + driver.when.notifyThatApiLoaded(); - vcRecaptchaService.create(_element, { + driver.service.create(_element, { key: _confRender.key, callback: _fn }); - $rootScope.$digest(); + driver.applyChanges(); - expect($window.grecaptcha.render).toHaveBeenCalledWith(_element, _confRender); - })); - }); + expect(grecaptchaMock.render).toHaveBeenCalledWith(_element, _confRender); + }); - describe('reload', function () { it('should call reset', function () { var _widgetId = 123; - vcRecaptchaService.reload(_widgetId); + driver.service.reload(_widgetId); - expect($window.grecaptcha.reset).toHaveBeenCalledWith(_widgetId); + expect(grecaptchaMock.reset).toHaveBeenCalledWith(_widgetId); }); - }); - describe('getResponse', function () { it('should call getResponse', function () { var _widgetId = 123; - vcRecaptchaService.getResponse(_widgetId); + driver.service.getResponse(_widgetId); - expect($window.grecaptcha.getResponse).toHaveBeenCalledWith(_widgetId); + expect(grecaptchaMock.getResponse).toHaveBeenCalledWith(_widgetId); }); - }); - describe('useLang', function () { - it('should call useLang', inject(function ($rootScope) { - var _element = angular.element('
')[0], - _key = '1234567890123456789012345678901234567890'; + it('should call useLang', function () { + var _element = angular.element('
')[0], + _key = '1234567890123456789012345678901234567890'; - $window.vcRecaptchaApiLoaded(); + driver.when.notifyThatApiLoaded(); - vcRecaptchaService.create(_element, { + driver.service.create(_element, { key: _key }).then(function (widgetId) { - var instance = vcRecaptchaService.getInstance(widgetId); + var instance = driver.service.getInstance(widgetId); expect(instance).toEqual(_element); - vcRecaptchaService.useLang(widgetId, 'es'); - expect(vcRecaptchaService.useLang(widgetId)).toEqual('es'); - }) + driver.service.useLang(widgetId, 'es'); + expect(driver.service.useLang(widgetId)).toEqual('es'); + }); + + driver.applyChanges(); + }); + }); - $rootScope.$digest(); - })); + describe('without loaded api', function () { + var scriptTagSpy, + appendChildSpy, + funcName; + + beforeEach(function () { + scriptTagSpy = jasmine.createSpy('scriptTagSpy'); + appendChildSpy = jasmine.createSpy('appendChildSpy'); + + driver + .given.onLoadFunctionName(funcName = 'my-func') + .given.mockDocument({ + get: function () { + return { + createElement: function () { + return scriptTagSpy; + }, + body: { + appendChild: appendChildSpy + } + }; + } + }) + .when.created(); + + }); + + it('should add script tag to body', function () { + expect(scriptTagSpy.async).toBe(true); + expect(scriptTagSpy.defer).toBe(true); + expect(appendChildSpy).toHaveBeenCalledWith(scriptTagSpy); + }); + + it('should add callback function name to src', function () { + expect(scriptTagSpy.src).toBe('https://www.google.com/recaptcha/api.js?onload=' + funcName + '&render=explicit'); + }); }); }); From ba287ea0cd61e4f7a6427038ecf7910e3856bedd Mon Sep 17 00:00:00 2001 From: Felix Mosheev Date: Fri, 13 Jan 2017 11:52:50 +0200 Subject: [PATCH 21/31] Update docs regarding script include --- README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/README.md b/README.md index d0ce05b..243d457 100644 --- a/README.md +++ b/README.md @@ -41,20 +41,7 @@ See [the demo file](demo/usage.html) for a quick usage example. - First, you need to get a valid recaptcha key for your domain. Go to http://www.google.com/recaptcha. -- Include the reCaptcha [API](https://developers.google.com/recaptcha/docs/display#AJAX) using this script in your HTML: - -```html - -``` - -As you can see, we are specifying a `onload` callback, which will notify the angular service once the api is ready for usage. - -The `onload` callback name defaults to `vcRecaptchaApiLoaded`, but can be overridden by the service provider via `vcRecaptchaServiceProvider.setOnLoadFunctionName('myOtherFunctionName');`. - -- Also include the vc-recaptcha script and make your angular app depend on the `vcRecaptcha` module. +- Include the vc-recaptcha script and make your angular app depend on the `vcRecaptcha` module. ```html From 7bdc4ac98da2aae91ec396439e0f28349ce543e3 Mon Sep 17 00:00:00 2001 From: Felix Mosheev Date: Fri, 13 Jan 2017 12:50:37 +0200 Subject: [PATCH 22/31] Added missing tests --- tests/provider.driver.js | 35 +++++++++++++++ tests/provider_test.js | 94 ++++++++++++++++++++++++++++++++++++++++ tests/service_test.js | 4 ++ 3 files changed, 133 insertions(+) create mode 100644 tests/provider.driver.js create mode 100644 tests/provider_test.js diff --git a/tests/provider.driver.js b/tests/provider.driver.js new file mode 100644 index 0000000..299f256 --- /dev/null +++ b/tests/provider.driver.js @@ -0,0 +1,35 @@ +function ProviderDriver() { + var _this = this; + var mockModules = { + $window: {} + }; + + module(mockModules); // mock all the properties + + this.given = { + recaptchaLoaded: function (recaptchaMock) { + mockModules.$window.grecaptcha = recaptchaMock; + return _this; + } + }; + + this.when = { + created: function () { + module(function (vcRecaptchaServiceProvider) { + _this.provider = vcRecaptchaServiceProvider; + }); + + inject(); // needed for angular-mocks to kick off + + return _this; + }, + callingCreate: function () { + inject(function (vcRecaptchaService, $rootScope) { + vcRecaptchaService.create(null, {}); + $rootScope.$digest(); + }); + + return this; + } + }; +} \ No newline at end of file diff --git a/tests/provider_test.js b/tests/provider_test.js new file mode 100644 index 0000000..89bd06c --- /dev/null +++ b/tests/provider_test.js @@ -0,0 +1,94 @@ +describe('provider', function () { + 'use strict'; + + var driver, + recaptchaMock, + key; + + beforeEach(module('vcRecaptcha')); + + beforeEach(function () { + driver = new ProviderDriver(); + driver.given.recaptchaLoaded(recaptchaMock = jasmine.createSpyObj('recaptchaMock', ['render'])) + .when.created(); + + driver.provider.setSiteKey(key = '1234567890123456789012345678901234567890'); + }); + + it('should setDefaults', function () { + var modifiedKey = key.substring(0, 39) + 'x'; + + driver.provider.setDefaults({key: modifiedKey}); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({sitekey: modifiedKey})); + }); + + it('should setSiteKey', function () { + driver.provider.setSiteKey(key); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({sitekey: key})); + }); + + it('should setTheme', function () { + var theme = 'theme'; + driver.provider.setTheme(theme); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({theme: theme})); + }); + + it('should setStoken', function () { + var stoken = 'stoken'; + driver.provider.setStoken(stoken); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({stoken: stoken})); + }); + + it('should setSize', function () { + var size = 'size'; + driver.provider.setSize(size); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({size: size})); + }); + + it('should setType', function () { + var type = 'type'; + driver.provider.setType(type); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({type: type})); + }); + + it('should setLang', function () { + var lang = 'en'; + driver.provider.setLang(lang); + + driver.when.callingCreate(); + + var callArgs = recaptchaMock.render.calls.mostRecent().args[1]; + + expect(callArgs).toEqual(jasmine.objectContaining({hl: lang})); + }); +}); diff --git a/tests/service_test.js b/tests/service_test.js index b44a660..5caabfc 100644 --- a/tests/service_test.js +++ b/tests/service_test.js @@ -117,5 +117,9 @@ describe('service', function () { it('should add callback function name to src', function () { expect(scriptTagSpy.src).toBe('https://www.google.com/recaptcha/api.js?onload=' + funcName + '&render=explicit'); }); + + it('should validate that recaptcha is loaded', function () { + expect(driver.service.reload).toThrowError('reCaptcha has not been loaded yet.'); + }); }); }); From 1e18cdff27d202d97892a885123c903543eea3dc Mon Sep 17 00:00:00 2001 From: Miguel Trias Date: Wed, 18 Jan 2017 16:28:32 -0300 Subject: [PATCH 23/31] updated changelog with the last release information --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 243d457..858bc4b 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,7 @@ Differences with the old reCaptcha Recent Changelog ---------------- +- 3.0.0 - Removed the need to include the Google recaptcha api. - 2.2.3 - Removed _cleanup_ after creating the captcha element. - 2.0.1 - Fixed onload when using ng-route and recaptcha is placed in a secondary view. - 2.0.0 - Rewritten service to support new reCaptcha From 87fa4514b801ca46f6b99802716438bf86a8b0bf Mon Sep 17 00:00:00 2001 From: Miguel Trias Date: Wed, 18 Jan 2017 16:28:48 -0300 Subject: [PATCH 24/31] [release] --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 5615a2e..e639399 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.2.1", + "version": "4.0.0", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index be13462..3eb87bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "3.2.1", + "version": "4.0.0", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", From 26aec3e7a7e0ce9e84c105c4a1f7bceb88e722da Mon Sep 17 00:00:00 2001 From: Miguel Trias Date: Wed, 18 Jan 2017 16:42:47 -0300 Subject: [PATCH 25/31] [release] --- bower.json | 2 +- package.json | 2 +- release/angular-recaptcha.js | 13 ++++++++++--- release/angular-recaptcha.min.js | 6 +++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/bower.json b/bower.json index e639399..c719418 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.0", + "version": "4.0.1", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index 3eb87bc..3d76a4d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.0", + "version": "4.0.1", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index 6af1bda..afea8d2 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-12-07 + * @license angular-recaptcha build:2017-01-18 * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * Copyright (c) 2017 VividCortex **/ /*global angular, Recaptcha */ @@ -109,7 +109,7 @@ provider.onLoadFunctionName = onLoadFunctionName; }; - provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) { + provider.$get = ['$rootScope','$window', '$q', '$document', function ($rootScope, $window, $q, $document) { var deferred = $q.defer(), promise = deferred.promise, instances = {}, recaptcha; $window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || []; @@ -147,6 +147,13 @@ // Check if grecaptcha is not defined already. if (ng.isDefined($window.grecaptcha)) { callback(); + } else { + // Generate link on demand + var script = $document.get(0).createElement('script'); + script.async = true; + script.defer = true; + script.src = 'https://www.google.com/recaptcha/api.js?onload='+provider.onLoadFunctionName+'&render=explicit'; + $document.get(0).body.appendChild(script); } return { diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index b7792ca..6ff48be 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2016-12-07 + * @license angular-recaptcha build:2017-01-18 * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2016 VividCortex + * Copyright (c) 2017 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q",function(e,f,g){function h(){return j?g.when(j):l}function i(){if(!j)throw new Error("reCaptcha has not been loaded yet.")}var j,k=g.defer(),l=k.promise,m={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var n=function(){j=f.grecaptcha,k.resolve(j)};return f.vcRecaptchaApiLoadedCallback.push(n),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha)&&n(),{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),h().then(function(b){var d=b.render(a,c);return m[d]=a,d})},reload:function(a){i(),j.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=m[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return i(),j.getResponse(a)},getInstance:function(a){return m[a]},destroy:function(a){delete m[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q","$document",function(e,f,g,h){function i(){return k?g.when(k):m}function j(){if(!k)throw new Error("reCaptcha has not been loaded yet.")}var k,l=g.defer(),m=l.promise,n={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var o=function(){k=f.grecaptcha,l.resolve(k)};if(f.vcRecaptchaApiLoadedCallback.push(o),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha))o();else{var p=h.get(0).createElement("script");p.async=!0,p.defer=!0,p.src="https://www.google.com/recaptcha/api.js?onload="+c.onLoadFunctionName+"&render=explicit",h.get(0).body.appendChild(p)}return{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),i().then(function(b){var d=b.render(a,c);return n[d]=a,d})},reload:function(a){j(),k.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=n[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return j(),k.getResponse(a)},getInstance:function(a){return n[a]},destroy:function(a){delete n[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From cef5ede28fcaea2cfc97dd798b8b5727ad42ac9a Mon Sep 17 00:00:00 2001 From: Valeriy Donika Date: Thu, 19 Jan 2017 13:07:47 +0200 Subject: [PATCH 26/31] Remove include script in of google api Because of Changelog "3.0.0 - Removed the need to include the Google recaptcha api." --- demo/usage.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/demo/usage.html b/demo/usage.html index 0180e32..fe7abce 100644 --- a/demo/usage.html +++ b/demo/usage.html @@ -8,10 +8,6 @@ - - - - From 98dabbd149d492201b3ad22d3cb2d7296703f4cc Mon Sep 17 00:00:00 2001 From: Miguel Trias Date: Fri, 20 Jan 2017 22:24:20 -0300 Subject: [PATCH 27/31] [minor] - badge added --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 858bc4b..c411114 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ AngularJS reCaptcha [![Build Status](https://travis-ci.org/VividCortex/angular-recaptcha.svg?branch=master)](https://travis-ci.org/VividCortex/angular-recaptcha) [![Coverage Status](https://coveralls.io/repos/VividCortex/angular-recaptcha/badge.svg?branch=master)](https://coveralls.io/r/VividCortex/angular-recaptcha?branch=master) +![image](https://img.shields.io/npm/dm/angular-recaptcha.svg) Add a [reCaptcha](https://www.google.com/recaptcha/intro/index.html) to your [AngularJS](angularjs.org) project. From 4276c0cc025f17e16d533e2ddac6885c497dbb1a Mon Sep 17 00:00:00 2001 From: MrWoGu Date: Mon, 30 Jan 2017 22:39:08 +0100 Subject: [PATCH 28/31] Removed jQuery dependency --- bower.json | 3 +-- karma.conf.js | 1 - src/service.js | 4 ++-- tests/service.driver.js | 7 ++++++- tests/service_test.js | 28 +++++++++++++++------------- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/bower.json b/bower.json index c719418..72d5a42 100644 --- a/bower.json +++ b/bower.json @@ -14,7 +14,6 @@ "angular": "1.*" }, "devDependencies": { - "angular-mocks": "~1.*", - "jquery": "~2.1.3" + "angular-mocks": "~1.*" } } diff --git a/karma.conf.js b/karma.conf.js index 19c0606..5f5eecb 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -15,7 +15,6 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ - 'bower_components/jquery/dist/jquery.min.js', 'bower_components/angular/angular.js', 'bower_components/angular-mocks/angular-mocks.js', diff --git a/src/service.js b/src/service.js index 4933593..787a6a2 100644 --- a/src/service.js +++ b/src/service.js @@ -135,11 +135,11 @@ callback(); } else { // Generate link on demand - var script = $document.get(0).createElement('script'); + var script = $window.document.createElement('script'); script.async = true; script.defer = true; script.src = 'https://www.google.com/recaptcha/api.js?onload='+provider.onLoadFunctionName+'&render=explicit'; - $document.get(0).body.appendChild(script); + $document.find('body').append(script); } return { diff --git a/tests/service.driver.js b/tests/service.driver.js index 34e63b9..84a269c 100644 --- a/tests/service.driver.js +++ b/tests/service.driver.js @@ -20,7 +20,12 @@ function ServiceDriver() { return _this; }, mockDocument: function (mockDocument) { - mockModules.$document.get = mockDocument.get; + mockModules.$document.find = mockDocument.find; + + return _this; + }, + mockWindow: function (mockWindow) { + mockModules.$window.document = mockWindow.document; return _this; } diff --git a/tests/service_test.js b/tests/service_test.js index 5caabfc..e5a36fb 100644 --- a/tests/service_test.js +++ b/tests/service_test.js @@ -83,27 +83,29 @@ describe('service', function () { describe('without loaded api', function () { var scriptTagSpy, - appendChildSpy, + appendSpy, funcName; beforeEach(function () { scriptTagSpy = jasmine.createSpy('scriptTagSpy'); - appendChildSpy = jasmine.createSpy('appendChildSpy'); + appendSpy = jasmine.createSpy('appendSpy'); driver .given.onLoadFunctionName(funcName = 'my-func') .given.mockDocument({ - get: function () { - return { - createElement: function () { - return scriptTagSpy; - }, - body: { - appendChild: appendChildSpy - } - }; + find: function () { + return { + append: appendSpy + }; + } + }) + .given.mockWindow({ + document: { + createElement: function () { + return scriptTagSpy; } - }) + } + }) .when.created(); }); @@ -111,7 +113,7 @@ describe('service', function () { it('should add script tag to body', function () { expect(scriptTagSpy.async).toBe(true); expect(scriptTagSpy.defer).toBe(true); - expect(appendChildSpy).toHaveBeenCalledWith(scriptTagSpy); + expect(appendSpy).toHaveBeenCalledWith(scriptTagSpy); }); it('should add callback function name to src', function () { From 60b986fade487f9fda8a003770d3d9b5f942c2ec Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Thu, 2 Feb 2017 23:16:00 -0300 Subject: [PATCH 29/31] [release] --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 72d5a42..3b28cab 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.1", + "version": "4.0.2", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index 3d76a4d..6c44518 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.1", + "version": "4.0.2", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT", From 47b4d5a60269ff7a4cf85bcb45088a8bd911c339 Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Thu, 2 Feb 2017 23:38:41 -0300 Subject: [PATCH 30/31] Fix release files not being built with latest changes --- release/angular-recaptcha.js | 6 +++--- release/angular-recaptcha.min.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index afea8d2..4255e05 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,5 +1,5 @@ /** - * @license angular-recaptcha build:2017-01-18 + * @license angular-recaptcha build:2017-02-02 * https://github.com/vividcortex/angular-recaptcha * Copyright (c) 2017 VividCortex **/ @@ -149,11 +149,11 @@ callback(); } else { // Generate link on demand - var script = $document.get(0).createElement('script'); + var script = $window.document.createElement('script'); script.async = true; script.defer = true; script.src = 'https://www.google.com/recaptcha/api.js?onload='+provider.onLoadFunctionName+'&render=explicit'; - $document.get(0).body.appendChild(script); + $document.find('body').append(script); } return { diff --git a/release/angular-recaptcha.min.js b/release/angular-recaptcha.min.js index 6ff48be..0e43083 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2017-01-18 + * @license angular-recaptcha build:2017-02-02 * https://github.com/vividcortex/angular-recaptcha * Copyright (c) 2017 VividCortex **/ -!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q","$document",function(e,f,g,h){function i(){return k?g.when(k):m}function j(){if(!k)throw new Error("reCaptcha has not been loaded yet.")}var k,l=g.defer(),m=l.promise,n={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var o=function(){k=f.grecaptcha,l.resolve(k)};if(f.vcRecaptchaApiLoadedCallback.push(o),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha))o();else{var p=h.get(0).createElement("script");p.async=!0,p.defer=!0,p.src="https://www.google.com/recaptcha/api.js?onload="+c.onLoadFunctionName+"&render=explicit",h.get(0).body.appendChild(p)}return{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),i().then(function(b){var d=b.render(a,c);return n[d]=a,d})},reload:function(a){j(),k.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=n[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return j(),k.getResponse(a)},getInstance:function(a){return n[a]},destroy:function(a){delete n[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file +!function(a){"use strict";a.module("vcRecaptcha",[])}(angular),function(a){"use strict";function b(){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')}var c=a.module("vcRecaptcha");c.provider("vcRecaptchaService",function(){var c=this,d={};c.onLoadFunctionName="vcRecaptchaApiLoaded",c.setDefaults=function(b){a.copy(b,d)},c.setSiteKey=function(a){d.key=a},c.setTheme=function(a){d.theme=a},c.setStoken=function(a){d.stoken=a},c.setSize=function(a){d.size=a},c.setType=function(a){d.type=a},c.setLang=function(a){d.lang=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q","$document",function(e,f,g,h){function i(){return k?g.when(k):m}function j(){if(!k)throw new Error("reCaptcha has not been loaded yet.")}var k,l=g.defer(),m=l.promise,n={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var o=function(){k=f.grecaptcha,l.resolve(k)};if(f.vcRecaptchaApiLoadedCallback.push(o),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha))o();else{var p=f.document.createElement("script");p.async=!0,p.defer=!0,p.src="https://www.google.com/recaptcha/api.js?onload="+c.onLoadFunctionName+"&render=explicit",h.find("body").append(p)}return{create:function(a,c){return c.sitekey=c.key||d.key,c.theme=c.theme||d.theme,c.stoken=c.stoken||d.stoken,c.size=c.size||d.size,c.type=c.type||d.type,c.hl=c.lang||d.lang,c.sitekey&&40===c.sitekey.length||b(),i().then(function(b){var d=b.render(a,c);return n[d]=a,d})},reload:function(a){j(),k.reset(a),e.$broadcast("reCaptchaReset",a)},useLang:function(a,b){var c=n[a];if(!c)throw new Error("reCaptcha Widget ID not exists",a);var d=c.querySelector("iframe");if(!b)return d&&d.src&&/[?&]hl=\w+/.test(d.src)?d.src.replace(/.+[?&]hl=(\w+)([^\w].+)?/,"$1"):null;if(d&&d.src){var e=d.src;/[?&]hl=/.test(e)?e=e.replace(/([?&]hl=)\w+/,"$1"+b):e+=(e.indexOf("?")===-1?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return j(),k.getResponse(a)},getInstance:function(a){return n[a]},destroy:function(a){delete n[a]}}}]})}(angular),function(a){"use strict";var b=a.module("vcRecaptcha");b.directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(e,f,g,h){function i(){h&&h.$setValidity("recaptcha",null),l()}function j(){c(function(){e.response="",k(),e.onExpire({widgetId:e.widgetId})})}function k(){h&&h.$setValidity("recaptcha",e.required===!1?null:Boolean(e.response))}function l(){d.destroy(e.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}e.widgetId=null,h&&a.isDefined(g.required)&&e.$watch("required",k);var m=e.$watch("key",function(b){var h=function(a){c(function(){e.response=a,k(),e.onSuccess({response:a,widgetId:e.widgetId})})};d.create(f[0],{callback:h,key:b,stoken:e.stoken||g.stoken||null,theme:e.theme||g.theme||null,type:e.type||g.type||null,lang:e.lang||g.lang||null,tabindex:e.tabindex||g.tabindex||null,size:e.size||g.size||null,"expired-callback":j}).then(function(b){k(),e.widgetId=b,e.onCreate({widgetId:b}),e.$on("$destroy",i),e.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(e.response="",k())})}),m()})}}}])}(angular); \ No newline at end of file From a853512427c2931aba0474e82dff5fdcab327072 Mon Sep 17 00:00:00 2001 From: Ismael Ambrosi Date: Thu, 2 Feb 2017 23:38:48 -0300 Subject: [PATCH 31/31] [release] --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 3b28cab..ff091b7 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.2", + "version": "4.0.3", "keywords": ["angular", "captcha", "recaptcha", "vividcortex", "human", "form", "validation", "signup", "security", "login"], "main": "release/angular-recaptcha.js", "ignore": [ diff --git a/package.json b/package.json index 6c44518..80a6bcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-recaptcha", - "version": "4.0.2", + "version": "4.0.3", "description": "An AngularJS module to ease usage of reCaptcha inside a form", "author": "VividCortex", "license": "MIT",