Skip to content
This repository has been archived by the owner on Aug 17, 2021. It is now read-only.

Allow site key to be a promise #220

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 44 additions & 39 deletions release/angular-recaptcha.js
Original file line number Diff line number Diff line change
@@ -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 */
Expand Down Expand Up @@ -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: '=?',
Expand All @@ -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 () {
Expand All @@ -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();
Expand All @@ -355,50 +361,49 @@

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.
removeCreationListener();
});

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();
}
}
};
Expand Down
6 changes: 3 additions & 3 deletions release/angular-recaptcha.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 42 additions & 37 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: '=?',
Expand All @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This early return prevents the service from throwing an error when the key is undefined. Check the failing test.

}

var callback = function (gRecaptchaResponse) {
// Safe $apply
$timeout(function () {
Expand All @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, this should go in the service and not in the directive. The support for key-as-promise would be complete in this project, since ATM the service doesn't support a key as promise.. Also, it could make testing the directive simpler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, should we use $q.when instead of $q.resolve? The latter was added in Angular 1.4

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to have the feature in the service as well as the directive.
$q.when should be used for the reason mentioned above, more support.

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();
Expand All @@ -64,50 +70,49 @@

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.
removeCreationListener();
});

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();
}
}
};
Expand Down