diff --git a/release/angular-recaptcha.js b/release/angular-recaptcha.js index 907dab5..38f6163 100644 --- a/release/angular-recaptcha.js +++ b/release/angular-recaptcha.js @@ -1,7 +1,7 @@ /** - * @license angular-recaptcha build:2017-11-22 + * @license angular-recaptcha build:2018-01-03 * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2017 VividCortex + * Copyright (c) 2018 VividCortex **/ /*global angular, Recaptcha */ @@ -295,11 +295,11 @@ var app = ng.module('vcRecaptcha'); - app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', function ($document, $timeout, vcRecaptcha) { + app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', '$q', function ($document, $timeout, vcRecaptcha, $q) { return { restrict: 'A', - require: "?^^form", + require: '?^^form', scope: { response: '=?ngModel', key: '=?', @@ -318,11 +318,16 @@ link: function (scope, elm, attrs, ctrl) { scope.widgetId = null; - if(ctrl && ng.isDefined(attrs.required)){ + if (ctrl && ng.isDefined(attrs.required)) { scope.$watch('required', validate); } var removeCreationListener = scope.$watch('key', function (key) { + // Abort if undefined or null + if (ng.isUndefined(key) || key === null) { + return; + } + var callback = function (gRecaptchaResponse) { // Safe $apply $timeout(function () { @@ -334,19 +339,20 @@ }); }; - vcRecaptcha.create(elm[0], { - - callback: callback, - key: key, - 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, - badge: scope.badge || attrs.badge || null, - 'expired-callback': expired - + // Accept a promise, or resolve immediately if the value is a bare string + $q.resolve(key).then(function (resolved) { + return vcRecaptcha.create(elm[0], { + callback: callback, + key: resolved, + 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, + badge: scope.badge || attrs.badge || null, + 'expired-callback': expired + }); }).then(function (widgetId) { // The widget has been created validate(); @@ -355,13 +361,12 @@ scope.$on('$destroy', destroy); - scope.$on('reCaptchaReset', function(event, resetWidgetId){ - if(ng.isUndefined(resetWidgetId) || widgetId === resetWidgetId){ - scope.response = ""; - validate(); - } - }) - + scope.$on('reCaptchaReset', function (event, resetWidgetId) { + if (ng.isUndefined(resetWidgetId) || widgetId === resetWidgetId) { + scope.response = ''; + validate(); + } + }); }); // Remove this listener to avoid creating the widget more than once. @@ -369,36 +374,36 @@ }); function destroy() { - if (ctrl) { - // reset the validity of the form if we were removed - ctrl.$setValidity('recaptcha', null); - } + if (ctrl) { + // reset the validity of the form if we were removed + ctrl.$setValidity('recaptcha', null); + } - cleanup(); + cleanup(); } - function expired(){ + function expired() { // Safe $apply $timeout(function () { - scope.response = ""; + scope.response = ''; validate(); // Notify about the response availability - scope.onExpire({ widgetId: scope.widgetId }); + scope.onExpire({widgetId: scope.widgetId}); }); } - function validate(){ - if(ctrl){ + function validate() { + if (ctrl) { ctrl.$setValidity('recaptcha', scope.required === false ? null : Boolean(scope.response)); } } - function cleanup(){ - vcRecaptcha.destroy(scope.widgetId); + function cleanup() { + vcRecaptcha.destroy(scope.widgetId); - // removes elements reCaptcha added. - ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); + // 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 759666c..d0450fd 100644 --- a/release/angular-recaptcha.min.js +++ b/release/angular-recaptcha.min.js @@ -1,8 +1,8 @@ /** - * @license angular-recaptcha build:2017-11-22 + * @license angular-recaptcha build:2018-01-03 * https://github.com/vividcortex/angular-recaptcha - * Copyright (c) 2017 VividCortex + * Copyright (c) 2018 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')}a.module("vcRecaptcha").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.setBadge=function(a){d.badge=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q","$document","$interval",function(e,f,g,h,i){function j(){return l?g.when(l):n}function k(){if(!l)throw new Error("reCaptcha has not been loaded yet.")}var l,m=g.defer(),n=m.promise,o={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var p=function(){l=f.grecaptcha,m.resolve(l)};if(f.vcRecaptchaApiLoadedCallback.push(p),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha))p();else if(f.document.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]'))var q=i(function(){a.isDefined(f.grecaptcha)&&(i.cancel(q),p())},25);else{var r=f.document.createElement("script");r.async=!0,r.defer=!0,r.src="https://www.google.com/recaptcha/api.js?onload="+c.onLoadFunctionName+"&render=explicit",h.find("body")[0].appendChild(r)}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.badge=c.badge||d.badge,c.sitekey&&40===c.sitekey.length||b(),j().then(function(b){var d=b.render(a,c);return o[d]=a,d})},reload:function(a){k(),l.reset(a),e.$broadcast("reCaptchaReset",a)},execute:function(a){k(),l.execute(a)},useLang:function(a,b){var c=o[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+=(-1===e.indexOf("?")?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return k(),l.getResponse(a)},getInstance:function(a){return o[a]},destroy:function(a){delete o[a]}}}]})}(angular),function(a){"use strict";a.module("vcRecaptcha").directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService",function(b,c,d){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",badge:"=?",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",!1===e.required?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,badge:e.badge||g.badge||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')}a.module("vcRecaptcha").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.setBadge=function(a){d.badge=a},c.setOnLoadFunctionName=function(a){c.onLoadFunctionName=a},c.$get=["$rootScope","$window","$q","$document","$interval",function(e,f,g,h,i){function j(){return l?g.when(l):n}function k(){if(!l)throw new Error("reCaptcha has not been loaded yet.")}var l,m=g.defer(),n=m.promise,o={};f.vcRecaptchaApiLoadedCallback=f.vcRecaptchaApiLoadedCallback||[];var p=function(){l=f.grecaptcha,m.resolve(l)};if(f.vcRecaptchaApiLoadedCallback.push(p),f[c.onLoadFunctionName]=function(){f.vcRecaptchaApiLoadedCallback.forEach(function(a){a()})},a.isDefined(f.grecaptcha))p();else if(f.document.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]'))var q=i(function(){a.isDefined(f.grecaptcha)&&(i.cancel(q),p())},25);else{var r=f.document.createElement("script");r.async=!0,r.defer=!0,r.src="https://www.google.com/recaptcha/api.js?onload="+c.onLoadFunctionName+"&render=explicit",h.find("body")[0].appendChild(r)}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.badge=c.badge||d.badge,c.sitekey&&40===c.sitekey.length||b(),j().then(function(b){var d=b.render(a,c);return o[d]=a,d})},reload:function(a){k(),l.reset(a),e.$broadcast("reCaptchaReset",a)},execute:function(a){k(),l.execute(a)},useLang:function(a,b){var c=o[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+=(-1===e.indexOf("?")?"?":"&")+"hl="+b,d.src=e}},getResponse:function(a){return k(),l.getResponse(a)},getInstance:function(a){return o[a]},destroy:function(a){delete o[a]}}}]})}(angular),function(a){"use strict";a.module("vcRecaptcha").directive("vcRecaptcha",["$document","$timeout","vcRecaptchaService","$q",function(b,c,d,e){return{restrict:"A",require:"?^^form",scope:{response:"=?ngModel",key:"=?",stoken:"=?",theme:"=?",size:"=?",type:"=?",lang:"=?",badge:"=?",tabindex:"=?",required:"=?",onCreate:"&",onSuccess:"&",onExpire:"&"},link:function(f,g,h,i){function j(){i&&i.$setValidity("recaptcha",null),m()}function k(){c(function(){f.response="",l(),f.onExpire({widgetId:f.widgetId})})}function l(){i&&i.$setValidity("recaptcha",!1===f.required?null:Boolean(f.response))}function m(){d.destroy(f.widgetId),a.element(b[0].querySelectorAll(".pls-container")).parent().remove()}f.widgetId=null,i&&a.isDefined(h.required)&&f.$watch("required",l);var n=f.$watch("key",function(b){if(!a.isUndefined(b)&&null!==b){var i=function(a){c(function(){f.response=a,l(),f.onSuccess({response:a,widgetId:f.widgetId})})};e.resolve(b).then(function(a){return d.create(g[0],{callback:i,key:a,stoken:f.stoken||h.stoken||null,theme:f.theme||h.theme||null,type:f.type||h.type||null,lang:f.lang||h.lang||null,tabindex:f.tabindex||h.tabindex||null,size:f.size||h.size||null,badge:f.badge||h.badge||null,"expired-callback":k})}).then(function(b){l(),f.widgetId=b,f.onCreate({widgetId:b}),f.$on("$destroy",j),f.$on("reCaptchaReset",function(c,d){(a.isUndefined(d)||b===d)&&(f.response="",l())})}),n()}})}}}])}(angular); \ No newline at end of file diff --git a/src/directive.js b/src/directive.js index 60b1ea9..24c9dfd 100644 --- a/src/directive.js +++ b/src/directive.js @@ -4,11 +4,11 @@ var app = ng.module('vcRecaptcha'); - app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', function ($document, $timeout, vcRecaptcha) { + app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', '$q', function ($document, $timeout, vcRecaptcha, $q) { return { restrict: 'A', - require: "?^^form", + require: '?^^form', scope: { response: '=?ngModel', key: '=?', @@ -27,11 +27,16 @@ link: function (scope, elm, attrs, ctrl) { scope.widgetId = null; - if(ctrl && ng.isDefined(attrs.required)){ + if (ctrl && ng.isDefined(attrs.required)) { scope.$watch('required', validate); } var removeCreationListener = scope.$watch('key', function (key) { + // Abort if undefined or null + if (ng.isUndefined(key) || key === null) { + return; + } + var callback = function (gRecaptchaResponse) { // Safe $apply $timeout(function () { @@ -43,19 +48,20 @@ }); }; - vcRecaptcha.create(elm[0], { - - callback: callback, - key: key, - 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, - badge: scope.badge || attrs.badge || null, - 'expired-callback': expired - + // Accept a promise, or resolve immediately if the value is a bare string + $q.resolve(key).then(function (resolved) { + return vcRecaptcha.create(elm[0], { + callback: callback, + key: resolved, + 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, + badge: scope.badge || attrs.badge || null, + 'expired-callback': expired + }); }).then(function (widgetId) { // The widget has been created validate(); @@ -64,13 +70,12 @@ scope.$on('$destroy', destroy); - scope.$on('reCaptchaReset', function(event, resetWidgetId){ - if(ng.isUndefined(resetWidgetId) || widgetId === resetWidgetId){ - scope.response = ""; - validate(); - } - }) - + scope.$on('reCaptchaReset', function (event, resetWidgetId) { + if (ng.isUndefined(resetWidgetId) || widgetId === resetWidgetId) { + scope.response = ''; + validate(); + } + }); }); // Remove this listener to avoid creating the widget more than once. @@ -78,36 +83,36 @@ }); function destroy() { - if (ctrl) { - // reset the validity of the form if we were removed - ctrl.$setValidity('recaptcha', null); - } + if (ctrl) { + // reset the validity of the form if we were removed + ctrl.$setValidity('recaptcha', null); + } - cleanup(); + cleanup(); } - function expired(){ + function expired() { // Safe $apply $timeout(function () { - scope.response = ""; + scope.response = ''; validate(); // Notify about the response availability - scope.onExpire({ widgetId: scope.widgetId }); + scope.onExpire({widgetId: scope.widgetId}); }); } - function validate(){ - if(ctrl){ + function validate() { + if (ctrl) { ctrl.$setValidity('recaptcha', scope.required === false ? null : Boolean(scope.response)); } } - function cleanup(){ - vcRecaptcha.destroy(scope.widgetId); + function cleanup() { + vcRecaptcha.destroy(scope.widgetId); - // removes elements reCaptcha added. - ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); + // removes elements reCaptcha added. + ng.element($document[0].querySelectorAll('.pls-container')).parent().remove(); } } };