├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── build ├── angular-easyfb.js └── angular-easyfb.min.js ├── package.json ├── src └── angular-easyfb.js └── test ├── helper.js ├── karma-unit.conf.js ├── pubsub.js └── unit ├── ezfb.spec.js ├── pubsub.spec.js └── social-plugin-directive.spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | *.sublime-* 4 | .tern-port 5 | .DS_Store 6 | bower_components 7 | coverage 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.12' 5 | before_script: 6 | - 'npm install -g grunt-cli' 7 | - 'npm install -g bower' 8 | - 'bower install' 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.6.0 2 | 3 | - Support jQuery 3 through migrating deprecated `.bind` to `.on` ([#84](https://github.com/pc035860/angular-easyfb/pull/84)) 4 | 5 | ## v1.5.2 6 | 7 | * Support [Login Button](https://developers.facebook.com/docs/facebook-login/web/login-button) ([#78](https://github.com/pc035860/angular-easyfb/issues/78)) 8 | * Support [Save Button](https://developers.facebook.com/docs/plugins/save) and [Embedded Comments](https://developers.facebook.com/docs/plugins/embedded-comments) 9 | 10 | ## v1.5.1 11 | 12 | * Bump bower version 13 | 14 | ## v1.5.0 15 | 16 | * Add [Send-to-Messenger Plugin](https://developers.facebook.com/docs/messenger-platform/plugin-reference#send_to_messenger) and [Message-Us Plugin](https://developers.facebook.com/docs/messenger-platform/plugin-reference#message_us) 17 | * Change default FB JS SDK platform version to `v2.6` 18 | 19 | ## v1.4.4 20 | 21 | * Fix intialization error when `xfbml: false` parameter is given ([#66](https://github.com/pc035860/angular-easyfb/issues/66)) 22 | 23 | ## v1.4.3 24 | 25 | * Fix the "childNodes error" caused by [Google Analytics Opt-out Add-on](https://chrome.google.com/webstore/detail/google-analytics-opt-out/fllaojicojecljbmefodhfapmkghcbnh) ([Ben Nadel's article](http://www.bennadel.com/blog/2892-typeerror-cannot-read-property-childnodes-of-undefined-in-angularjs.htm)) ([#65](https://github.com/pc035860/angular-easyfb/issues/65)) 26 | 27 | 28 | ## v1.4.2 29 | 30 | * Update `main` configuration of `package.json` ([#63](https://github.com/pc035860/angular-easyfb/issues/63)) 31 | 32 | 33 | ## v1.4.1 34 | 35 | * Update `main` configuration of `bower.json` ([#56](https://github.com/pc035860/angular-easyfb/issues/56)) 36 | 37 | ## v1.4.0 38 | 39 | * Add [Ad Preview Plugin](https://developers.facebook.com/docs/marketing-api/ad-preview-plugin/v2.4) support 40 | * In order to support _Ad Preview Plugin_, default platform version is now `2.4` 41 | 42 | ## v1.3.2 43 | 44 | * Support `100%` literal of `width` setting of [Comments](https://developers.facebook.com/docs/plugins/comments). ([#53](https://github.com/pc035860/angular-easyfb/issues/53)) 45 | 46 | ## v1.3.1 47 | 48 | * Refine social plugin directives parsing process for plugins with adaptive width (`fb-page` for now) ([#45](https://github.com/pc035860/angular-easyfb/pull/45)) 49 | * Add [Embedded Video Player](https://developers.facebook.com/docs/plugins/embedded-video-player/) support 50 | 51 | ## v1.3.0 52 | 53 | * Change default `FB.init` version from `v1.0` to `v2.0` 54 | * Add [Page plugin](https://developers.facebook.com/docs/plugins/page-plugin/) support ([#45](https://github.com/pc035860/angular-easyfb/pull/45)) 55 | 56 | ## v1.2.1 57 | 58 | * Add API support: `Canvas.getPageInfo` ([#40](https://github.com/pc035860/angular-easyfb/pull/40)) 59 | 60 | ## v1.2.0 61 | 62 | * Add [App Events for Canvas](https://developers.facebook.com/docs/canvas/appevents) support. ([#39](https://github.com/pc035860/angular-easyfb/issues/39)) 63 | 64 | ## v1.1.0 65 | 66 | * Upgrade Facebook JS SDK loading function to support [Facebook Platform versioning](https://developers.facebook.com/docs/apps/changelog/) 67 | * Default version is `v1.0` 68 | 69 | ## v1.0.1 70 | 71 | * Add [fb:comments-count](https://developers.facebook.com/docs/plugins/comments/#faqcount) directive 72 | 73 | ## v1.0.0 74 | 75 | * Rename service `$FB` to `ezfb` 76 | * Local DIs get renamed. 77 | * `$fbInitParams` -> `ezfbInitParams` 78 | * `$fbAsyncInit` -> `ezfbAsyncInit` 79 | * `$fbLocale` -> `ezfbLocale` 80 | 81 | ## v0.3.1 82 | 83 | * Social plugin directivs now support interpolated attributes 84 | 85 | ## v0.3.0 86 | 87 | * `setLoadSDKFunction()` in configuration phase for sdk loading customization 88 | * Support [Facebook Social Plugins](https://developers.facebook.com/docs/plugins) with built-in directives 89 | * `ezfb-xfbml` directive is now deprecated 90 | * Add unit tests for `$FB` and all directives 91 | 92 | ## v0.2.3 93 | 94 | * Implement `$FB.getAuthResponse()` which maps to [`FB.getAuthResponse()`](https://developers.facebook.com/docs/reference/javascript/FB.getAuthResponse/) 95 | 96 | ## v0.2.2 97 | 98 | * `setInitFunction()` in configuration phase for initialization customization 99 | * Make `$FB.Event.unsubscribe` unsubscribes events properly 100 | 101 | ## v0.2.1 102 | 103 | * `setLocale()` in configuration phase 104 | * Configure `FB.init` parameters with `$FB.init` in run block 105 | 106 | ## v0.2.0 107 | 108 | * AngularJS $q promise support 109 | * Fix minified code run-time error 110 | 111 | ## v0.1.0 112 | 113 | First release! 114 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module*/ 2 | module.exports = function(grunt) { 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | jshint: { 6 | options: { 7 | curly: true, 8 | jshintrc: '.jshintrc', 9 | reporterOutput: '' 10 | }, 11 | beforeuglify: ['src/<%= pkg.name %>.js'], 12 | gruntfile: ['Gruntfile.js'] 13 | }, 14 | uglify: { 15 | src: { 16 | src: 'src/<%= pkg.name %>.js', 17 | dest: 'build/<%= pkg.name %>.js', 18 | options: { 19 | mangle: false, 20 | compress: false, 21 | beautify: true, 22 | banner: 23 | '/*! <%= pkg.name %>\n' + 24 | 'version: <%= pkg.version %>\n' + 25 | 'build date: <%= grunt.template.today("yyyy-mm-dd") %>\n' + 26 | 'author: <%= pkg.author %>\n' + 27 | '<%= pkg.repository.url %> */\n' 28 | } 29 | }, 30 | build: { 31 | src: 'src/<%= pkg.name %>.js', 32 | dest: 'build/<%= pkg.name %>.min.js', 33 | options: { 34 | mangle: true, 35 | compress: {}, 36 | banner: 37 | '/*! <%= pkg.name %>\n' + 38 | 'version: <%= pkg.version %>\n' + 39 | 'build date: <%= grunt.template.today("yyyy-mm-dd") %>\n' + 40 | 'author: <%= pkg.author %>\n' + 41 | '<%= pkg.repository.url %> */\n' 42 | } 43 | } 44 | }, 45 | watch: { 46 | gruntfile: { 47 | files: 'Gruntfile.js', 48 | tasks: ['jshint:gruntfile'] 49 | }, 50 | src: { 51 | files: '<%= pkg.name %>.js', 52 | tasks: ['default'] 53 | } 54 | }, 55 | connect: { 56 | server: { 57 | options: { 58 | port: 8080, 59 | base: '', 60 | keepalive: true 61 | } 62 | }, 63 | coverage: { 64 | options: { 65 | port: 5555, 66 | base: 'coverage/', 67 | keepalive: true 68 | } 69 | } 70 | }, 71 | karma: { 72 | unit: { 73 | configFile: './test/karma-unit.conf.js', 74 | autoWatch: false, 75 | singleRun: true 76 | }, 77 | unit_auto: { 78 | configFile: './test/karma-unit.conf.js', 79 | autoWatch: true, 80 | singleRun: false 81 | }, 82 | unit_coverage: { 83 | configFile: './test/karma-unit.conf.js', 84 | autoWatch: false, 85 | singleRun: true, 86 | reporters: ['progress', 'coverage'], 87 | preprocessors: { 88 | 'angular-easyfb.js': ['coverage'] 89 | }, 90 | coverageReporter: { 91 | type : 'html', 92 | dir : 'coverage/' 93 | } 94 | } 95 | }, 96 | open: { 97 | coverage: { 98 | path: 'http://localhost:5555' 99 | } 100 | } 101 | }); 102 | 103 | grunt.loadNpmTasks('grunt-contrib-jshint'); 104 | grunt.loadNpmTasks('grunt-contrib-uglify'); 105 | grunt.loadNpmTasks('grunt-contrib-watch'); 106 | grunt.loadNpmTasks('grunt-contrib-connect'); 107 | grunt.loadNpmTasks('grunt-karma'); 108 | grunt.loadNpmTasks('grunt-open'); 109 | 110 | // single run tests 111 | grunt.registerTask('test', ['test:unit']); 112 | grunt.registerTask('test:unit', ['karma:unit']); 113 | 114 | // autotest and watch tests 115 | grunt.registerTask('autotest', ['karma:unit_auto']); 116 | grunt.registerTask('autotest:unit', ['karma:unit_auto']); 117 | 118 | //coverage testing 119 | grunt.registerTask('test:coverage', ['karma:unit_coverage']); 120 | grunt.registerTask('coverage', ['karma:unit_coverage','open:coverage','connect:coverage']); 121 | 122 | grunt.registerTask('default', ['jshint:beforeuglify', 'test', 'uglify']); 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Robin Fan 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-easyfb [](https://travis-ci.org/pc035860/angular-easyfb) [](https://www.npmjs.com/package/angular-easyfb) [](http://bower.io/search/?q=angular-easyfb) [](https://gitter.im/pc035860/angular-easyfb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 2 | 3 | AngularJS + Facebook JavaScript SDK. 4 | 5 | **Start from v1.1.0, `angular-easyfb` adds support for [Facebook Platform versioning](https://developers.facebook.com/docs/apps/changelog/).** 6 | 7 | **Please check out [the new FB JS SDK setup doc](https://developers.facebook.com/docs/javascript/quickstart#loading) if you want to switch platform versions (module default is `v2.6`).** 8 | 9 | #### Features 10 | 11 | * Full [Facebook JavaScript SDK](https://developers.facebook.com/docs/reference/javascript/) support 12 | * Seamless FB SDK initialization (asynchronously load script and FB.init) 13 | * All SDK API callbacks are automatically applied with AngularJS context 14 | * Support both callback and $q promise 15 | * Provide built-in directive support for Facebook XFBML plugins 16 | 17 | #### Demos 18 | 19 | * [API demo](http://plnkr.co/edit/qclqht?p=preview) 20 | * [API demo (promise version)](http://plnkr.co/edit/UMUtFc?p=preview) 21 | * [Built-in social plugin directives demo](http://plnkr.co/edit/z8751z?p=preview) 22 | 23 | 24 | ## Getting started 25 | 26 | Include the angular-easyfb module with AngularJS script in your page. 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | Add `ezfb` to your app module's dependency. 33 | ```js 34 | angular.module('myApp', ['ezfb']); 35 | ``` 36 | 37 | ### Install with npm 38 | 39 | ```sh 40 | npm install angular-easyfb 41 | ``` 42 | 43 | ### Install with Bower 44 | 45 | ```sh 46 | bower install angular-easyfb 47 | ``` 48 | 49 | ## Usage 50 | 51 | ### `ezfb` service 52 | 53 | #### Configuration 54 | 55 | ###### `getLocale` / `setLocale` 56 | 57 | Configure the locale of the original FB script file. Default locale is `en_US`. 58 | 59 | ```js 60 | angular.module('myApp') 61 | 62 | .config(function (ezfbProvider) { 63 | ezfbProvider.setLocale('zh_TW'); 64 | }); 65 | ``` 66 | 67 | ###### `getInitParams` / `setInitParams` 68 | 69 | Configure parameters for the original `FB.init` with `ezfbProvider.setInitParams`. (See also [`ezfb.init`](#fbinit)) 70 | 71 | ```js 72 | angular.module('myApp') 73 | 74 | .config(function (ezfbProvider) { 75 | ezfbProvider.setInitParams({ 76 | // This is my FB app id for plunker demo app 77 | appId: '386469651480295', 78 | 79 | // Module default is `v2.6`. 80 | // If you want to use Facebook platform `v2.3`, you'll have to add the following parameter. 81 | // https://developers.facebook.com/docs/javascript/reference/FB.init 82 | version: 'v2.3' 83 | }); 84 | }); 85 | ``` 86 | 87 | ###### `getInitFunction` / `setInitFunction` 88 | 89 | Customize the original `FB.init` function call with services injection support. The initialization parameters set in `setInitParams` are available via local injection `ezfbInitParams`. 90 | 91 | ```js 92 | // Default init function 93 | var _defaultInitFunction = ['$window', 'ezfbInitParams', function ($window, ezfbInitParams) { 94 | // Initialize the FB JS SDK 95 | $window.FB.init(ezfbInitParams); 96 | }]; 97 | ``` 98 | 99 | Customization example: 100 | ```js 101 | angular.module('myApp') 102 | 103 | .config(function (ezfbProvider) { 104 | var myInitFunction = function ($window, $rootScope, ezfbInitParams) { 105 | $window.FB.init({ 106 | appId: '386469651480295' 107 | }); 108 | // or 109 | // $window.FB.init(ezfbInitParams); 110 | 111 | $rootScope.$broadcast('FB.init'); 112 | }; 113 | 114 | ezfbProvider.setInitFunction(myInitFunction); 115 | }); 116 | ``` 117 | 118 | ###### `getLoadSDKFunction / setLoadSDKFunction` 119 | 120 | Customize Facebook JS SDK loading. The function also supports DI, with two more local injections: 121 | 122 | - `ezfbLocale` - locale name 123 | - `ezfbAsyncInit` - must called to finish the module initialization process 124 | 125 | ```js 126 | // Default load SDK function 127 | var _defaultLoadSDKFunction = [ 128 | '$window', '$document', 'ezfbAsyncInit', 'ezfbLocale', 129 | function ($window, $document, ezfbAsyncInit, ezfbLocale) { 130 | // Load the SDK's source Asynchronously 131 | (function(d){ 132 | var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0]; 133 | if (d.getElementById(id)) {return;} 134 | js = d.createElement('script'); js.id = id; js.async = true; 135 | js.src = "//connect.facebook.net/" + ezfbLocale + "/sdk.js"; 136 | // js.src = "//connect.facebook.net/" + ezfbLocale + "/sdk/debug.js"; // debug 137 | ref.parentNode.insertBefore(js, ref); 138 | }($document[0])); 139 | 140 | $window.fbAsyncInit = ezfbAsyncInit; 141 | }]; 142 | ``` 143 | 144 | Customization example: 145 | ```js 146 | angular.module('myApp') 147 | 148 | .config(function (ezfbProvider) { 149 | // Feasible config if the FB JS SDK script is already loaded 150 | ezfbProvider.setLoadSDKFunction(function (ezfbAsyncInit) { 151 | ezfbAsyncInit(); 152 | }); 153 | }); 154 | ``` 155 | 156 | 157 | #### ezfb.init 158 | 159 | In the case that you don't want to(or you can't) configure your `FB.init` parameters in configuration phase, you may use `ezfb.init` in run phase. And any `ezfb` API call will not run until `ezfb.init` is called. 160 | 161 | ```js 162 | angular.module('myApp') 163 | 164 | .run(function (ezfb) { 165 | ezfb.init({ 166 | // This is my FB app id for plunker demo app 167 | appId: '386469651480295' 168 | }); 169 | }); 170 | ``` 171 | 172 | 173 | #### using ezfb 174 | 175 | This is the original `FB` wrapping service, all `FB.*` APIs are available through `ezfb.*`. 176 | 177 | No need to worry about FB script loading and Angular context applying at all. 178 | 179 | 180 | ```js 181 | angular.module('myApp') 182 | 183 | /** 184 | * Inject into controller 185 | */ 186 | .controller('MainCtrl', function (ezfb) { 187 | /** 188 | * Origin: FB.getLoginStatus 189 | */ 190 | ezfb.getLoginStatus(function (res) { 191 | $scope.loginStatus = res; 192 | 193 | (more || angular.noop)(); 194 | }); 195 | 196 | /** 197 | * Origin: FB.api 198 | */ 199 | ezfb.api('/me', function (res) { 200 | $scope.apiMe = res; 201 | }); 202 | }); 203 | 204 | ``` 205 | 206 | Watch the [demo](http://plnkr.co/edit/qclqht?p=preview) to see it in action. 207 | 208 | #### $q promise support 209 | 210 | Support of $q promise create more possibility for `ezfb` service. 211 | 212 | **Only the APIs with callback support returning promise.** 213 | 214 | ##### Combine multiple api calls 215 | ```js 216 | $q.all([ 217 | ezfb.api('/me'), 218 | ezfb.api('/me/likes') 219 | ]) 220 | .then(function (rsvList) { 221 | // result of api('/me') 222 | console.log(rsvList[0]); 223 | 224 | // result of api('/me/likes') 225 | console.log(rsvList[1]); 226 | }); 227 | ``` 228 | 229 | Watch the [promise version api demo](http://plnkr.co/edit/UMUtFc?p=preview) to see it in action. 230 | 231 | 232 | ### Social plugins support 233 | 234 | [Facebook Social Plugins](https://developers.facebook.com/docs/plugins/) are now supported with built-in directives. 235 | 236 | The code copied from the above link will automatically work in `angular-easyfb`-covered AngularJS apps. 237 | 238 | Additionally, you can add an `onrender` parameter to the social plugin directive. Expressions in the `onrender` parameter will be evaluated every time the social plugin gets rendered. 239 | 240 | ```html 241 |
247 | ``` 248 | 249 | [Demo (directives demonstration)](http://plnkr.co/edit/z8751z?p=preview) 250 | 251 | [Demo2 (interpolated attributes)](http://plnkr.co/edit/gFM1LV?p=preview) 252 | 253 | 254 | ## Changelog 255 | 256 | See the changelog [here](https://github.com/pc035860/angular-easyfb/blob/master/CHANGELOG.md). 257 | 258 | 259 | ## Develop 260 | 261 | `angular-easyfb` uses [Grunt](http://gruntjs.com/) to run all the development tasks. 262 | 263 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. 264 | 265 | `angular-easyfb` also uses [Bower](http://bower.io/) to manage packages for tests. 266 | 267 | ### Setup 268 | 269 | After cloning the git repo to your place, simply run following commands to install required packages. 270 | ```sh 271 | npm install 272 | bower install 273 | ``` 274 | 275 | ### Build 276 | 277 | Generate a minified js file after running all the tests. 278 | 279 | ```sh 280 | grunt 281 | ``` 282 | 283 | ### Running tests 284 | 285 | Unit tests: 286 | ```sh 287 | grunt test:unit 288 | ``` 289 | 290 | Test coverage: 291 | ```sh 292 | grunt coverage 293 | ``` 294 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-easyfb", 3 | "version": "1.6.0", 4 | "description": "Super easy AngularJS + Facebook JavaScript SDK.", 5 | "main": "./build/angular-easyfb.js", 6 | "ignore": [ 7 | "**/.*", 8 | "node_modules", 9 | "bower_components", 10 | "Gruntfile.js", 11 | "package.json", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": {}, 16 | "devDependencies": { 17 | "angular": "^1.4.0", 18 | "angular-mocks": "^1.4.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /build/angular-easyfb.js: -------------------------------------------------------------------------------- 1 | /*! angular-easyfb 2 | version: 1.6.0 3 | build date: 2019-08-20 4 | author: Robin Fan 5 | https://github.com/pc035860/angular-easyfb.git */ 6 | (function(module) { 7 | module.provider("ezfb", function() { 8 | var APP_EVENTS_EVENT_NAMES = { 9 | COMPLETED_REGISTRATION: "fb_mobile_complete_registration", 10 | VIEWED_CONTENT: "fb_mobile_content_view", 11 | SEARCHED: "fb_mobile_search", 12 | RATED: "fb_mobile_rate", 13 | COMPLETED_TUTORIAL: "fb_mobile_tutorial_completion", 14 | ADDED_TO_CART: "fb_mobile_add_to_cart", 15 | ADDED_TO_WISHLIST: "fb_mobile_add_to_wishlist", 16 | INITIATED_CHECKOUT: "fb_mobile_initiated_checkout", 17 | ADDED_PAYMENT_INFO: "fb_mobile_add_payment_info", 18 | ACHIEVED_LEVEL: "fb_mobile_level_achieved", 19 | UNLOCKED_ACHIEVEMENT: "fb_mobile_achievement_unlocked", 20 | SPENT_CREDITS: "fb_mobile_spent_credits" 21 | }, APP_EVENTS_PARAMETER_NAMES = { 22 | CURRENCY: "fb_currency", 23 | REGISTRATION_METHOD: "fb_registration_method", 24 | CONTENT_TYPE: "fb_content_type", 25 | CONTENT_ID: "fb_content_id", 26 | SEARCH_STRING: "fb_search_string", 27 | SUCCESS: "fb_success", 28 | MAX_RATING_VALUE: "fb_max_rating_value", 29 | PAYMENT_INFO_AVAILABLE: "fb_payment_info_available", 30 | NUM_ITEMS: "fb_num_items", 31 | LEVEL: "fb_level", 32 | DESCRIPTION: "fb_description" 33 | }; 34 | var NO_CALLBACK = -1; 35 | var _publishedApis = { 36 | api: [ 1, 2, 3 ], 37 | ui: 1, 38 | getAuthResponse: NO_CALLBACK, 39 | getLoginStatus: 0, 40 | login: 0, 41 | logout: 0, 42 | "Event.subscribe": 1, 43 | "Event.unsubscribe": 1, 44 | "XFBML.parse": 1, 45 | "Canvas.Prefetcher.addStaticResource": NO_CALLBACK, 46 | "Canvas.Prefetcher.setCollectionMode": NO_CALLBACK, 47 | "Canvas.getPageInfo": 0, 48 | "Canvas.hideFlashElement": NO_CALLBACK, 49 | "Canvas.scrollTo": NO_CALLBACK, 50 | "Canvas.setAutoGrow": NO_CALLBACK, 51 | "Canvas.setDoneLoading": 0, 52 | "Canvas.setSize": NO_CALLBACK, 53 | "Canvas.setUrlHandler": 0, 54 | "Canvas.showFlashElement": NO_CALLBACK, 55 | "Canvas.startTimer": NO_CALLBACK, 56 | "Canvas.stopTimer": 0, 57 | "AppEvents.logEvent": NO_CALLBACK, 58 | "AppEvents.logPurchase": NO_CALLBACK, 59 | "AppEvents.activateApp": NO_CALLBACK 60 | }; 61 | var _locale = "en_US"; 62 | var _initParams = { 63 | status: true, 64 | cookie: true, 65 | xfbml: true, 66 | version: "v2.6" 67 | }; 68 | var _defaultLoadSDKFunction = [ "$window", "$document", "$timeout", "ezfbAsyncInit", "ezfbLocale", function($window, $document, $timeout, ezfbAsyncInit, ezfbLocale) { 69 | (function(d) { 70 | var insertScript = function() { 71 | var js, id = "facebook-jssdk", ref = d.getElementsByTagName("script")[0]; 72 | if (d.getElementById(id)) { 73 | return; 74 | } 75 | js = d.createElement("script"); 76 | js.id = id; 77 | js.async = true; 78 | js.src = "//connect.facebook.net/" + ezfbLocale + "/sdk.js"; 79 | ref.parentNode.insertBefore(js, ref); 80 | }; 81 | $timeout(insertScript, 0, false); 82 | })($document[0]); 83 | $window.fbAsyncInit = ezfbAsyncInit; 84 | } ], _loadSDKFunction = _defaultLoadSDKFunction; 85 | var _defaultInitFunction = [ "$window", "ezfbInitParams", function($window, ezfbInitParams) { 86 | $window.FB.init(ezfbInitParams); 87 | } ], _initFunction = _defaultInitFunction; 88 | function _config(target, config) { 89 | if (angular.isObject(config)) { 90 | angular.extend(target, config); 91 | } else { 92 | return angular.copy(target); 93 | } 94 | } 95 | function _proxy(func, context, args) { 96 | return function() { 97 | return func.apply(context, args); 98 | }; 99 | } 100 | return { 101 | setInitParams: function(params) { 102 | _config(_initParams, params); 103 | }, 104 | getInitParams: function() { 105 | return _config(_initParams); 106 | }, 107 | setLocale: function(locale) { 108 | _locale = locale; 109 | }, 110 | getLocale: function() { 111 | return _locale; 112 | }, 113 | setLoadSDKFunction: function(func) { 114 | if (angular.isArray(func) || angular.isFunction(func)) { 115 | _loadSDKFunction = func; 116 | } else { 117 | throw new Error("Init function type error."); 118 | } 119 | }, 120 | getLoadSDKFunction: function() { 121 | return _loadSDKFunction; 122 | }, 123 | setInitFunction: function(func) { 124 | if (angular.isArray(func) || angular.isFunction(func)) { 125 | _initFunction = func; 126 | } else { 127 | throw new Error("Init function type error."); 128 | } 129 | }, 130 | getInitFunction: function() { 131 | return _initFunction; 132 | }, 133 | $get: [ "$window", "$q", "$document", "$parse", "$rootScope", "$injector", "$timeout", function($window, $q, $document, $parse, $rootScope, $injector, $timeout) { 134 | var _initReady, _initRenderReady, _ezfb, _savedListeners, _paramsReady, ezfbAsyncInit; 135 | _savedListeners = {}; 136 | _paramsReady = $q.defer(); 137 | if (_initParams.appId || _initFunction !== _defaultInitFunction) { 138 | _paramsReady.resolve(); 139 | } 140 | _initReady = $q.defer(); 141 | _initRenderReady = $q.defer(); 142 | if (!$document[0].getElementById("fb-root")) { 143 | $document.find("body").append(''); 144 | } 145 | ezfbAsyncInit = function() { 146 | _paramsReady.promise.then(function() { 147 | if (_initParams.xfbml) { 148 | var onRender = function() { 149 | _ezfb.$$xfbmlRendered = true; 150 | $timeout(function() { 151 | _initRenderReady.resolve(true); 152 | }); 153 | _ezfb.Event.unsubscribe("xfbml.render", onRender); 154 | }; 155 | _ezfb.Event.subscribe("xfbml.render", onRender); 156 | } else { 157 | $timeout(function() { 158 | _initRenderReady.resolve(false); 159 | }); 160 | } 161 | $injector.invoke(_initFunction, null, { 162 | ezfbInitParams: _initParams 163 | }); 164 | _ezfb.$$ready = true; 165 | _initReady.resolve(); 166 | }); 167 | }; 168 | $injector.invoke(_loadSDKFunction, null, { 169 | ezfbAsyncInit: ezfbAsyncInit, 170 | ezfbLocale: _locale 171 | }); 172 | _ezfb = { 173 | $$ready: false, 174 | $$xfbmlRendered: false, 175 | $ready: function(fn) { 176 | if (angular.isFunction(fn)) { 177 | _initReady.promise.then(fn); 178 | } 179 | return _initReady.promise; 180 | }, 181 | $rendered: function(fn) { 182 | if (angular.isFunction(fn)) { 183 | _initRenderReady.promise.then(fn); 184 | } 185 | return _initRenderReady.promise; 186 | }, 187 | init: function(params) { 188 | _config(_initParams, params); 189 | _paramsReady.resolve(); 190 | }, 191 | AppEvents: { 192 | EventNames: APP_EVENTS_EVENT_NAMES, 193 | ParameterNames: APP_EVENTS_PARAMETER_NAMES 194 | } 195 | }; 196 | angular.forEach(_publishedApis, function(cbArgIndex, apiPath) { 197 | var getter = $parse(apiPath), setter = getter.assign; 198 | setter(_ezfb, function() { 199 | var apiCall = _proxy(function(args) { 200 | var dfd, replaceCallbackAt; 201 | dfd = $q.defer(); 202 | replaceCallbackAt = function(index) { 203 | var func, newFunc; 204 | func = angular.isFunction(args[index]) ? args[index] : angular.noop; 205 | newFunc = function() { 206 | var funcArgs = Array.prototype.slice.call(arguments); 207 | if ($rootScope.$$phase) { 208 | func.apply(null, funcArgs); 209 | dfd.resolve.apply(dfd, funcArgs); 210 | } else { 211 | $rootScope.$apply(function() { 212 | func.apply(null, funcArgs); 213 | dfd.resolve.apply(dfd, funcArgs); 214 | }); 215 | } 216 | }; 217 | while (args.length <= index) { 218 | args.push(null); 219 | } 220 | var eventName; 221 | if (apiPath === "Event.subscribe") { 222 | eventName = args[0]; 223 | if (angular.isUndefined(_savedListeners[eventName])) { 224 | _savedListeners[eventName] = []; 225 | } 226 | _savedListeners[eventName].push({ 227 | original: func, 228 | wrapped: newFunc 229 | }); 230 | } else if (apiPath === "Event.unsubscribe") { 231 | eventName = args[0]; 232 | if (angular.isArray(_savedListeners[eventName])) { 233 | var i, subscribed, l = _savedListeners[eventName].length; 234 | for (i = 0; i < l; i++) { 235 | subscribed = _savedListeners[eventName][i]; 236 | if (subscribed.original === func) { 237 | newFunc = subscribed.wrapped; 238 | _savedListeners[eventName].splice(i, 1); 239 | break; 240 | } 241 | } 242 | } 243 | } 244 | args[index] = newFunc; 245 | }; 246 | if (cbArgIndex !== NO_CALLBACK) { 247 | if (angular.isNumber(cbArgIndex)) { 248 | replaceCallbackAt(cbArgIndex); 249 | } else if (angular.isArray(cbArgIndex)) { 250 | var i, c; 251 | for (i = 0; i < cbArgIndex.length; i++) { 252 | c = cbArgIndex[i]; 253 | if (args.length == c || args.length == c + 1 && angular.isFunction(args[c])) { 254 | replaceCallbackAt(c); 255 | break; 256 | } 257 | } 258 | } 259 | } 260 | var origFBFunc = getter($window.FB); 261 | if (!origFBFunc) { 262 | throw new Error("Facebook API `FB." + apiPath + "` doesn't exist."); 263 | } 264 | origFBFunc.apply($window.FB, args); 265 | return dfd.promise; 266 | }, null, [ Array.prototype.slice.call(arguments) ]); 267 | if (apiPath === "getAuthResponse") { 268 | if (angular.isUndefined($window.FB)) { 269 | throw new Error("`FB` is not ready."); 270 | } 271 | return $window.FB.getAuthResponse(); 272 | } else if (cbArgIndex === NO_CALLBACK) { 273 | _initReady.promise.then(apiCall); 274 | } else { 275 | return _initReady.promise.then(apiCall); 276 | } 277 | }); 278 | }); 279 | return _ezfb; 280 | } ] 281 | }; 282 | }).directive("ezfbXfbml", [ "ezfb", "$parse", "$compile", "$timeout", function(ezfb, $parse, $compile, $timeout) { 283 | return { 284 | restrict: "EAC", 285 | controller: function() {}, 286 | compile: function(tElm, tAttrs) { 287 | var _savedHtml = tElm.html(); 288 | return function postLink(scope, iElm, iAttrs) { 289 | var _rendering = true, onrenderExp = iAttrs.onrender, onrenderHandler = function() { 290 | if (_rendering) { 291 | if (onrenderExp) { 292 | scope.$eval(onrenderExp); 293 | } 294 | _rendering = false; 295 | } 296 | }; 297 | ezfb.XFBML.parse(iElm[0], onrenderHandler); 298 | var setter = $parse(iAttrs.ezfbXfbml).assign; 299 | scope.$watch(iAttrs.ezfbXfbml, function(val) { 300 | if (val) { 301 | _rendering = true; 302 | iElm.html(_savedHtml); 303 | $compile(iElm.contents())(scope); 304 | $timeout(function() { 305 | ezfb.XFBML.parse(iElm[0], onrenderHandler); 306 | }); 307 | (setter || angular.noop)(scope, false); 308 | } 309 | }, true); 310 | }; 311 | } 312 | }; 313 | } ]); 314 | var _socialPluginDirectiveConfig = { 315 | fbLike: [ "action", "colorscheme", "href", "kidDirectedSite", "layout", "ref", "share", "showFaces", "width" ], 316 | fbShareButton: [ "href", "layout", "width" ], 317 | fbSend: [ "colorscheme", "href", "kidDirectedSite", "ref" ], 318 | fbPost: [ "href", "width" ], 319 | fbFollow: [ "colorscheme", "href", "kidDirectedSite", "layout", "showFaces", "width" ], 320 | fbComments: [ "colorscheme", "href", "mobile", "numPosts", "orderBy", "width" ], 321 | fbCommentsCount: [ "href" ], 322 | fbActivity: [ "action", "appId", "colorscheme", "filter", "header", "height", "linktarget", "maxAge", "recommendations", "ref", "site", "width" ], 323 | fbRecommendations: [ "action", "appId", "colorscheme", "header", "height", "linktarget", "maxAge", "ref", "site", "width" ], 324 | fbRecommendationsBar: [ "action", "href", "maxAge", "numRecommendations", "readTime", "ref", "side", "site", "trigger" ], 325 | fbLikeBox: [ "colorscheme", "forceWall", "header", "height", "href", "showBorder", "showFaces", "stream", "width" ], 326 | fbFacepile: [ "action", "appId", "colorscheme", "href", "maxRows", "size", "width" ], 327 | fbPage: [ "href", "width", "height", "hideCover", "showFacepile", "showPosts" ], 328 | fbVideo: [ "href", "width", "allowfullscreen" ], 329 | fbAdPreview: [ "adAccountId", "adgroupId", "creative", "creativeId", "adFormat", "pageType", "targeting", "post" ], 330 | fbSendToMessenger: [ "messengerAppId", "pageId", "ref", "color", "size" ], 331 | fbMessengermessageus: [ "messengerAppId", "pageId", "color", "size" ], 332 | fbLoginButton: [ "autoLogoutLink", "maxRows", "onLogin", "scope", "size", "showFaces", "defaultAudience" ], 333 | fbCommentEmbed: [ "href", "width", "includeParent" ], 334 | fbSave: [ "uri" ] 335 | }; 336 | angular.forEach(_socialPluginDirectiveConfig, creatSocialPluginDirective); 337 | function creatSocialPluginDirective(availableAttrs, dirName) { 338 | var CLASS_WRAP = "ezfb-social-plugin-wrap", STYLE_WRAP_SPAN = "display: inline-block; width: 0; height: 0; overflow: hidden;"; 339 | var PLUGINS_WITH_ADAPTIVE_WIDTH = [ "fbPage", "fbComments" ]; 340 | var _wrap = function($elm) { 341 | var tmpl = ''; 342 | return $elm.wrap(tmpl).parent(); 343 | }, _wrapAdaptive = function($elm) { 344 | var tmpl = '