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

Commit

Permalink
Change service to serivce provider
Browse files Browse the repository at this point in the history
Resolves #98 by changing  the service to a service provider which allows the user to set default values for the site key, theme, stoken, size, and type. These
defaults can be overridden by setting the values on individual instances
of the directive.
The key validation has been moved out of the directive and into the
service since now the directive does not require a key (as it can use the
default defined in the provider during the config by the user).
  • Loading branch information
TheSharpieOne committed Feb 6, 2016
1 parent 4e8d274 commit 6617d58
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 91 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,35 @@ If you want to use a secure token pass it along with the site key as an html att
Please note that you have to enrypt your token yourself with your private key upfront!
To learn more about secure tokens and how to generate & encrypt them please refer to the [reCAPTCHA Docs](https://developers.google.com/recaptcha/docs/secure_token).

Service Provider
----------------
You can use the vcRecaptchaServiceProvider to configure the recaptcha service once in your application's config function.
This is a convenient way to set your reCaptcha site key, theme, stoken, size, and type in one place instead of each vc-recaptcha directive element instance.
The defaults defined in the service provider will be overrode by any values passed to the vc-recaptcha directive element for that instance.

```javascript
myApp.config(function(vcRecaptchaServiceProvider){
vcRecaptchaServiceProvider.setSiteKey('---- YOUR PUBLIC KEY GOES HERE ----')
vcRecaptchaServiceProvider.setTheme('---- light or dark ----')
vcRecaptchaServiceProvider.setStoken('--- YOUR GENERATED SECURE TOKEN ---')
vcRecaptchaServiceProvider.setSize('---- compact or normal ----')
vcRecaptchaServiceProvider.setType('---- audio or image ----')
});
```

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 ----'
});
```
Note: any value omitted will be undefined, even if previously set.
Differences with the old reCaptcha
----------------------------------
Expand Down
22 changes: 4 additions & 18 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
(function (ng) {
'use strict';

function throwNoKeyException() {
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 app = ng.module('vcRecaptcha');

app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', function ($document, $timeout, vcRecaptcha) {
Expand All @@ -15,7 +11,7 @@
require: "?^^form",
scope: {
response: '=?ngModel',
key: '=',
key: '=?',
stoken: '=?',
theme: '=?',
size: '=?',
Expand All @@ -25,22 +21,10 @@
onExpire: '&'
},
link: function (scope, elm, attrs, ctrl) {
if (!attrs.hasOwnProperty('key')) {
throwNoKeyException();
}

scope.widgetId = null;

var sessionTimeout;
var removeCreationListener = scope.$watch('key', function (key) {
if (!key) {
return;
}

if (key.length !== 40) {
throwNoKeyException();
}

var callback = function (gRecaptchaResponse) {
// Safe $apply
$timeout(function () {
Expand All @@ -63,8 +47,10 @@
}, 2 * 60 * 1000);
};

vcRecaptcha.create(elm[0], key, callback, {
vcRecaptcha.create(elm[0], {

callback: callback,
key: key,
stoken: scope.stoken || attrs.stoken || null,
theme: scope.theme || attrs.theme || null,
tabindex: scope.tabindex || attrs.tabindex || null,
Expand Down
221 changes: 148 additions & 73 deletions src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,172 @@
(function (ng) {
'use strict';

function throwNoKeyException() {
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 app = ng.module('vcRecaptcha');

/**
* An angular service to wrap the reCaptcha API
*/
app.service('vcRecaptchaService', ['$window', '$q', function ($window, $q) {
var deferred = $q.defer(), promise = deferred.promise, recaptcha;
app.provider('vcRecaptchaService', function(){
var provider = this;
var config = {};

/**
* Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param defaults object which overrides the current defaults object.
*/
provider.setDefaults = function(defaults){
angular.copy(config, defaults);
};

$window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || [];
/**
* Sets the reCaptcha key which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param siteKey the reCaptcha public key (refer to the README file if you don't know what this is).
*/
provider.setSiteKey = function(siteKey){
config.key = siteKey;
};

var callback = function () {
recaptcha = $window.grecaptcha;
/**
* Sets the reCaptcha theme which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param theme The reCaptcha theme.
*/
provider.setTheme = function(theme){
config.theme = theme;
};

deferred.resolve(recaptcha);
/**
* Sets the reCaptcha stoken which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param stoken The reCaptcha stoken.
*/
provider.setStoken = function(stoken){
config.stoken = stoken;
};

$window.vcRecaptchaApiLoadedCallback.push(callback);
/**
* Sets the reCaptcha size which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param size The reCaptcha size.
*/
provider.setSize = function(size){
config.size = size;
};

$window.vcRecaptchaApiLoaded = function () {
$window.vcRecaptchaApiLoadedCallback.forEach(function(callback) {
callback();
});
/**
* Sets the reCaptcha type which will be used by default is not specified in a specific directive instance.
*
* @since 2.5.0
* @param type The reCaptcha type.
*/
provider.setType = function(type){
config.type = type;
};

provider.$get = ['$window', '$q', function ($window, $q) {
var deferred = $q.defer(), promise = deferred.promise, recaptcha;

function getRecaptcha() {
if (!!recaptcha) {
return $q.when(recaptcha);
}
$window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || [];

return promise;
}
var callback = function () {
recaptcha = $window.grecaptcha;

function validateRecaptchaInstance() {
if (!recaptcha) {
throw new Error('reCaptcha has not been loaded yet.');
}
}


// Check if grecaptcha is not defined already.
if (ng.isDefined($window.grecaptcha)) {
callback();
}

return {

/**
* Creates a new reCaptcha object
*
* @param elm the DOM element where to put the captcha
* @param key the recaptcha public key (refer to the README file if you don't know what this is)
* @param fn a callback function to call when the captcha is resolved
* @param conf the captcha object configuration
*/
create: function (elm, key, fn, conf) {
conf.callback = fn;
conf.sitekey = key;

return getRecaptcha().then(function (recaptcha) {
return recaptcha.render(elm, conf);
deferred.resolve(recaptcha);
};

$window.vcRecaptchaApiLoadedCallback.push(callback);

$window.vcRecaptchaApiLoaded = function () {
$window.vcRecaptchaApiLoadedCallback.forEach(function(callback) {
callback();
});
},

/**
* Reloads the reCaptcha
*/
reload: function (widgetId) {
validateRecaptchaInstance();

// $log.info('Reloading captcha');
recaptcha.reset(widgetId);

// reCaptcha will call the same callback provided to the
// create function once this new captcha is resolved.
},

/**
* Gets the response from the reCaptcha widget.
*
* @see https://developers.google.com/recaptcha/docs/display#js_api
*
* @returns {String}
*/
getResponse: function (widgetId) {
validateRecaptchaInstance();

return recaptcha.getResponse(widgetId);
};


function getRecaptcha() {
if (!!recaptcha) {
return $q.when(recaptcha);
}

return promise;
}

function validateRecaptchaInstance() {
if (!recaptcha) {
throw new Error('reCaptcha has not been loaded yet.');
}
}


// Check if grecaptcha is not defined already.
if (ng.isDefined($window.grecaptcha)) {
callback();
}
};

}]);
return {

/**
* Creates a new reCaptcha object
*
* @param elm the DOM element where to put the captcha
* @param conf the captcha object configuration
* @throws NoKeyException if no key is provided in the provider config or the directive instance (via attribute)
*/
create: function (elm, conf) {

conf.key = conf.key || config.key;
conf.theme = conf.theme || config.theme;
conf.stoken = conf.stoken || config.stoken;
conf.size = conf.size || config.size;
conf.type = conf.type || config.type;

if (!conf.key || conf.key.length !== 40) {
throwNoKeyException();
}
return getRecaptcha().then(function (recaptcha) {
return recaptcha.render(elm, conf);
});
},

/**
* Reloads the reCaptcha
*/
reload: function (widgetId) {
validateRecaptchaInstance();

// $log.info('Reloading captcha');
recaptcha.reset(widgetId);

// reCaptcha will call the same callback provided to the
// create function once this new captcha is resolved.
},

/**
* Gets the response from the reCaptcha widget.
*
* @see https://developers.google.com/recaptcha/docs/display#js_api
*
* @returns {String}
*/
getResponse: function (widgetId) {
validateRecaptchaInstance();

return recaptcha.getResponse(widgetId);
}
};

}];
});

}(angular));

0 comments on commit 6617d58

Please sign in to comment.