├── .project ├── README.md ├── bower.json ├── dist └── ng-facebook-api.js └── example ├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── app ├── .buildignore ├── .htaccess ├── 404.html ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ └── yeoman.png ├── index.html ├── robots.txt ├── scripts │ ├── app.js │ ├── controllers │ │ └── main.js │ └── services │ │ └── ng-facebook-api.js ├── styles │ └── main.css └── views │ └── main.html ├── bower.json ├── karma-e2e.conf.js ├── karma.conf.js ├── package.json └── test ├── .jshintrc ├── runner.html └── spec └── controllers └── main.js /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ng-facebook-api 4 | 5 | 6 | 7 | 8 | 9 | tern.eclipse.ide.core.ternBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.angularjs.core.angularnature 16 | tern.eclipse.ide.core.ternnature 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Welcome to the ng-facebook-api 2 | =============================== 3 | 4 | Facebook API wrapper for AngularJS, you don't have to worry about the configuration and working principles of Facebook JS SDK, **you have only to code using our API**. 5 | 6 | **Ready and easy to use.** 7 | 8 | [See the official site](http://jberta93.github.io/ng-facebook-api/) 9 | 10 | [Demo](http://jberta93.github.io/ng-facebook-api/demo) 11 | 12 | 13 | The module is now under developing, in the next weeks the module will be completed and we're going to put online the official docs. 14 | 15 | ##Configuration 16 | 17 | 1. Download the library [clicking here](https://github.com/jberta93/ng-facebook-api/archive/master.zip) or use `bower install ng-facebook-api` 18 | 2. Include [Facebook JS SDK](https://developers.facebook.com/docs/javascript/quickstart/v2.1), only the SDK not the initialization `window.fbAsyncInit` and `FB.init` 19 | 3. Modify your application inject the `ng-facebook-api` 20 | 4. In your application config, you **must** setup the facebookProvider with your Facebook App settings. The settings are used in [FB.init](https://developers.facebook.com/docs/javascript/reference/FB.init/) 21 | 5. If your app uses extra permissions that require user approval, set in your config. You can set permissions also in the controller, obviously before using login. 22 | 23 | Here an example of configuration: 24 | ```javascript 25 | angular 26 | .module('',['ng-facebook-api']) 27 | .config(function( facebookProvider) { 28 | /** 29 | * Here the list of params used to configure the provider 30 | * @param appId 31 | * @param status 32 | * @param xfbml 33 | * @param cookie 34 | * @param api-version 35 | */ 36 | facebookProvider.setInitParams('your-fb-app-id',true,true,true,'v2.1'); 37 | //if your app use extra permissions set it 38 | facebookProvider.setPermissions(['email','read_stream']); 39 | }); 40 | ``` 41 | 42 | 43 | ~~**Important:** You must include the JS SDK in your `` or in method `.run(function(){})` of angular app.~~ 44 | **Not necessary from version 0.1.2** 45 | 46 | ##Usage 47 | 48 | The provider permits to have an easy access to the most used api (like user info retriving, avatar, etc..) but it also permits, thanks to `api` method, to used all methods of [Graph API](https://developers.facebook.com/docs/graph-api/reference/). 49 | 50 | Every methods check the login status and if user isn't logged, it launches the login procedure. Anyway the provider has public methods `login` and `checkLoginStatus` to implement your custom flows. 51 | 52 | ### Check SDK Loading 53 | 54 | In your controller listen on the `$rootScope` the event `fb.init` 55 | 56 | ```javascript 57 | module.controller('MainCtrl', function ($rootScope,$scope, facebook) { 58 | $rootScope.$on("fb.init",function(){ 59 | console.log("SDK Ready"); 60 | }); 61 | }); 62 | ``` 63 | 64 | ### Run the method 65 | 66 | Run the method that you need. For instance: `getUser` to obtain available user information. 67 | 68 | ```javascript 69 | module.controller('MainCtrl', function ($rootScope,$scope, facebook) { 70 | $rootScope.$on("fb.init",function(){ 71 | console.log("SDK Ready"); 72 | facebook.getUser().then(function(r){ 73 | console.log(r.user); //User data returned; 74 | console.log(r.authResponse); //Token auth, id etc.. 75 | }, function(err){ 76 | console.log("Ops, something went wrong..."); 77 | }); 78 | 79 | }); 80 | }); 81 | ``` 82 | 83 | ###Documentation 84 | 85 | The official docs wiki is [here](https://github.com/jberta93/ng-facebook-api/wiki/Home) 86 | 87 | #### Enum 88 | * [API_METHOD](https://github.com/jberta93/ng-facebook-api/wiki/API_METHOD) | The HTTP method that you can use to call the api. 89 | * [PICTURE_TYPE](https://github.com/jberta93/ng-facebook-api/wiki/PICTURE_TYPE) | When you get user profile image use this to indicate a pre-specified size of picture. 90 | 91 | #### Method 92 | 93 | * [api(path, method, params)](https://github.com/jberta93/ng-facebook-api/wiki/api) | It permits to call the Graph API 94 | * [checkLoginStatus()](https://github.com/jberta93/ng-facebook-api/wiki/checkLoginStatus) | It checks if user isn't logged and launch login procedure. 95 | * [getUser(id, fields)](https://github.com/jberta93/ng-facebook-api/wiki/getUser) | It retrives available user (depends on requested permissions) information. 96 | * [getUserPicture(id,fields)](https://github.com/jberta93/ng-facebook-api/wiki/getUserPicture) | It retrives user's profile picture. 97 | * [login()](https://github.com/jberta93/ng-facebook-api/wiki/login) 98 | * [logout()](https://github.com/jberta93/ng-facebook-api/wiki/logout) 99 | * [setPermissions(permissions)](https://github.com/jberta93/ng-facebook-api/wiki/setPermissions) | It set the permissions that the Facebook App need to do it work. 100 | * [setAutomaticLoginFlow(bool)](https://github.com/jberta93/ng-facebook-api/wiki/setAutomaticLoginFlow) | It permits to choose if use the automatic login flow (every time is needed) or implements custom login flow. 101 | 102 | 103 | ##Versions & Changelog 104 | 105 | ### 0.1.2 106 | Bug Fixing: 107 | * In checkLoginStatus you can now choose if force login or not 108 | 109 | * Added integration with async FB SDK loading (you don't have to load manually the sdk) 110 | 111 | ### 0.1.1 112 | Bug fixing: 113 | 114 | * [#1 - Play well with bower rules](https://github.com/jberta93/ng-facebook-api/issues/1) 115 | 116 | ### 0.1.0 117 | First stable version. 118 | 119 | ## Next Version 120 | 121 | **Version: 0.2.0** 122 | 123 | * Minor bug fixing 124 | * New easy method: 125 | * getUserFeed() -> It fetches user feed post. 126 | * uploadPhoto() -> It permits to upload a photo from form or url. 127 | * createPost() -> It permits to post on user's feed. 128 | * New configuration method: 129 | * Disable/enabled autologin 130 | 131 | License 132 | =============================== 133 | This project is released over [MIT license](http://opensource.org/licenses/MIT "MIT License") 134 | 135 | Author 136 | =============================== 137 | [Lorenzo Bertacchi](http://www.lorenzobertacchi.it/) 138 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-facebook-api", 3 | "version": "0.0.2", 4 | "homepage": "https://github.com/jberta93/ng-facebook-api", 5 | "description": "Facebook API wrapper for AngularJS", 6 | "keywords": [ 7 | "facebook" 8 | ], 9 | "authors": [ 10 | "Lorenzo Bertacchi" 11 | ], 12 | "main": "./dist/ng-facebook-api.js", 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /dist/ng-facebook-api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Lorenzo Bertacchi (www.lorenzobertacchi.it) 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var module = angular.module('ng-facebook-api', []); 8 | 9 | 10 | module.provider('facebook',function (){ 11 | var config = {}; 12 | var sdkLang = "en_US" 13 | var permissions = []; 14 | this.setInitParams = function(appId,status,xfbml,cookie,apiVersion, sdkLangParam){ 15 | config = { 16 | appId : appId, 17 | status : status, 18 | xfbml : xfbml, 19 | cookie : cookie, 20 | version : apiVersion 21 | }; 22 | sdkLang=sdkLangParam || sdkLang; 23 | /*if(typeof sdkLang !== "undefined"){ 24 | sdkLang = sdkLangParam; 25 | }*/ 26 | } 27 | 28 | 29 | this.setAppId = function(appId){ 30 | config.appId = appId; 31 | } 32 | 33 | this.setApiVersion = function(apiVersion){ 34 | config.version = apiVersion; 35 | } 36 | 37 | this.setCookie = function(cookie){ 38 | config.cookie = cookie; 39 | } 40 | 41 | 42 | 43 | var sdkInit = function($rootScope,$timeout){ 44 | (function (d, s, id) { 45 | var js, fjs = d.getElementsByTagName(s)[0]; 46 | if (d.getElementById(id)) return; 47 | js = d.createElement(s); 48 | js.id = id; 49 | js.src = "//connect.facebook.net/"+sdkLang+"/sdk.js"; 50 | fjs.parentNode.insertBefore(js, fjs); 51 | }(document, 'script', 'facebook-jssdk')); 52 | if(typeof window.FB == 'undefined'){ 53 | window.fbAsyncInit = function() { 54 | window.FB.init(config); 55 | $rootScope.$broadcast("fb.init", window.FB); 56 | }; 57 | }else{ 58 | 59 | $timeout(function(){ 60 | window.FB.init(config); 61 | $rootScope.$broadcast("fb.init", window.FB); 62 | },0) 63 | } 64 | } 65 | 66 | this.setPermissions = function(perms){ 67 | permissions = perms; 68 | } 69 | 70 | this.$get = ["FacebookService","$rootScope","$timeout",function(FacebookService, $rootScope,$timeout){ 71 | 72 | sdkInit($rootScope,$timeout); 73 | 74 | FacebookService.setPermissions(permissions); 75 | 76 | var providerFunc = { 77 | getConfig : function(){ 78 | return config; 79 | } 80 | }; 81 | angular.extend(providerFunc,FacebookService); 82 | return providerFunc; 83 | 84 | }] 85 | 86 | }); 87 | 88 | module.service('FacebookService',["$q", function ($q) { 89 | 90 | var settings = {}; 91 | var currentUserAuthResponse = null; 92 | var API_METHOD = {GET: "get", POST: "post", DELETE: "delete"} 93 | var PICTURE_TYPE = {SQUARE: "square", SMALL:"small", NORMAL: "normal", LARGE: "large"} 94 | 95 | var setPermissions = function(permissions){ 96 | settings.permissions = permissions; 97 | } 98 | 99 | 100 | var getUser = function(id, fields){ 101 | var deferred = $q.defer(); 102 | 103 | var request = "/me/"; 104 | 105 | if(angular.isDefined(id) && id != null){ 106 | request = "/"+id+"/" 107 | } 108 | 109 | if(!angular.isDefined(fields) && fields == null){ 110 | fields = {}; 111 | } 112 | api(request,API_METHOD.GET,fields).then(function(response){ 113 | deferred.resolve({user:response,authResponse: currentUserAuthResponse}); 114 | },function(err){ 115 | deferred.reject(err); 116 | }); 117 | 118 | return deferred.promise; 119 | } 120 | 121 | var getUserPicture = function(id,fields){ 122 | var deferred = $q.defer(); 123 | var userId = angular.isUndefined(id) ? "me": id; 124 | var fieldsDef = { 125 | redirect: false, 126 | height: "200", 127 | type: PICTURE_TYPE.NORMAL, 128 | width: "200" 129 | } 130 | 131 | angular.extend(fieldsDef,fields); 132 | 133 | api('/'+userId+'/picture',API_METHOD.GET,fieldsDef).then(function(response){ 134 | deferred.resolve({picture:response.data,authResponse: currentUserAuthResponse}); 135 | },function(err){ 136 | deferred.reject(err); 137 | }); 138 | 139 | return deferred.promise; 140 | } 141 | 142 | var apiWrapper = function(path,method,params){ 143 | var deferred = $q.defer(); 144 | FB.api(path,method,params, function(response){ 145 | if (!response || response.error) { 146 | deferred.reject(response); 147 | }else{ 148 | deferred.resolve(response); 149 | } 150 | }); 151 | return deferred.promise; 152 | } 153 | 154 | var api = function(path, method, params){ 155 | var deferred = $q.defer(); 156 | 157 | if(typeof(method) === "undefined"){ 158 | method: API_METHOD.GET; 159 | } 160 | 161 | if(typeof(params) === "undefined"){ 162 | params = {}; 163 | } 164 | 165 | if(currentUserAuthResponse != null){ 166 | deferred.promise = apiWrapper(path, method, params); 167 | 168 | }else{ 169 | deferred.promise = checkLoginStatus().then( 170 | function(resp){ 171 | return apiWrapper(path, method, params); 172 | }, 173 | function(err){ 174 | var r = $q.defer(); 175 | r.reject(err); 176 | return r.promise; 177 | } 178 | ); 179 | } 180 | return deferred.promise; 181 | } 182 | 183 | 184 | var checkLoginStatus = function( forcelogin ){ 185 | var deferred = $q.defer(); 186 | var forcelogin = (typeof forcelogin == "undefined" || forcelogin == null)?true:forcelogin; 187 | 188 | FB.getLoginStatus( function (response){ 189 | if (response.status === 'connected') { 190 | currentUserAuthResponse = response.authResponse; 191 | deferred.resolve(currentUserAuthResponse); 192 | } else { 193 | if(forcelogin) deferred.promise = doLogin(); 194 | else deferred.reject(null); //Better {response:null,reason:"Not logged in"} 195 | } 196 | }); 197 | 198 | return deferred.promise; 199 | } 200 | 201 | var doLogin = function(){ 202 | var deferred = $q.defer(); 203 | FB.login( 204 | function(response){ 205 | if(response.authResponse){ 206 | currentUserAuthResponse = response.authResponse; 207 | deferred.resolve(currentUserAuthResponse); 208 | }else{ 209 | deferred.reject("Not authorized!"); 210 | } 211 | }, 212 | { 213 | scope: settings.permissions 214 | } 215 | ); 216 | return deferred.promise; 217 | } 218 | 219 | var doLogout = function(){ 220 | var deferred = $q.defer(); 221 | FB.logout(function(response) { 222 | if (!response || response.error) { 223 | deferred.reject(response); 224 | }else{ 225 | deferred.resolve(response); 226 | } 227 | }); 228 | currentUserAuthResponse = null; 229 | return deferred.promise; 230 | } 231 | 232 | return { 233 | API_METHOD: API_METHOD, 234 | PICTURE_TYPE: PICTURE_TYPE, 235 | api: api, 236 | checkLoginStatus: checkLoginStatus, 237 | getUser: getUser, 238 | getUserPicture: getUserPicture, 239 | login:doLogin, 240 | logout: doLogout, 241 | setPermissions : setPermissions 242 | } 243 | }]); 244 | 245 | 246 | -------------------------------------------------------------------------------- /example/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /example/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | app/bower_components 6 | -------------------------------------------------------------------------------- /example/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "angular": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.8' 4 | - '0.10' 5 | before_script: 6 | - 'npm install -g bower grunt-cli' 7 | - 'bower install' 8 | -------------------------------------------------------------------------------- /example/Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2014-10-02 using generator-angular 0.8.0 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Load grunt tasks automatically 13 | require('load-grunt-tasks')(grunt); 14 | 15 | // Time how long tasks take. Can help when optimizing build times 16 | require('time-grunt')(grunt); 17 | 18 | // Define the configuration for all the tasks 19 | grunt.initConfig({ 20 | 21 | // Project settings 22 | yeoman: { 23 | // configurable paths 24 | app: require('./bower.json').appPath || 'app', 25 | dist: 'dist' 26 | }, 27 | 28 | // Watches files for changes and runs tasks based on the changed files 29 | watch: { 30 | bower: { 31 | files: ['bower.json'], 32 | tasks: ['bowerInstall'] 33 | }, 34 | js: { 35 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], 36 | tasks: ['newer:jshint:all'], 37 | options: { 38 | livereload: true 39 | } 40 | }, 41 | jsTest: { 42 | files: ['test/spec/{,*/}*.js'], 43 | tasks: ['newer:jshint:test', 'karma'] 44 | }, 45 | styles: { 46 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 47 | tasks: ['newer:copy:styles', 'autoprefixer'] 48 | }, 49 | gruntfile: { 50 | files: ['Gruntfile.js'] 51 | }, 52 | livereload: { 53 | options: { 54 | livereload: '<%= connect.options.livereload %>' 55 | }, 56 | files: [ 57 | '<%= yeoman.app %>/{,*/}*.html', 58 | '.tmp/styles/{,*/}*.css', 59 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 60 | ] 61 | } 62 | }, 63 | 64 | // The actual grunt server settings 65 | connect: { 66 | options: { 67 | port: 9000, 68 | // Change this to '0.0.0.0' to access the server from outside. 69 | hostname: 'localhost', 70 | livereload: 35729 71 | }, 72 | livereload: { 73 | options: { 74 | open: true, 75 | base: [ 76 | '.tmp', 77 | '<%= yeoman.app %>' 78 | ] 79 | } 80 | }, 81 | test: { 82 | options: { 83 | port: 9001, 84 | base: [ 85 | '.tmp', 86 | 'test', 87 | '<%= yeoman.app %>' 88 | ] 89 | } 90 | }, 91 | dist: { 92 | options: { 93 | base: '<%= yeoman.dist %>' 94 | } 95 | } 96 | }, 97 | 98 | // Make sure code styles are up to par and there are no obvious mistakes 99 | jshint: { 100 | options: { 101 | jshintrc: '.jshintrc', 102 | reporter: require('jshint-stylish') 103 | }, 104 | all: [ 105 | 'Gruntfile.js', 106 | '<%= yeoman.app %>/scripts/{,*/}*.js' 107 | ], 108 | test: { 109 | options: { 110 | jshintrc: 'test/.jshintrc' 111 | }, 112 | src: ['test/spec/{,*/}*.js'] 113 | } 114 | }, 115 | 116 | // Empties folders to start fresh 117 | clean: { 118 | dist: { 119 | files: [{ 120 | dot: true, 121 | src: [ 122 | '.tmp', 123 | '<%= yeoman.dist %>/*', 124 | '!<%= yeoman.dist %>/.git*' 125 | ] 126 | }] 127 | }, 128 | server: '.tmp' 129 | }, 130 | 131 | // Add vendor prefixed styles 132 | autoprefixer: { 133 | options: { 134 | browsers: ['last 1 version'] 135 | }, 136 | dist: { 137 | files: [{ 138 | expand: true, 139 | cwd: '.tmp/styles/', 140 | src: '{,*/}*.css', 141 | dest: '.tmp/styles/' 142 | }] 143 | } 144 | }, 145 | 146 | // Automatically inject Bower components into the app 147 | bowerInstall: { 148 | app: { 149 | src: ['<%= yeoman.app %>/index.html'], 150 | ignorePath: '<%= yeoman.app %>/' 151 | } 152 | }, 153 | 154 | // Renames files for browser caching purposes 155 | rev: { 156 | dist: { 157 | files: { 158 | src: [ 159 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 160 | '<%= yeoman.dist %>/styles/{,*/}*.css', 161 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 162 | '<%= yeoman.dist %>/styles/fonts/*' 163 | ] 164 | } 165 | } 166 | }, 167 | 168 | // Reads HTML for usemin blocks to enable smart builds that automatically 169 | // concat, minify and revision files. Creates configurations in memory so 170 | // additional tasks can operate on them 171 | useminPrepare: { 172 | html: '<%= yeoman.app %>/index.html', 173 | options: { 174 | dest: '<%= yeoman.dist %>', 175 | flow: { 176 | html: { 177 | steps: { 178 | js: ['concat', 'uglifyjs'], 179 | css: ['cssmin'] 180 | }, 181 | post: {} 182 | } 183 | } 184 | } 185 | }, 186 | 187 | // Performs rewrites based on rev and the useminPrepare configuration 188 | usemin: { 189 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 190 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 191 | options: { 192 | assetsDirs: ['<%= yeoman.dist %>'] 193 | } 194 | }, 195 | 196 | // The following *-min tasks produce minified files in the dist folder 197 | cssmin: { 198 | options: { 199 | root: '<%= yeoman.app %>' 200 | } 201 | }, 202 | 203 | imagemin: { 204 | dist: { 205 | files: [{ 206 | expand: true, 207 | cwd: '<%= yeoman.app %>/images', 208 | src: '{,*/}*.{png,jpg,jpeg,gif}', 209 | dest: '<%= yeoman.dist %>/images' 210 | }] 211 | } 212 | }, 213 | 214 | svgmin: { 215 | dist: { 216 | files: [{ 217 | expand: true, 218 | cwd: '<%= yeoman.app %>/images', 219 | src: '{,*/}*.svg', 220 | dest: '<%= yeoman.dist %>/images' 221 | }] 222 | } 223 | }, 224 | 225 | htmlmin: { 226 | dist: { 227 | options: { 228 | collapseWhitespace: true, 229 | collapseBooleanAttributes: true, 230 | removeCommentsFromCDATA: true, 231 | removeOptionalTags: true 232 | }, 233 | files: [{ 234 | expand: true, 235 | cwd: '<%= yeoman.dist %>', 236 | src: ['*.html', 'views/{,*/}*.html'], 237 | dest: '<%= yeoman.dist %>' 238 | }] 239 | } 240 | }, 241 | 242 | // ngmin tries to make the code safe for minification automatically by 243 | // using the Angular long form for dependency injection. It doesn't work on 244 | // things like resolve or inject so those have to be done manually. 245 | ngmin: { 246 | dist: { 247 | files: [{ 248 | expand: true, 249 | cwd: '.tmp/concat/scripts', 250 | src: '*.js', 251 | dest: '.tmp/concat/scripts' 252 | }] 253 | } 254 | }, 255 | 256 | // Replace Google CDN references 257 | cdnify: { 258 | dist: { 259 | html: ['<%= yeoman.dist %>/*.html'] 260 | } 261 | }, 262 | 263 | // Copies remaining files to places other tasks can use 264 | copy: { 265 | dist: { 266 | files: [{ 267 | expand: true, 268 | dot: true, 269 | cwd: '<%= yeoman.app %>', 270 | dest: '<%= yeoman.dist %>', 271 | src: [ 272 | '*.{ico,png,txt}', 273 | '.htaccess', 274 | '*.html', 275 | 'views/{,*/}*.html', 276 | 'images/{,*/}*.{webp}', 277 | 'fonts/*' 278 | ] 279 | }, { 280 | expand: true, 281 | cwd: '.tmp/images', 282 | dest: '<%= yeoman.dist %>/images', 283 | src: ['generated/*'] 284 | }] 285 | }, 286 | styles: { 287 | expand: true, 288 | cwd: '<%= yeoman.app %>/styles', 289 | dest: '.tmp/styles/', 290 | src: '{,*/}*.css' 291 | } 292 | }, 293 | 294 | // Run some tasks in parallel to speed up the build process 295 | concurrent: { 296 | server: [ 297 | 'copy:styles' 298 | ], 299 | test: [ 300 | 'copy:styles' 301 | ], 302 | dist: [ 303 | 'copy:styles', 304 | 'imagemin', 305 | 'svgmin' 306 | ] 307 | }, 308 | 309 | // By default, your `index.html`'s will take care of 310 | // minification. These next options are pre-configured if you do not wish 311 | // to use the Usemin blocks. 312 | // cssmin: { 313 | // dist: { 314 | // files: { 315 | // '<%= yeoman.dist %>/styles/main.css': [ 316 | // '.tmp/styles/{,*/}*.css', 317 | // '<%= yeoman.app %>/styles/{,*/}*.css' 318 | // ] 319 | // } 320 | // } 321 | // }, 322 | // uglify: { 323 | // dist: { 324 | // files: { 325 | // '<%= yeoman.dist %>/scripts/scripts.js': [ 326 | // '<%= yeoman.dist %>/scripts/scripts.js' 327 | // ] 328 | // } 329 | // } 330 | // }, 331 | // concat: { 332 | // dist: {} 333 | // }, 334 | 335 | // Test settings 336 | karma: { 337 | unit: { 338 | configFile: 'karma.conf.js', 339 | singleRun: true 340 | } 341 | } 342 | }); 343 | 344 | 345 | grunt.registerTask('serve', function (target) { 346 | if (target === 'dist') { 347 | return grunt.task.run(['build', 'connect:dist:keepalive']); 348 | } 349 | 350 | grunt.task.run([ 351 | 'clean:server', 352 | 'bowerInstall', 353 | 'concurrent:server', 354 | 'autoprefixer', 355 | 'connect:livereload', 356 | 'watch' 357 | ]); 358 | }); 359 | 360 | grunt.registerTask('server', function (target) { 361 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 362 | grunt.task.run(['serve:' + target]); 363 | }); 364 | 365 | grunt.registerTask('test', [ 366 | 'clean:server', 367 | 'concurrent:test', 368 | 'autoprefixer', 369 | 'connect:test', 370 | 'karma' 371 | ]); 372 | 373 | grunt.registerTask('build', [ 374 | 'clean:dist', 375 | 'bowerInstall', 376 | 'useminPrepare', 377 | 'concurrent:dist', 378 | 'autoprefixer', 379 | 'concat', 380 | 'ngmin', 381 | 'copy:dist', 382 | 'cdnify', 383 | 'cssmin', 384 | 'uglify', 385 | 'rev', 386 | 'usemin', 387 | 'htmlmin' 388 | ]); 389 | 390 | grunt.registerTask('default', [ 391 | 'newer:jshint', 392 | 'test', 393 | 'build' 394 | ]); 395 | }; 396 | -------------------------------------------------------------------------------- /example/app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /example/app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /example/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /example/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jberta93/ng-facebook-api/8c2fe07fed8cad1daf86e1de918370b8c62655a3/example/app/favicon.ico -------------------------------------------------------------------------------- /example/app/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jberta93/ng-facebook-api/8c2fe07fed8cad1daf86e1de918370b8c62655a3/example/app/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /example/app/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /example/app/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jberta93/ng-facebook-api/8c2fe07fed8cad1daf86e1de918370b8c62655a3/example/app/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /example/app/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jberta93/ng-facebook-api/8c2fe07fed8cad1daf86e1de918370b8c62655a3/example/app/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /example/app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jberta93/ng-facebook-api/8c2fe07fed8cad1daf86e1de918370b8c62655a3/example/app/images/yeoman.png -------------------------------------------------------------------------------- /example/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | 34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /example/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /example/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular 4 | .module('exampleApp',['ng-facebook-api']).config(function( facebookProvider) { 5 | facebookProvider.setInitParams('1529491150629819',true,true,true,'v2.1'); 6 | facebookProvider.setPermissions(['email','read_stream']); 7 | }); 8 | -------------------------------------------------------------------------------- /example/app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('exampleApp') 4 | .controller('MainCtrl', function ($rootScope,$scope, facebook) { 5 | $scope.results = []; 6 | $rootScope.$on("fb.init",function(){ 7 | console.log("SDK Ready"); 8 | }); 9 | 10 | $scope.getPublicProfile = function(){ 11 | facebook.getUser().then(function(r){ 12 | $scope.results = r.user; 13 | }); 14 | } 15 | $scope.img = ""; 16 | 17 | $scope.getProfilePicture = function(){ 18 | facebook.getUserPicture("me",{width:300, height:300}).then(function(r){ 19 | $scope.img = r.picture.url; 20 | }); 21 | } 22 | 23 | 24 | $scope.form = {method:'GET',path: "/me/feed"}; 25 | $scope.api = function(){ 26 | facebook.api($scope.form.path, facebook.API_METHOD[$scope.form.method]).then(function(resp){ 27 | console.log("Success!"); 28 | console.log(resp); 29 | }) 30 | } 31 | 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /example/app/scripts/services/ng-facebook-api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Lorenzo Bertacchi (www.lorenzobertacchi.it) 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var module = angular.module('ng-facebook-api', []); 8 | 9 | 10 | module.provider('facebook',function (){ 11 | var config = {}; 12 | var permissions = []; 13 | this.setInitParams = function(appId,status,xfbml,cookie,apiVersion){ 14 | config = { 15 | appId : appId, 16 | status : status, 17 | xfbml : xfbml, 18 | cookie : cookie, 19 | version : apiVersion 20 | } 21 | } 22 | 23 | 24 | this.setAppId = function(appId){ 25 | config.appId = appId; 26 | } 27 | 28 | this.setApiVersion = function(apiVersion){ 29 | config.version = apiVersion; 30 | } 31 | 32 | this.setCookie = function(cookie){ 33 | config.cookie = cookie; 34 | } 35 | 36 | var sdkInit = function($rootScope){ 37 | if(typeof window.FB == 'undefined'){ 38 | window.fbAsyncInit = function() { 39 | FB.init(config); 40 | $rootScope.$broadcast("fb.init", FB); 41 | }; 42 | }else{ 43 | FB.init(config); 44 | $rootScope.$broadcast("fb.init", FB); 45 | } 46 | } 47 | 48 | this.setPermissions = function(perms){ 49 | permissions = perms; 50 | } 51 | 52 | this.$get = function(FacebookService, $rootScope){ 53 | 54 | sdkInit($rootScope); 55 | 56 | FacebookService.setPermissions(permissions); 57 | 58 | var providerFunc = { 59 | getConfig : function(){ 60 | return config; 61 | } 62 | }; 63 | angular.extend(providerFunc,FacebookService); 64 | return providerFunc; 65 | 66 | } 67 | 68 | }); 69 | 70 | module.service('FacebookService', function FacebookService($q) { 71 | 72 | var settings = {}; 73 | var currentUserAuthResponse = null; 74 | var API_METHOD = {GET: "get", POST: "post", DELETE: "delete"} 75 | var PICTURE_TYPE = {SQUARE: "square", SMALL:"small", NORMAL: "normal", LARGE: "large"} 76 | 77 | var setPermissions = function(permissions){ 78 | settings.permissions = permissions; 79 | } 80 | 81 | 82 | var getUser = function(id, fields){ 83 | var deferred = $q.defer(); 84 | 85 | var request = "/me/"; 86 | 87 | if(angular.isDefined(id) && id != null){ 88 | request = "/"+id+"/" 89 | } 90 | 91 | if(!angular.isDefined(fields) && fields == null){ 92 | fields = {}; 93 | } 94 | api(request,API_METHOD.GET,fields).then(function(response){ 95 | deferred.resolve({user:response,authResponse: currentUserAuthResponse}); 96 | },function(err){ 97 | deferred.reject(err); 98 | }); 99 | 100 | return deferred.promise; 101 | } 102 | 103 | var getUserPicture = function(id,fields){ 104 | var deferred = $q.defer(); 105 | var userId = angular.isUndefined(id) ? "me": id; 106 | var fieldsDef = { 107 | redirect: false, 108 | height: "200", 109 | type: PICTURE_TYPE.NORMAL, 110 | width: "200" 111 | } 112 | 113 | angular.extend(fieldsDef,fields); 114 | 115 | api('/'+userId+'/picture',API_METHOD.GET,fieldsDef).then(function(response){ 116 | deferred.resolve({picture:response.data,authResponse: currentUserAuthResponse}); 117 | },function(err){ 118 | deferred.reject(err); 119 | }); 120 | 121 | return deferred.promise; 122 | } 123 | 124 | var apiWrapper = function(path,method,params){ 125 | var deferred = $q.defer(); 126 | FB.api(path,method,params, function(response){ 127 | if (!response || response.error) { 128 | deferred.reject(response); 129 | }else{ 130 | deferred.resolve(response); 131 | } 132 | }); 133 | return deferred.promise; 134 | } 135 | 136 | var api = function(path, method, params){ 137 | var deferred = $q.defer(); 138 | 139 | if(typeof(method) === "undefined"){ 140 | method: API_METHOD.GET; 141 | } 142 | 143 | if(typeof(params) === "undefined"){ 144 | params = {}; 145 | } 146 | 147 | if(currentUserAuthResponse != null){ 148 | deferred.promise = apiWrapper(path, method, params); 149 | 150 | }else{ 151 | deferred.promise = checkLoginStatus().then( 152 | function(resp){ 153 | return apiWrapper(path, method, params); 154 | }, 155 | function(err){ 156 | var r = $q.defer(); 157 | r.reject(err); 158 | return r.promise; 159 | } 160 | ); 161 | } 162 | return deferred.promise; 163 | } 164 | 165 | 166 | var checkLoginStatus = function(){ 167 | var deferred = $q.defer(); 168 | 169 | FB.getLoginStatus( function (response){ 170 | if (response.status === 'connected') { 171 | currentUserAuthResponse = response.authResponse; 172 | deferred.resolve(currentUserAuthResponse); 173 | } else { 174 | deferred.promise = doLogin(); 175 | } 176 | }); 177 | 178 | return deferred.promise; 179 | } 180 | 181 | var doLogin = function(){ 182 | var deferred = $q.defer(); 183 | FB.login( 184 | function(response){ 185 | if(response.authResponse){ 186 | currentUserAuthResponse = response.authResponse; 187 | deferred.resolve(currentUserAuthResponse); 188 | }else{ 189 | deferred.reject("Not authorized!"); 190 | } 191 | }, 192 | { 193 | scope: settings.permissions 194 | } 195 | ); 196 | return deferred.promise; 197 | } 198 | 199 | var doLogout = function(){ 200 | var deferred = $q.defer(); 201 | FB.logout(function(response) { 202 | if (!response || response.error) { 203 | deferred.reject(response); 204 | }else{ 205 | deferred.resolve(response); 206 | } 207 | }); 208 | currentUserAuthResponse = null; 209 | return deferred.promise; 210 | } 211 | 212 | return { 213 | API_METHOD: API_METHOD, 214 | PICTURE_TYPE: PICTURE_TYPE, 215 | api: api, 216 | checkLoginStatus: checkLoginStatus, 217 | getUser: getUser, 218 | getUserPicture: getUserPicture, 219 | login:doLogin, 220 | logout: doLogout, 221 | setPermissions : setPermissions 222 | } 223 | }); 224 | 225 | 226 | -------------------------------------------------------------------------------- /example/app/styles/main.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | body { 3 | padding-top: 20px; 4 | padding-bottom: 20px; 5 | } 6 | 7 | /* Everything but the jumbotron gets side spacing for mobile first views */ 8 | .header, 9 | .marketing, 10 | .footer { 11 | padding-left: 15px; 12 | padding-right: 15px; 13 | } 14 | 15 | /* Custom page header */ 16 | .header { 17 | border-bottom: 1px solid #e5e5e5; 18 | } 19 | /* Make the masthead heading the same height as the navigation */ 20 | .header h3 { 21 | margin-top: 0; 22 | margin-bottom: 0; 23 | line-height: 40px; 24 | padding-bottom: 19px; 25 | } 26 | 27 | /* Custom page footer */ 28 | .footer { 29 | padding-top: 19px; 30 | color: #777; 31 | border-top: 1px solid #e5e5e5; 32 | } 33 | 34 | /* Customize container */ 35 | @media (min-width: 768px) { 36 | .container { 37 | max-width: 730px; 38 | } 39 | } 40 | .container-narrow > hr { 41 | margin: 30px 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: 1px solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | font-size: 21px; 51 | padding: 14px 24px; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 40px 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 28px; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 768px) { 64 | /* Remove the padding we set earlier */ 65 | .header, 66 | .marketing, 67 | .footer { 68 | padding-left: 0; 69 | padding-right: 0; 70 | } 71 | /* Space out the masthead */ 72 | .header { 73 | margin-bottom: 30px; 74 | } 75 | /* Remove the bottom border on the jumbotron for visual effect */ 76 | .jumbotron { 77 | border-bottom: 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /example/app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |

