├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── dist ├── angular-google-plus.js └── angular-google-plus.min.js ├── karma.conf.js ├── package.json ├── src └── angular-google-plus.js └── test ├── dom └── index.html └── unit └── angular-google-plus.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | coverage 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // -------------------------------------------------------------------- 3 | // JSHint Configuration, Strict Edition 4 | // -------------------------------------------------------------------- 5 | // 6 | // This is a options template for [JSHint][1], using [JSHint example][2] 7 | // and [Ory Band's example][3] as basis and setting config values to 8 | // be most strict: 9 | // 10 | // * set all enforcing options to true 11 | // * set all relaxing options to false 12 | // * set all environment options to false, except the browser value 13 | // * set all JSLint legacy options to false 14 | // 15 | // [1]: http://www.jshint.com/ 16 | // [2]: https://github.com/jshint/node-jshint/blob/master/example/config.json 17 | // [3]: https://github.com/oryband/dotfiles/blob/master/jshintrc 18 | // 19 | // @author http://michael.haschke.biz/ 20 | // @license http://unlicense.org/ 21 | 22 | // == Enforcing Options =============================================== 23 | // 24 | // These options tell JSHint to be more strict towards your code. Use 25 | // them if you want to allow only a safe subset of JavaScript, very 26 | // useful when your codebase is shared with a big number of developers 27 | // with different skill levels. 28 | 29 | "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). 30 | "curly" : true, // Require {} for every new block or scope. 31 | "eqeqeq" : true, // Require triple equals i.e. `===`. 32 | "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. 33 | "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 34 | "latedef" : true, // Prohibit variable use before definition. 35 | "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. 36 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. 37 | "noempty" : true, // Prohibit use of empty blocks. 38 | "nonew" : true, // Prohibit use of constructors for side-effects. 39 | "plusplus" : true, // Prohibit use of `++` & `--`. 40 | "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. 41 | "undef" : true, // Require all non-global variables be declared before they are used. 42 | "strict" : true, // Require `use strict` pragma in every file. 43 | "trailing" : true, // Prohibit trailing whitespaces. 44 | 45 | // == Relaxing Options ================================================ 46 | // 47 | // These options allow you to suppress certain types of warnings. Use 48 | // them only if you are absolutely positive that you know what you are 49 | // doing. 50 | 51 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). 52 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. 53 | "debug" : false, // Allow debugger statements e.g. browser breakpoints. 54 | "eqnull" : false, // Tolerate use of `== null`. 55 | "es5" : false, // Allow EcmaScript 5 syntax. 56 | "esnext" : false, // Allow ES.next specific features such as `const` and `let`. 57 | "evil" : false, // Tolerate use of `eval`. 58 | "expr" : false, // Tolerate `ExpressionStatement` as Programs. 59 | "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. 60 | "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). 61 | "iterator" : false, // Allow usage of __iterator__ property. 62 | "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. 63 | "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. 64 | "laxcomma" : false, // Suppress warnings about comma-first coding style. 65 | "loopfunc" : false, // Allow functions to be defined within loops. 66 | "multistr" : false, // Tolerate multi-line strings. 67 | "onecase" : false, // Tolerate switches with just one case. 68 | "proto" : false, // Tolerate __proto__ property. This property is deprecated. 69 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. 70 | "scripturl" : false, // Tolerate script-targeted URLs. 71 | "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. 72 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. 73 | "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 74 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. 75 | "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. 76 | 77 | // == Environments ==================================================== 78 | // 79 | // These options pre-define global variables that are exposed by 80 | // popular JavaScript libraries and runtime environments—such as 81 | // browser or node.js. 82 | 83 | "browser" : true, // Standard browser globals e.g. `window`, `document`. 84 | "couch" : false, // Enable globals exposed by CouchDB. 85 | "devel" : false, // Allow development statements e.g. `console.log();`. 86 | "dojo" : false, // Enable globals exposed by Dojo Toolkit. 87 | "jquery" : false, // Enable globals exposed by jQuery JavaScript library. 88 | "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. 89 | "node" : false, // Enable globals available when code is running inside of the NodeJS runtime environment. 90 | "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. 91 | "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. 92 | "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. 93 | "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. 94 | "jasmine" : true, 95 | 96 | "globals": { 97 | "gapi": true 98 | }, 99 | 100 | // == JSLint Legacy =================================================== 101 | // 102 | // These options are legacy from JSLint. Aside from bug fixes they will 103 | // not be improved in any way and might be removed at any point. 104 | 105 | "nomen" : false, // Prohibit use of initial or trailing underbars in names. 106 | "onevar" : false, // Allow only one `var` statement per function. 107 | "passfail" : false, // Stop on first error. 108 | "white" : false, // Check against strict whitespace and indentation rules. 109 | 110 | // == Undocumented Options ============================================ 111 | // 112 | // While I've found these options in [example1][2] and [example2][3] 113 | // they are not described in the [JSHint Options documentation][4]. 114 | // 115 | // [4]: http://www.jshint.com/options/ 116 | 117 | "maxerr" : 100, // Maximum errors before stopping. 118 | "predef" : [ // Extra globals. 119 | //"exampleVar", 120 | //"anotherCoolGlobal", 121 | //"iLoveDouglas" 122 | ], 123 | "indent" : 2 // Specify indentation spacing 124 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: 5 | - npm install -g karma bower grunt-cli 6 | - bower install 7 | - npm install -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.3 2 | 3 | - Enable use of server side sign-in (573dcec, 710aba5) 4 | - API: Add logout method (2a2d10c) 5 | 6 | ## 0.1.2 7 | 8 | - Bower: remove minifed version from main value 9 | - Dependencies: remove connect dependency (Gruntfile) 10 | - API: Add the getUser method and remove it from login (#1, #4) 11 | 12 | #### Breaking Changes 13 | 14 | The API for login has been changed (#4): 15 | 16 | **Before:** 17 | 18 | ```javascript 19 | GooglePlus.login().then(function (user) { 20 | console.log(user); 21 | }, function (err) { 22 | console.log(err); 23 | }); 24 | ``` 25 | 26 | **After:** 27 | 28 | ```javascript 29 | GooglePlus.login().then(function (authResult) { 30 | console.log(authResult); 31 | 32 | GooglePlus.getUser().then(function (user) { 33 | console.log(user); 34 | }); 35 | }, function (err) { 36 | console.log(err); 37 | }); 38 | ``` 39 | 40 | ## 0.1.1 41 | 42 | - added setToken / getToken methods 43 | 44 | ## 0.1.0 45 | 46 | - Initial release -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | 7 | jshint: { 8 | jshintrc: '.jshintrc', 9 | gruntfile: { 10 | src: 'Gruntfile.js' 11 | }, 12 | source: { 13 | src: ['src/**/*.js', 'test/**/*.js'] 14 | } 15 | }, 16 | watch: { 17 | gruntfile: { 18 | files: '<%= jshint.gruntfile.src %>', 19 | tasks: ['jshint:gruntfile'] 20 | }, 21 | dist: { 22 | files: '<%= jshint.source.src %>', 23 | tasks: ['jshint', 'uglify:dist', 'uglify:src'] 24 | } 25 | }, 26 | uglify: { 27 | dist: { 28 | options: { 29 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> ' + 30 | '<%= grunt.template.today("yyyy-mm-dd") %> */\n' 31 | }, 32 | files: { 33 | 'dist/angular-google-plus.min.js': ['src/angular-google-plus.js'] 34 | } 35 | }, 36 | src: { 37 | options: { 38 | beautify: true, 39 | compress: false, 40 | preserveComments: 'all', 41 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> ' + 42 | '<%= grunt.template.today("yyyy-mm-dd") %> */\n' 43 | }, 44 | files: { 45 | 'dist/angular-google-plus.js': ['src/angular-google-plus.js'] 46 | } 47 | } 48 | }, 49 | karma: { 50 | unit: { 51 | configFile: 'karma.conf.js' 52 | }, 53 | ci: { 54 | configFile: 'karma.conf.js', 55 | singleRun: true, 56 | browsers: ['PhantomJS'] 57 | } 58 | } 59 | }); 60 | 61 | // These plugins provide necessary tasks. 62 | grunt.loadNpmTasks('grunt-karma'); 63 | grunt.loadNpmTasks('grunt-contrib-jshint'); 64 | grunt.loadNpmTasks('grunt-contrib-watch'); 65 | grunt.loadNpmTasks('grunt-contrib-uglify'); 66 | 67 | // Default task. 68 | grunt.registerTask('default', ['jshint', 'uglify:dist', 'uglify:src']); 69 | }; 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Moritz Meyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angular-google-plus 2 | ================== 3 | 4 | [![Build Status](http://img.shields.io/travis/mrzmyr/angular-google-plus.svg?style=flat)](https://travis-ci.org/mrzmyr/angular-google-plus) [![npm version](https://badge.fury.io/js/angular-google-plus.svg)](http://badge.fury.io/js/angular-google-plus) [![Bower version](https://badge.fury.io/bo/angular-google-plus.svg)](http://badge.fury.io/bo/angular-google-plus) 5 | 6 | > A angular module which handles the login with the Google+ API 7 | 8 | #### Demo 9 | 10 | Try [this demo](http://plnkr.co/edit/jvHVtNedJoPcqRKg8OLz?p=preview). _Remind that there is no `API Key` and `Client ID` inserted_ 11 | 12 | #### Install 13 | 14 | Install the angular module with bower or npm. 15 | 16 | ``` 17 | $ bower install angular-google-plus 18 | ``` 19 | 20 | ``` 21 | $ npm install angular-google-plus 22 | ``` 23 | 24 | #### Usage 25 | 26 | ```js 27 | var app = angular.module('app', ['googleplus']); 28 | 29 | app.config(['GooglePlusProvider', function(GooglePlusProvider) { 30 | GooglePlusProvider.init({ 31 | clientId: 'YOUR_CLIENT_ID', 32 | apiKey: 'YOUR_API_KEY' 33 | }); 34 | }]); 35 | 36 | app.controller('AuthCtrl', ['$scope', 'GooglePlus', function ($scope, GooglePlus) { 37 | $scope.login = function () { 38 | GooglePlus.login().then(function (authResult) { 39 | console.log(authResult); 40 | 41 | GooglePlus.getUser().then(function (user) { 42 | console.log(user); 43 | }); 44 | }, function (err) { 45 | console.log(err); 46 | }); 47 | }; 48 | }]); 49 | ``` 50 | 51 | #### Credits 52 | 53 | - Insperation from [jakemmarsh's gist](https://gist.github.com/jakemmarsh/5809963) 54 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-google-plus", 3 | "version": "0.1.3", 4 | "homepage": "https://github.com/mrzmyr/angular-google-plus", 5 | "authors": [ 6 | "Moritz Meyer " 7 | ], 8 | "main": "dist/angular-google-plus.js", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "angular": ">=1.2.0 <=1.4" 19 | }, 20 | "devDependencies": { 21 | "angular-mocks": ">=1.2.0 <=1.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dist/angular-google-plus.js: -------------------------------------------------------------------------------- 1 | /*! angular-google-plus - v0.1.3 2015-08-27 */ 2 | /** 3 | * googleplus module 4 | */ 5 | angular.module("googleplus", []).provider("GooglePlus", [ function() { 6 | /** 7 | * Options object available for module 8 | * options/services definition. 9 | * @type {Object} 10 | */ 11 | var a = {}; 12 | /** 13 | * clientId 14 | * @type {Number} 15 | */ 16 | a.clientId = null; 17 | this.setClientId = function(b) { 18 | a.clientId = b; 19 | return this; 20 | }; 21 | this.getClientId = function() { 22 | return a.clientId; 23 | }; 24 | /** 25 | * apiKey 26 | * @type {String} 27 | */ 28 | a.apiKey = null; 29 | this.setApiKey = function(b) { 30 | a.apiKey = b; 31 | return this; 32 | }; 33 | this.getApiKey = function() { 34 | return a.apiKey; 35 | }; 36 | /** 37 | * Scopes 38 | * @default 'https://www.googleapis.com/auth/plus.login' 39 | * @type {Boolean} 40 | */ 41 | a.scopes = "https://www.googleapis.com/auth/plus.login"; 42 | this.setScopes = function(b) { 43 | a.scopes = b; 44 | return this; 45 | }; 46 | this.getScopes = function() { 47 | return a.scopes; 48 | }; 49 | /** 50 | * Init Google Plus API 51 | */ 52 | this.init = function(b) { 53 | angular.extend(a, b); 54 | }; 55 | /** 56 | * Make sign-in server side 57 | */ 58 | this.enableServerSide = function() { 59 | a.accessType = "offline"; 60 | a.responseType = "code token id_token gsession"; 61 | }; 62 | /** 63 | * Make sign-in client side (default) 64 | */ 65 | this.disableServerSide = function() { 66 | delete a.accessType; 67 | delete a.responseType; 68 | }; 69 | /** 70 | * This defines the Google Plus Service on run. 71 | */ 72 | this.$get = [ "$q", "$rootScope", "$timeout", function(b, c, d) { 73 | /** 74 | * Define a deferred instance that will implement asynchronous calls 75 | * @type {Object} 76 | */ 77 | var e; 78 | /** 79 | * NgGooglePlus Class 80 | * @type {Class} 81 | */ 82 | var f = function() {}; 83 | f.prototype.login = function() { 84 | e = b.defer(); 85 | var c = { 86 | client_id: a.clientId, 87 | scope: a.scopes, 88 | immediate: false 89 | }; 90 | if (a.accessType && a.responseType) { 91 | c.access_type = a.accessType; 92 | c.response_type = a.responseType; 93 | } 94 | gapi.auth.authorize(c, this.handleAuthResult); 95 | return e.promise; 96 | }; 97 | f.prototype.checkAuth = function() { 98 | gapi.auth.authorize({ 99 | client_id: a.clientId, 100 | scope: a.scopes, 101 | immediate: true 102 | }, this.handleAuthResult); 103 | }; 104 | f.prototype.handleClientLoad = function() { 105 | gapi.client.setApiKey(a.apiKey); 106 | gapi.auth.init(function() {}); 107 | d(this.checkAuth, 1); 108 | }; 109 | f.prototype.handleAuthResult = function(a) { 110 | if (a && !a.error) { 111 | e.resolve(a); 112 | c.$apply(); 113 | } else { 114 | e.reject("error"); 115 | } 116 | }; 117 | f.prototype.getUser = function() { 118 | var a = b.defer(); 119 | gapi.client.load("oauth2", "v2", function() { 120 | gapi.client.oauth2.userinfo.get().execute(function(b) { 121 | a.resolve(b); 122 | c.$apply(); 123 | }); 124 | }); 125 | return a.promise; 126 | }; 127 | f.prototype.getToken = function() { 128 | return gapi.auth.getToken(); 129 | }; 130 | f.prototype.setToken = function(a) { 131 | return gapi.auth.setToken(a); 132 | }; 133 | f.prototype.logout = function() { 134 | gapi.auth.signOut(); 135 | }; 136 | return new f(); 137 | } ]; 138 | } ]).run([ function() { 139 | var a = document.createElement("script"); 140 | a.type = "text/javascript"; 141 | a.async = true; 142 | a.src = "https://apis.google.com/js/client.js"; 143 | var b = document.getElementsByTagName("script")[0]; 144 | b.parentNode.insertBefore(a, b); 145 | } ]); -------------------------------------------------------------------------------- /dist/angular-google-plus.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-google-plus - v0.1.3 2015-08-27 */ 2 | angular.module("googleplus",[]).provider("GooglePlus",[function(){var a={};a.clientId=null,this.setClientId=function(b){return a.clientId=b,this},this.getClientId=function(){return a.clientId},a.apiKey=null,this.setApiKey=function(b){return a.apiKey=b,this},this.getApiKey=function(){return a.apiKey},a.scopes="https://www.googleapis.com/auth/plus.login",this.setScopes=function(b){return a.scopes=b,this},this.getScopes=function(){return a.scopes},this.init=function(b){angular.extend(a,b)},this.enableServerSide=function(){a.accessType="offline",a.responseType="code token id_token gsession"},this.disableServerSide=function(){delete a.accessType,delete a.responseType},this.$get=["$q","$rootScope","$timeout",function(b,c,d){var e,f=function(){};return f.prototype.login=function(){e=b.defer();var c={client_id:a.clientId,scope:a.scopes,immediate:!1};return a.accessType&&a.responseType&&(c.access_type=a.accessType,c.response_type=a.responseType),gapi.auth.authorize(c,this.handleAuthResult),e.promise},f.prototype.checkAuth=function(){gapi.auth.authorize({client_id:a.clientId,scope:a.scopes,immediate:!0},this.handleAuthResult)},f.prototype.handleClientLoad=function(){gapi.client.setApiKey(a.apiKey),gapi.auth.init(function(){}),d(this.checkAuth,1)},f.prototype.handleAuthResult=function(a){a&&!a.error?(e.resolve(a),c.$apply()):e.reject("error")},f.prototype.getUser=function(){var a=b.defer();return gapi.client.load("oauth2","v2",function(){gapi.client.oauth2.userinfo.get().execute(function(b){a.resolve(b),c.$apply()})}),a.promise},f.prototype.getToken=function(){return gapi.auth.getToken()},f.prototype.setToken=function(a){return gapi.auth.setToken(a)},f.prototype.logout=function(){gapi.auth.signOut()},new f}]}]).run([function(){var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="https://apis.google.com/js/client.js";var b=document.getElementsByTagName("script")[0];b.parentNode.insertBefore(a,b)}]); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sat Oct 19 2013 13:04:35 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | frameworks: ['jasmine'], 13 | 14 | 15 | // list of files / patterns to load in the browser 16 | files: [ 17 | 'bower_components/angular/angular.js', 18 | 'bower_components/angular-mocks/angular-mocks.js', 19 | 'src/*.js', 20 | 'test/**/*.spec.js' 21 | ], 22 | 23 | // list of files to exclude 24 | exclude: [], 25 | 26 | // test results reporter to use 27 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 28 | reporters: ['progress'], 29 | 30 | 31 | // web server port 32 | port: 9876, 33 | 34 | 35 | // enable / disable colors in the output (reporters and logs) 36 | colors: true, 37 | 38 | 39 | // level of logging 40 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 41 | logLevel: config.LOG_INFO, 42 | 43 | 44 | // enable / disable watching file and executing tests whenever any file changes 45 | autoWatch: true, 46 | 47 | 48 | // Start these browsers, currently available: 49 | // - Chrome 50 | // - ChromeCanary 51 | // - Firefox 52 | // - Opera 53 | // - Safari (only Mac) 54 | // - PhantomJS 55 | // - IE (only Windows) 56 | browsers: ['Opera', 'Chrome', 'Firefox', 'Safari', 'PhantomJS'], 57 | 58 | 59 | // If browser does not capture in given timeout [ms], kill it 60 | captureTimeout: 60000, 61 | 62 | 63 | // Continuous Integration mode 64 | // if true, it capture browsers, run tests and exit 65 | singleRun: false 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-google-plus", 3 | "version": "0.1.3", 4 | "description": "A angular module which handles the login with the Google+ API", 5 | "main": "dist/angular-google-plus.js", 6 | "scripts": { 7 | "test": "grunt karma:ci" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/mrzmyr/angular-google-plus.git" 12 | }, 13 | "keywords": [ 14 | "google", 15 | "plus", 16 | "angular", 17 | "login", 18 | "api" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/mrzmyr/angular-google-plus/issues" 22 | }, 23 | "author": "Moritz Meyer ", 24 | "license": "MIT", 25 | "readmeFilename": "README.md", 26 | "gitHead": "e6875b80904f553b9f6fddae1efce1d5d7008483", 27 | "devDependencies": { 28 | "grunt": "~0.4.1", 29 | "grunt-contrib-jshint": "^0.11.2", 30 | "grunt-contrib-uglify": "^0.9.1", 31 | "grunt-contrib-watch": "~0.6.1", 32 | "grunt-karma": "^0.12.0", 33 | "jasmine-core": "^2.3.4", 34 | "karma": "^0.13.9", 35 | "karma-chrome-launcher": "^0.2.0", 36 | "karma-coffee-preprocessor": "^0.3.0", 37 | "karma-firefox-launcher": "~0.1.0", 38 | "karma-html2js-preprocessor": "~0.1.0", 39 | "karma-jasmine": "~0.3.0", 40 | "karma-opera-launcher": "^0.3.0", 41 | "karma-phantomjs-launcher": "^0.2.0", 42 | "karma-safari-launcher": "~0.1.1", 43 | "karma-script-launcher": "~0.1.0", 44 | "phantomjs": "^1.9.17" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/angular-google-plus.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * googleplus module 4 | */ 5 | angular.module('googleplus', []). 6 | 7 | /** 8 | * GooglePlus provider 9 | */ 10 | provider('GooglePlus', [function() { 11 | 12 | /** 13 | * Options object available for module 14 | * options/services definition. 15 | * @type {Object} 16 | */ 17 | var options = {}; 18 | 19 | /** 20 | * clientId 21 | * @type {Number} 22 | */ 23 | options.clientId = null; 24 | 25 | this.setClientId = function(clientId) { 26 | options.clientId = clientId; 27 | return this; 28 | }; 29 | 30 | this.getClientId = function() { 31 | return options.clientId; 32 | }; 33 | 34 | /** 35 | * apiKey 36 | * @type {String} 37 | */ 38 | options.apiKey = null; 39 | 40 | this.setApiKey = function(apiKey) { 41 | options.apiKey = apiKey; 42 | return this; 43 | }; 44 | 45 | this.getApiKey = function() { 46 | return options.apiKey; 47 | }; 48 | 49 | /** 50 | * Scopes 51 | * @default 'https://www.googleapis.com/auth/plus.login' 52 | * @type {Boolean} 53 | */ 54 | options.scopes = 'https://www.googleapis.com/auth/plus.login'; 55 | 56 | this.setScopes = function(scopes) { 57 | options.scopes = scopes; 58 | return this; 59 | }; 60 | 61 | this.getScopes = function() { 62 | return options.scopes; 63 | }; 64 | 65 | /** 66 | * Init Google Plus API 67 | */ 68 | this.init = function(customOptions) { 69 | angular.extend(options, customOptions); 70 | }; 71 | 72 | /** 73 | * Make sign-in server side 74 | */ 75 | this.enableServerSide = function () { 76 | options.accessType = 'offline'; 77 | options.responseType = 'code token id_token gsession'; 78 | }; 79 | 80 | /** 81 | * Make sign-in client side (default) 82 | */ 83 | this.disableServerSide = function () { 84 | delete options.accessType; 85 | delete options.responseType; 86 | }; 87 | 88 | /** 89 | * This defines the Google Plus Service on run. 90 | */ 91 | this.$get = ['$q', '$rootScope', '$timeout', function($q, $rootScope, $timeout) { 92 | 93 | /** 94 | * Define a deferred instance that will implement asynchronous calls 95 | * @type {Object} 96 | */ 97 | var deferred; 98 | 99 | /** 100 | * NgGooglePlus Class 101 | * @type {Class} 102 | */ 103 | var NgGooglePlus = function () {}; 104 | 105 | NgGooglePlus.prototype.login = function () { 106 | deferred = $q.defer(); 107 | 108 | var authOptions = { 109 | client_id: options.clientId, 110 | scope: options.scopes, 111 | immediate: false 112 | }; 113 | 114 | if(options.accessType && options.responseType) { 115 | authOptions.access_type = options.accessType; 116 | authOptions.response_type = options.responseType; 117 | } 118 | 119 | gapi.auth.authorize(authOptions, this.handleAuthResult); 120 | 121 | return deferred.promise; 122 | }; 123 | 124 | NgGooglePlus.prototype.checkAuth = function() { 125 | gapi.auth.authorize({ 126 | client_id: options.clientId, 127 | scope: options.scopes, 128 | immediate: true 129 | }, this.handleAuthResult); 130 | }; 131 | 132 | NgGooglePlus.prototype.handleClientLoad = function () { 133 | gapi.client.setApiKey(options.apiKey); 134 | gapi.auth.init(function () { }); 135 | $timeout(this.checkAuth, 1); 136 | }; 137 | 138 | NgGooglePlus.prototype.handleAuthResult = function(authResult) { 139 | if (authResult && !authResult.error) { 140 | deferred.resolve(authResult); 141 | $rootScope.$apply(); 142 | } else { 143 | deferred.reject('error'); 144 | } 145 | }; 146 | 147 | NgGooglePlus.prototype.getUser = function() { 148 | var deferred = $q.defer(); 149 | 150 | gapi.client.load('oauth2', 'v2', function () { 151 | gapi.client.oauth2.userinfo.get().execute(function (resp) { 152 | deferred.resolve(resp); 153 | $rootScope.$apply(); 154 | }); 155 | }); 156 | 157 | return deferred.promise; 158 | }; 159 | 160 | NgGooglePlus.prototype.getToken = function() { 161 | return gapi.auth.getToken(); 162 | }; 163 | 164 | NgGooglePlus.prototype.setToken = function(token) { 165 | return gapi.auth.setToken(token); 166 | }; 167 | 168 | NgGooglePlus.prototype.logout = function () { 169 | gapi.auth.signOut(); 170 | }; 171 | 172 | return new NgGooglePlus(); 173 | }]; 174 | }]) 175 | 176 | // Initialization of module 177 | .run([function() { 178 | var po = document.createElement('script'); 179 | po.type = 'text/javascript'; 180 | po.async = true; 181 | po.src = 'https://apis.google.com/js/client.js'; 182 | var s = document.getElementsByTagName('script')[0]; 183 | s.parentNode.insertBefore(po, s); 184 | }]); 185 | -------------------------------------------------------------------------------- /test/dom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DOM Test 5 | 6 | 7 | 29 | 30 | 31 | Login with Google Plus 32 | 33 | -------------------------------------------------------------------------------- /test/unit/angular-google-plus.spec.js: -------------------------------------------------------------------------------- 1 | describe('googlePlus Module specs', function () { 2 | 3 | // mock global gapi object 4 | window.gapi = { 5 | auth: { 6 | authorize: jasmine.createSpy(), 7 | signOut: jasmine.createSpy() 8 | } 9 | }; 10 | 11 | var googlePlus, GooglePlusProvider; 12 | 13 | beforeEach(function () { 14 | module('googleplus', function (_GooglePlusProvider_) { 15 | GooglePlusProvider = _GooglePlusProvider_; 16 | GooglePlusProvider.init({ 17 | apiKey: 'daowpdmpomwa21o3no1in' 18 | }); 19 | }); 20 | 21 | inject(function (_GooglePlus_) { 22 | googlePlus = _GooglePlus_; 23 | }); 24 | }); 25 | 26 | it('should exist', function () { 27 | expect(!!GooglePlusProvider).toBeDefined(); 28 | }); 29 | 30 | describe('the provider api should provide', function () { 31 | 32 | it("a working login", inject(function () { 33 | 34 | GooglePlusProvider.enableServerSide(); 35 | GooglePlusProvider.disableServerSide(); 36 | 37 | expect(googlePlus.login().then).toEqual(jasmine.any(Function)); 38 | 39 | expect(window.gapi.auth.authorize).toHaveBeenCalledWith({ 40 | client_id: GooglePlusProvider.getClientId(), 41 | scope: GooglePlusProvider.getScopes(), 42 | immediate: false, 43 | }, googlePlus.handleAuthResult); 44 | })); 45 | 46 | it("a working login with server-side enabled", inject(function () { 47 | 48 | GooglePlusProvider.enableServerSide(); 49 | 50 | expect(googlePlus.login().then).toEqual(jasmine.any(Function)); 51 | 52 | expect(window.gapi.auth.authorize).toHaveBeenCalledWith({ 53 | client_id: GooglePlusProvider.getClientId(), 54 | scope: GooglePlusProvider.getScopes(), 55 | immediate: false, 56 | access_type: 'offline', 57 | response_type: 'code token id_token gsession' 58 | }, googlePlus.handleAuthResult); 59 | })); 60 | 61 | it("a working logout", inject(function () { 62 | expect(googlePlus.logout()); 63 | expect(window.gapi.auth.signOut).toHaveBeenCalled(); 64 | })); 65 | 66 | it('appId as default value', function () { 67 | expect(GooglePlusProvider.getClientId()).toBe(null); 68 | }); 69 | 70 | it('working getter / setter for appId', function () { 71 | GooglePlusProvider.setClientId('123456789101112'); 72 | expect(GooglePlusProvider.getClientId()).toBe('123456789101112'); 73 | }); 74 | 75 | it('locale as default value', function () { 76 | expect(GooglePlusProvider.getApiKey()).toBe('daowpdmpomwa21o3no1in'); 77 | }); 78 | 79 | it('working getter / setter for locale', function () { 80 | GooglePlusProvider.setApiKey('g4ilu32b42iub34piu32b4liu23b4i23'); 81 | expect(GooglePlusProvider.getApiKey()).toBe('g4ilu32b42iub34piu32b4liu23b4i23'); 82 | }); 83 | 84 | it('status as default value', function () { 85 | expect(GooglePlusProvider.getScopes()).toBe('https://www.googleapis.com/auth/plus.login'); 86 | }); 87 | 88 | it('working getter / setter for status', function () { 89 | GooglePlusProvider.setScopes('https://www.googleapis.com/auth/plus.me'); 90 | expect(GooglePlusProvider.getScopes()).toBe('https://www.googleapis.com/auth/plus.me'); 91 | }); 92 | }); 93 | }); --------------------------------------------------------------------------------