Skip to content

Commit

Permalink
Added Recaptcha and RecaptchaLoader components along with a sample page
Browse files Browse the repository at this point in the history
  • Loading branch information
DethAriel committed Aug 3, 2016
1 parent e003fc5 commit 6412fc4
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Editor settings
.vscode/

# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history
4 changes: 4 additions & 0 deletions ng2-recaptcha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { RecaptchaComponent } from './recaptcha/recaptcha.component';

export * from './recaptcha/recaptcha.component';
export * from './recaptcha/recaptcha-loader.service';
32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "ng2-recaptcha",
"version": "0.0.2",
"description": "Angular 2 + TypeScript component for Google reCAPTCHA",
"scripts": {
"showsample": "npm run copysamples && lite-server -c sample/lite-config.json",
"copysamples": "ncp recaptcha/ sample/app/"
},
"repository": {
"type": "git",
"url": "git+https://github.com/DethAriel/ng2-recaptcha"
},
"keywords": [
"angular2",
"recaptcha"
],
"author": "Ruslan Arkhipau <[email protected]>",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^2.0.0-rc.4"
},
"devDependencies": {
"@angular/core": "^2.0.0-rc.4",
"lite-server": "^2.2.0",
"ncp": "^2.0.0",
"rxjs": "5.0.0-beta.6",
"typescript": "1.8.10",
"typings": "0.8.1",
"zone.js": "0.6.12"
},
"homepage": "https://github.com/DethAriel/ng2-recaptcha"
}
37 changes: 37 additions & 0 deletions recaptcha/recaptcha-loader.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
Injectable,
EventEmitter,
Optional,
} from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class RecaptchaLoaderService {
private _language: string;
private static _ready: BehaviorSubject<boolean>;
public ready: Observable<boolean>;

constructor(@Optional() language?: string) {
this._language = language;
this._init();
this.ready = RecaptchaLoaderService._ready.asObservable();
}

private _init() {
if (RecaptchaLoaderService._ready) {
return;
}
window['ng2recaptchaloaded'] = () => {
RecaptchaLoaderService._ready.next(true);
};
RecaptchaLoaderService._ready = new BehaviorSubject<boolean>(false);
var head = <HTMLHeadElement> document.head;
var script = <HTMLScriptElement>document.createElement('script');
script.innerHTML = '';
script.src = 'https://www.google.com/recaptcha/api.js?render=explicit&onload=ng2recaptchaloaded' + (this._language ? '&hl=' + this._language : '');
script.async = true;
script.defer = true;
head.appendChild(script);
}
}
85 changes: 85 additions & 0 deletions recaptcha/recaptcha.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
OnDestroy,
Output,
Input,
NgZone,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { RecaptchaLoaderService } from './recaptcha-loader.service';
import { Subscription } from 'rxjs/Subscription';

export type RecaptchaTheme = "light" | "dark";
export type RecaptchaType = "image" | "audio";
export type RecaptchaSize = "normal" | "compact";

var nextId = 0;

@Component({
selector: 'recaptcha',
template: `<div [id]="id"></div>`,
providers: [RecaptchaLoaderService],
})
export class RecaptchaComponent implements AfterViewInit, OnDestroy {
@Input() id = `ngrecaptcha-${nextId++}`;

@Input() siteKey: string;
@Input() theme: RecaptchaTheme;
@Input() type: RecaptchaType;
@Input() size: RecaptchaSize;
@Input() tabIndex: number;
@Input() language: string;

@Output() resolved = new EventEmitter<string>();
private subscription: Subscription;
private widget: any;
private isResolved = false;

constructor(
private _el: ElementRef,
private _loader: RecaptchaLoaderService,
private _zone: NgZone
) {
}
ngAfterViewInit() {
this.subscription = this._loader.ready.subscribe(loaded => {
if (loaded) {
this._renderRecaptcha();
}
});
}

ngOnDestroy() {
this.subscription.unsubscribe();
}

reset() {
grecaptcha.reset(this.widget);
this.isResolved = false;
}

private captchaReponseCallback(response: string) {
this.isResolved = true;
this.resolved.emit(response);
}

private _renderRecaptcha() {
this.widget = grecaptcha.render(this.id, {
'callback': (response) => {
this._zone.run(() => this.captchaReponseCallback(response));
},
'expired-callback': () => {
this._zone.run(() => this.reset());
},
'sitekey': this.siteKey,
'theme': this.theme,
'type': this.type,
'size': this.size,
'tabindex': this.tabIndex,
});
}
}
2 changes: 2 additions & 0 deletions sample/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.ts
!main.ts
41 changes: 41 additions & 0 deletions sample/app/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { bootstrap } from '@angular/platform-browser-dynamic';
import {
Component,
provide,
} from '@angular/core';
import { RecaptchaComponent } from './recaptcha.component';
import { RecaptchaLoaderService } from './recaptcha-loader.service';