ng-facebook-api

3 |
4 | 5 |
6 |

ng-facebook-api

7 |

Facebook API wrapper for AngularJS!
Ready and easy to use!

8 |

9 | 10 | No developer has been mistreated during development. 11 |

12 |
13 |
14 | See your public profile 15 |
16 |

Public Profile

17 |

{{r}}

18 |
19 |
20 | See your profile's picture 21 |

Profile Image

22 | 23 | 24 |
25 | 26 |

Console API

27 |

Open the developer console to see the result

28 |
29 |
30 | 31 | 36 |

37 |

38 | Test it! 39 |
40 | 41 |
42 |
43 | 46 | -------------------------------------------------------------------------------- /example/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "1.2.15", 6 | "json3": "~3.2.6", 7 | "es5-shim": "~2.1.0", 8 | "jquery": "~1.11.0", 9 | "bootstrap": "~3.0.3" 10 | }, 11 | "devDependencies": { 12 | "angular-mocks": "1.2.15", 13 | "angular-scenario": "1.2.15" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/karma-e2e.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['ng-scenario'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'test/e2e/**/*.js' 15 | ], 16 | 17 | // list of files / patterns to exclude 18 | exclude: [], 19 | 20 | // web server port 21 | port: 8080, 22 | 23 | // level of logging 24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 25 | logLevel: config.LOG_INFO, 26 | 27 | 28 | // enable / disable watching file and executing tests whenever any file changes 29 | autoWatch: false, 30 | 31 | 32 | // Start these browsers, currently available: 33 | // - Chrome 34 | // - ChromeCanary 35 | // - Firefox 36 | // - Opera 37 | // - Safari (only Mac) 38 | // - PhantomJS 39 | // - IE (only Windows) 40 | browsers: ['Chrome'], 41 | 42 | 43 | // Continuous Integration mode 44 | // if true, it capture browsers, run tests and exit 45 | singleRun: false 46 | 47 | // Uncomment the following lines if you are using grunt's server to run the tests 48 | // proxies: { 49 | // '/': 'http://localhost:9000/' 50 | // }, 51 | // URL root prevent conflicts with the site root 52 | // urlRoot: '_karma_' 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /example/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['jasmine'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'app/bower_components/angular/angular.js', 15 | 'app/bower_components/angular-mocks/angular-mocks.js', 16 | 'app/scripts/*.js', 17 | 'app/scripts/**/*.js', 18 | 'test/mock/**/*.js', 19 | 'test/spec/**/*.js' 20 | ], 21 | 22 | // list of files / patterns to exclude 23 | exclude: [], 24 | 25 | // web server port 26 | port: 8080, 27 | 28 | // level of logging 29 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 30 | logLevel: config.LOG_INFO, 31 | 32 | 33 | // enable / disable watching file and executing tests whenever any file changes 34 | autoWatch: false, 35 | 36 | 37 | // Start these browsers, currently available: 38 | // - Chrome 39 | // - ChromeCanary 40 | // - Firefox 41 | // - Opera 42 | // - Safari (only Mac) 43 | // - PhantomJS 44 | // - IE (only Windows) 45 | browsers: ['Chrome'], 46 | 47 | 48 | // Continuous Integration mode 49 | // if true, it capture browsers, run tests and exit 50 | singleRun: false 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "grunt": "~0.4.1", 7 | "grunt-autoprefixer": "~0.4.0", 8 | "grunt-bower-install": "~1.0.0", 9 | "grunt-concurrent": "~0.5.0", 10 | "grunt-contrib-clean": "~0.5.0", 11 | "grunt-contrib-concat": "~0.3.0", 12 | "grunt-contrib-connect": "~0.5.0", 13 | "grunt-contrib-copy": "~0.4.1", 14 | "grunt-contrib-cssmin": "~0.7.0", 15 | "grunt-contrib-htmlmin": "~0.1.3", 16 | "grunt-contrib-imagemin": "~0.3.0", 17 | "grunt-contrib-jshint": "~0.7.1", 18 | "grunt-contrib-uglify": "~0.2.0", 19 | "grunt-contrib-watch": "~0.5.2", 20 | "grunt-google-cdn": "~0.2.0", 21 | "grunt-newer": "~0.6.1", 22 | "grunt-ngmin": "~0.0.2", 23 | "grunt-rev": "~0.1.0", 24 | "grunt-svgmin": "~0.2.0", 25 | "grunt-usemin": "~2.0.0", 26 | "jshint-stylish": "~0.1.3", 27 | "load-grunt-tasks": "~0.4.0", 28 | "time-grunt": "~0.2.1", 29 | "karma-ng-scenario": "^0.1.0", 30 | "grunt-karma": "^0.9.0", 31 | "karma": "^0.12.24", 32 | "karma-ng-html2js-preprocessor": "^0.1.0" 33 | }, 34 | "engines": { 35 | "node": ">=0.10.0" 36 | }, 37 | "scripts": { 38 | "test": "grunt test" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "after": false, 23 | "afterEach": false, 24 | "angular": false, 25 | "before": false, 26 | "beforeEach": false, 27 | "browser": false, 28 | "describe": false, 29 | "expect": false, 30 | "inject": false, 31 | "it": false, 32 | "jasmine": false, 33 | "spyOn": false 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /example/test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | End2end Test Runner 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('exampleApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | --------------------------------------------------------------------------------