@Component({
selector: 'my-app',
template: `
<section *ngIf="!siteKey">
<label>
Use this site key:
<input type="string" [(ngModel)]="enteredSiteKey" />
</label>
<button (click)="siteKey = enteredSiteKey">Apply</button>
</section>
<section *ngIf="siteKey">
<recaptcha (resolved)="resolved(1, $event)" [siteKey]="siteKey"></recaptcha>
<button (click)="secondCaptchaVisible = !secondCaptchaVisible">Toggle another captcha</button>
<recaptcha *ngIf="secondCaptchaVisible" (resolved)="resolved(2, $event)" [siteKey]="siteKey"></recaptcha>
</section>
`,
directives: [RecaptchaComponent],
}) export class MyApp {
siteKey: string;
secondCaptchaVisible = false;

resolved(captchaIndex: number, captchaResponse: string) {
console.log(`Resolved captcha #${captchaIndex} with response:`);
console.log(captchaResponse);
}
}

bootstrap(MyApp, [
provide(RecaptchaLoaderService, {
useValue: new RecaptchaLoaderService("fr"),
})
]);
21 changes: 21 additions & 0 deletions sample/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Angular2 ReCaptcha sample</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
<script src="https://npmcdn.com/[email protected]?main=browser"></script>
<script src="https://npmcdn.com/[email protected]"></script>
<script src="https://npmcdn.com/[email protected]/dist/system.src.js"></script>

<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err) { console.error(err); });
</script>
</head>
<body>
<my-app></my-app>
</body>
</html>
3 changes: 3 additions & 0 deletions sample/lite-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"server": { "baseDir": "./sample" }
}
73 changes: 73 additions & 0 deletions sample/systemjs.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* PLUNKER VERSION (based on systemjs.config.js in angular.io)
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function(global) {

var ngVer = '@2.0.0-rc.1'; // lock in the angular package version; do not let it float to current!

//map tells the System loader where to look for things
var map = {
'app': 'app',

'@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
'rxjs': 'https://npmcdn.com/[email protected]',
'ts': 'https://npmcdn.com/[email protected]/lib/plugin.js',
'typescript': 'https://npmcdn.com/[email protected]/lib/typescript.js',
};

//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};

var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'upgrade',
];

// Add map entries for each angular package
// only because we're pinning the version with `ngVer`.
ngPackageNames.forEach(function(pkgName) {
map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
});

// Add package entries for angular packages
ngPackageNames.forEach(function(pkgName) {

// Bundled (~40 requests):
packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };

// Individual files (~300 requests):
//packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
});

var config = {
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
"exports": "ts"
}
},
map: map,
packages: packages
}

System.config(config);

})(this);
15 changes: 15 additions & 0 deletions sample/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": false,
"declaration": true,
"removeComments": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"listFiles": false,
"noLib": false
}
}
23 changes: 23 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": false,
"declaration": true,
"removeComments": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"listFiles": false,
"noLib": false
},
"exclude": [
"node_modules"
],
"files": [
"./typings/browser.d.ts"
],
"compileOnSave": false,
"buildOnSave": false
}
6 changes: 6 additions & 0 deletions typings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"devDependencies": {},
"ambientDependencies": {
"es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654"
}
}

0 comments on commit 6412fc4

Please sign in to comment.