├── .travis.yml ├── .gitignore ├── samples └── scrumptious │ ├── public │ ├── images │ │ ├── meal.gif │ │ ├── fblogin.png │ │ ├── meals │ │ │ ├── pizza.png │ │ │ ├── thai.png │ │ │ ├── chinese.png │ │ │ ├── french.png │ │ │ ├── hotdog.png │ │ │ ├── indian.png │ │ │ ├── italian.png │ │ │ ├── pizza@2x.png │ │ │ ├── thai@2x.png │ │ │ ├── chinese@2x.png │ │ │ ├── french@2x.png │ │ │ ├── hotdog@2x.png │ │ │ ├── indian@2x.png │ │ │ ├── italian@2x.png │ │ │ ├── pizza-full.png │ │ │ ├── thai-full.png │ │ │ ├── cheeseburger.png │ │ │ ├── chinese-full.png │ │ │ ├── french-full.png │ │ │ ├── hotdog-full.png │ │ │ ├── indian-full.png │ │ │ ├── italian-full.png │ │ │ ├── cheeseburger@2x.png │ │ │ └── cheeseburger-full.png │ │ ├── action-eating.png │ │ ├── action-people.png │ │ ├── action-location.png │ │ ├── scrumptious_logo.png │ │ └── scrumptious_logo_large.png │ ├── css │ │ ├── images │ │ │ ├── ajax-loader.gif │ │ │ ├── icons-18-black.png │ │ │ ├── icons-18-white.png │ │ │ ├── icons-36-black.png │ │ │ └── icons-36-white.png │ │ └── base.css │ ├── stylesheets │ │ └── style.css │ └── js │ │ ├── main.js │ │ └── mustache.js │ ├── package.json │ ├── config.js │ ├── views │ ├── meal.ejs │ ├── index.ejs │ └── menu.ejs │ ├── routes │ ├── api.js │ ├── meals.js │ └── home.js │ └── app.js ├── LICENSE ├── package.json ├── test └── api │ └── get.js ├── README.md └── fb.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" 6 | - "0.6" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | 4 | .DS_Store 5 | Thumbs.db 6 | # WebStrom IDE ignore 7 | .idea/ 8 | -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meal.gif -------------------------------------------------------------------------------- /samples/scrumptious/public/images/fblogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/fblogin.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/pizza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/pizza.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/thai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/thai.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/action-eating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/action-eating.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/action-people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/action-people.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/chinese.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/chinese.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/french.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/french.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/hotdog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/hotdog.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/indian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/indian.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/italian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/italian.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/pizza@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/pizza@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/thai@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/thai@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/css/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/css/images/ajax-loader.gif -------------------------------------------------------------------------------- /samples/scrumptious/public/images/action-location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/action-location.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/chinese@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/chinese@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/french@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/french@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/hotdog@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/hotdog@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/indian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/indian@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/italian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/italian@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/pizza-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/pizza-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/thai-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/thai-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/scrumptious_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/scrumptious_logo.png -------------------------------------------------------------------------------- /samples/scrumptious/public/css/images/icons-18-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/css/images/icons-18-black.png -------------------------------------------------------------------------------- /samples/scrumptious/public/css/images/icons-18-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/css/images/icons-18-white.png -------------------------------------------------------------------------------- /samples/scrumptious/public/css/images/icons-36-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/css/images/icons-36-black.png -------------------------------------------------------------------------------- /samples/scrumptious/public/css/images/icons-36-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/css/images/icons-36-white.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/cheeseburger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/cheeseburger.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/chinese-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/chinese-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/french-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/french-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/hotdog-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/hotdog-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/indian-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/indian-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/italian-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/italian-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/cheeseburger@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/cheeseburger@2x.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/meals/cheeseburger-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/meals/cheeseburger-full.png -------------------------------------------------------------------------------- /samples/scrumptious/public/images/scrumptious_logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thuzi/facebook-node-sdk/HEAD/samples/scrumptious/public/images/scrumptious_logo_large.png -------------------------------------------------------------------------------- /samples/scrumptious/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } -------------------------------------------------------------------------------- /samples/scrumptious/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scrumptious", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app" 7 | }, 8 | "dependencies": { 9 | "express": "3.1.0", 10 | "ejs": "*", 11 | "step": "0.0.5" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/scrumptious/config.js: -------------------------------------------------------------------------------- 1 | 2 | var config = { }; 3 | 4 | // should end in / 5 | config.rootUrl = process.env.ROOT_URL || 'http://localhost:3000/'; 6 | 7 | config.facebook = { 8 | appId: process.env.FACEBOOK_APPID || '130243393813697', 9 | appSecret: process.env.FACEBOOK_APPSECRET || 'c82696768ae4ad8b63db874cb64eb558', 10 | appNamespace: process.env.FACEBOOK_APPNAMESPACE || 'nodescrumptious', 11 | redirectUri: process.env.FACEBOOK_REDIRECTURI || config.rootUrl + 'login/callback' 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /samples/scrumptious/views/meal.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= meal.title %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Thuzi LLC 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Thuzi LLC (https://github.com/Thuzi)", 3 | "name": "fb", 4 | "description": "NodeJS Library for Facebook", 5 | "keywords": [ 6 | "facebook", 7 | "fb", 8 | "graph" 9 | ], 10 | "version": "0.7.3", 11 | "homepage": "https://github.com/Thuzi/facebook-node-sdk", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/Thuzi/facebook-node-sdk.git" 15 | }, 16 | "engines": { 17 | "node": ">=0.6.9" 18 | }, 19 | "dependencies": { 20 | "request": "2.9.x", 21 | "crypto": "0.0.3" 22 | }, 23 | "devDependencies": { 24 | "nock": "~0.17.2", 25 | "mocha": "~1.9.0", 26 | "should": "~1.2.2" 27 | }, 28 | "optionalDependencies": {}, 29 | "main": "./fb.js", 30 | "scripts": { 31 | "test": "node ./node_modules/mocha/bin/mocha --recursive --reporter spec", 32 | "testw": "node ./node_modules/mocha/bin/mocha --recursive --watch --reporter spec" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/scrumptious/routes/api.js: -------------------------------------------------------------------------------- 1 | 2 | var FB = require('../../../fb'), 3 | 4 | config = require('../config'); 5 | 6 | exports.search = function (req, res) { 7 | var parameters = req.query; 8 | parameters.access_token = req.session.access_token; 9 | FB.api('/search', req.query, function (result) { 10 | if(!result || result.error) { 11 | return res.send(500, 'error'); 12 | } 13 | res.send(result); 14 | }); 15 | }; 16 | 17 | exports.friends = function (req, res) { 18 | FB.api('me/friends', { 19 | fields: 'name,picture', 20 | limit: 250, 21 | access_token: req.session.access_token 22 | }, function (result) { 23 | if(!result || result.error) { 24 | return res.send(500, 'error'); 25 | } 26 | res.send(result); 27 | }); 28 | }; 29 | 30 | exports.announce = function (req, res) { 31 | var parameters = req.body; 32 | parameters.access_token = req.session.access_token; 33 | FB.api('/me/' + config.facebook.appNamespace +':eat', 'post', parameters , function (result) { 34 | if(!result) { 35 | return res.send(500, 'error'); 36 | } else if(result.error) { 37 | if(result.error.type == 'OAuthException') { 38 | result.redirectUri = FB.getLoginUrl({ scope: 'user_about_me,publish_actions', state: encodeURIComponent(JSON.stringify(parameters)) }); 39 | } 40 | return res.send(500, result); 41 | } 42 | 43 | res.send(result); 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /samples/scrumptious/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Scrumptious 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 |
30 |
31 |
32 | ScrumptiousScrumptious 33 |
34 |
35 |

To get started, login using Facebook

36 |
37 | 41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /samples/scrumptious/routes/meals.js: -------------------------------------------------------------------------------- 1 | 2 | var config = require('../config'); 3 | 4 | var meals = { 5 | cheeseburger: "Cheeseburger", 6 | chinese: "Chinese", 7 | french: "French", 8 | hotdog: "Hot Dog", 9 | indian: "Indian", 10 | italian: "Italian", 11 | pizza: "Pizza" 12 | }; 13 | 14 | exports.show = function (req, res, next) { 15 | var id = req.params.id, 16 | meal; 17 | 18 | if(!meals[id]) { 19 | return res.send(404); 20 | } 21 | 22 | meal = { 23 | id: id, 24 | title: meals[id], 25 | url: config.rootUrl + 'meals/' + id, 26 | imageUrl: config.rootUrl + 'images/meals/' + id + '-full.png' 27 | }; 28 | 29 | res.render('meal', { 30 | appId: config.facebook.appId, 31 | appNamespace: config.facebook.appNamespace, 32 | meal: meal 33 | }); 34 | }; 35 | 36 | exports.showWinJs = function (req, res, next) { 37 | // this method is used for facebook-winjs-sdk sample and not required to actually run this sample 38 | var id = req.params.id, 39 | meal; 40 | 41 | if(!meals[id]) { 42 | return res.send(404); 43 | } 44 | 45 | meal = { 46 | id: id, 47 | title: meals[id], 48 | url: config.rootUrl + 'winjs/meals/' + id, 49 | imageUrl: config.rootUrl + 'images/meals/' + id + '-full.png' 50 | }; 51 | 52 | res.render('meal', { 53 | appId: '438749336206495', 54 | appNamespace: 'winjsscrumptious', 55 | meal: meal 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /samples/scrumptious/app.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('express'), 3 | FB = require('../../fb'), 4 | http = require('http'), 5 | path = require('path'), 6 | 7 | config = require('./config'), 8 | 9 | api = require('./routes/api'), 10 | home = require('./routes/home'), 11 | meals = require('./routes/meals'); 12 | 13 | var app = express(); 14 | 15 | if(!config.facebook.appId || !config.facebook.appSecret) { 16 | throw new Error('facebook appId and appSecret required in config.js'); 17 | } 18 | 19 | app.configure(function() { 20 | app.set('port', process.env.PORT || 3000); 21 | app.set('views', __dirname + '/views'); 22 | app.set('view engine', 'ejs'); 23 | app.use(express.favicon()); 24 | app.use(express.logger('dev')); 25 | app.use(express.cookieParser()); 26 | app.use(express.cookieSession({ secret: 'secret'})); 27 | app.use(express.bodyParser()); 28 | app.use(express.methodOverride()); 29 | app.use(app.router); 30 | app.use(express.static(path.join(__dirname, 'public'))); 31 | }); 32 | 33 | app.configure('development', function() { 34 | app.use(express.errorHandler()); 35 | }); 36 | 37 | app.get( '/', home.index); 38 | app.get( '/login/callback', home.loginCallback); 39 | app.get( '/logout', home.logout); 40 | app.get( '/search', api.search); 41 | app.get( '/friends', api.friends); 42 | app.post('/announce', api.announce); 43 | app.get( '/meals/:id', meals.show); 44 | app.get( '/winjs/meals/:id', meals.showWinJs); // this is used for facebook-winjs-sdk sample and not required to actually run this sample 45 | 46 | http.createServer(app).listen(app.get('port'), function() { 47 | console.log("Express server listening on port " + app.get('port')); 48 | }); 49 | -------------------------------------------------------------------------------- /samples/scrumptious/routes/home.js: -------------------------------------------------------------------------------- 1 | 2 | var FB = require('../../../fb'), 3 | Step = require('step'), 4 | 5 | config = require('../config'); 6 | 7 | FB.options({ 8 | appId: config.facebook.appId, 9 | appSecret: config.facebook.appSecret, 10 | redirectUri: config.facebook.redirectUri 11 | }); 12 | 13 | exports.index = function(req, res) { 14 | var accessToken = req.session.access_token; 15 | if(!accessToken) { 16 | res.render('index', { 17 | title: 'Express', 18 | loginUrl: FB.getLoginUrl({ scope: 'user_about_me' }) 19 | }); 20 | } else { 21 | res.render('menu'); 22 | } 23 | }; 24 | 25 | exports.loginCallback = function (req, res, next) { 26 | var code = req.query.code; 27 | 28 | if(req.query.error) { 29 | // user might have disallowed the app 30 | return res.send('login-error ' + req.query.error_description); 31 | } else if(!code) { 32 | return res.redirect('/'); 33 | } 34 | 35 | Step( 36 | function exchangeCodeForAccessToken() { 37 | FB.napi('oauth/access_token', { 38 | client_id: FB.options('appId'), 39 | client_secret: FB.options('appSecret'), 40 | redirect_uri: FB.options('redirectUri'), 41 | code: code 42 | }, this); 43 | }, 44 | function extendAccessToken(err, result) { 45 | if(err) throw(err); 46 | FB.napi('oauth/access_token', { 47 | client_id: FB.options('appId'), 48 | client_secret: FB.options('appSecret'), 49 | grant_type: 'fb_exchange_token', 50 | fb_exchange_token: result.access_token 51 | }, this); 52 | }, 53 | function (err, result) { 54 | if(err) return next(err); 55 | 56 | req.session.access_token = result.access_token; 57 | req.session.expires = result.expires || 0; 58 | 59 | if(req.query.state) { 60 | var parameters = JSON.parse(req.query.state); 61 | parameters.access_token = req.session.access_token; 62 | 63 | console.log(parameters); 64 | 65 | FB.api('/me/' + config.facebook.appNamespace +':eat', 'post', parameters , function (result) { 66 | console.log(result); 67 | if(!result || result.error) { 68 | return res.send(500, result || 'error'); 69 | // return res.send(500, 'error'); 70 | } 71 | 72 | return res.redirect('/'); 73 | }); 74 | } else { 75 | return res.redirect('/'); 76 | } 77 | } 78 | ); 79 | }; 80 | 81 | exports.logout = function (req, res) { 82 | req.session = null; // clear session 83 | res.redirect('/'); 84 | }; 85 | -------------------------------------------------------------------------------- /test/api/get.js: -------------------------------------------------------------------------------- 1 | var nock = require('nock'), 2 | should = require('should'), 3 | 4 | FB; 5 | 6 | beforeEach(function () { 7 | FB = require('../..'); 8 | }); 9 | 10 | afterEach(function () { 11 | nock.cleanAll(); 12 | }); 13 | 14 | describe('FB.api', function () { 15 | describe('GET', function () { 16 | 17 | describe("FB.api('4', cb)", function () { 18 | 19 | beforeEach(function () { 20 | nock('https://graph.facebook.com:443') 21 | .get('/4') 22 | .reply(200, "{\"id\":\"4\",\"name\":\"Mark Zuckerberg\",\"first_name\":\"Mark\",\"last_name\":\"Zuckerberg\",\"link\":\"http:\\/\\/www.facebook.com\\/zuck\",\"username\":\"zuck\",\"gender\":\"male\",\"locale\":\"en_US\"}", { 'access-control-allow-origin': '*', 23 | 'content-type': 'text/javascript; charset=UTF-8', 24 | 'content-length': '172' }); 25 | }); 26 | 27 | it('should have id 4', function (done) { 28 | FB.api('4', function (result) { 29 | result.should.have.property('id', '4'); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('should have username as zuck', function (done) { 35 | FB.api('4', function (result) { 36 | result.should.have.property('username', 'zuck'); 37 | done(); 38 | }); 39 | }); 40 | 41 | }); 42 | 43 | describe("FB.api('/4', cb)", function () { 44 | it('should have id 4', function (done) { 45 | nock('https://graph.facebook.com:443') 46 | .get('/4') 47 | .reply(200, "{\"id\":\"4\",\"name\":\"Mark Zuckerberg\",\"first_name\":\"Mark\",\"last_name\":\"Zuckerberg\",\"link\":\"http:\\/\\/www.facebook.com\\/zuck\",\"username\":\"zuck\",\"gender\":\"male\",\"locale\":\"en_US\"}", { 'access-control-allow-origin': '*', 48 | 'content-type': 'text/javascript; charset=UTF-8', 49 | 'content-length': '172' }); 50 | 51 | FB.api('/4', function (result) { 52 | result.should.have.property('id', '4'); 53 | done(); 54 | }); 55 | }); 56 | }); 57 | 58 | describe.skip("FB.api(4, cb)", function () { 59 | // this is the default behavior of client side js sdk 60 | it('should throw synchronously: Expression is of type number, not object', function (done) { 61 | // TODO 62 | FB.api(4, function (result) { 63 | }); 64 | }); 65 | }); 66 | 67 | describe("FB.api('4', { fields: 'id' }), cb)", function () { 68 | it("should return { id: '4' } object", function (done) { 69 | nock('https://graph.facebook.com:443') 70 | .get('/4?fields=id') 71 | .reply(200, "{\"id\":\"4\"}", { 72 | 'content-type': 'text/javascript; charset=UTF-8', 73 | 'content-length': '10' }); 74 | 75 | FB.api('4', { fields: 'id'}, function (result) { 76 | result.should.include({id: '4'}); 77 | done(); 78 | }); 79 | }); 80 | }); 81 | 82 | describe("FB.api('4?fields=name', cb)", function () { 83 | it("should return { id: '4' } object", function (done) { 84 | nock('https://graph.facebook.com:443') 85 | .get('/4?fields=name') 86 | .reply(200, "{\"name\":\"Mark Zuckerberg\",\"id\":\"4\"}", { 87 | 'content-type': 'text/javascript; charset=UTF-8', 88 | 'content-length': '10' }); 89 | 90 | FB.api('4?fields=name', function (result) { 91 | result.should.include({id: '4', name: 'Mark Zuckerberg'}); 92 | done(); 93 | }); 94 | }); 95 | }); 96 | 97 | describe("FB.api('/4?fields=name', cb)", function () { 98 | it("should return { id: '4', name: 'Mark Zuckerberg' } object", function (done) { 99 | nock('https://graph.facebook.com:443') 100 | .get('/4?fields=name') 101 | .reply(200, "{\"name\":\"Mark Zuckerberg\",\"id\":\"4\"}", { 102 | 'content-type': 'text/javascript; charset=UTF-8', 103 | 'content-length': '10' }); 104 | 105 | FB.api('4?fields=name', function (result) { 106 | result.should.include({id: '4', name: 'Mark Zuckerberg'}); 107 | done(); 108 | }); 109 | }); 110 | }); 111 | 112 | describe.skip("FB.api('/4?fields=name', { fields: 'id,first_name' }, cb)", function () { 113 | it("should return { id: '4', name: 'Mark Zuckerberg' } object", function (done) { 114 | FB.api('4?fields=name', { fields: 'id,first_name' }, function (result) { 115 | result.should.include({id: '4', name: 'Mark Zuckerberg'}); 116 | done(); 117 | }); 118 | }); 119 | }); 120 | 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /samples/scrumptious/views/menu.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Scrumptious 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 | ScrumptiousScrumptious 35 |
36 |
37 |

To get started, login using Facebook

38 |
39 | 42 |
43 | 44 | 45 | 78 | 79 |
80 |
81 | Back 82 |

Select a Meal

83 |
84 | 85 |
86 |
    87 |
    88 |
    89 | 90 |
    91 |
    92 | Back 93 |

    Meal

    94 |
    95 | 96 |
    97 |
    98 |
    99 | Select 100 |
    101 |
    102 |
    103 | 104 |
    105 |
    106 | Back 107 |

    Nearby

    108 | Done 109 |
    110 | 111 |
    112 |
      113 |
      114 |
      115 | 116 |
      117 |
      118 | Back 119 |

      Select Friends

      120 | Done 121 |
      122 | 123 |
      124 |
        125 |
        126 |
        127 | 128 | 129 | 134 | 135 | 141 | 142 | 147 | 148 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /samples/scrumptious/public/js/main.js: -------------------------------------------------------------------------------- 1 | var DEBUG_MODE = false; 2 | 3 | var selectedMealIndex = -1; 4 | var selectedPlaceIndex = -1; 5 | var selectedPlaceID = null; 6 | var nearbyPlaces = null; 7 | var myFriends = null; 8 | var currentlySelectedPlaceElement = null; 9 | var selectedFriends = {}; 10 | 11 | // DATA 12 | 13 | var meals = [ 14 | { 15 | "id" : "cheeseburger", 16 | "title" : "Cheeseburger", 17 | "url" : "http://nodescrumptious.azurewebsites.net/meals/cheeseburger" 18 | }, 19 | { 20 | "id" : "chinese", 21 | "title" : "Chinese", 22 | "url" : "http://nodescrumptious.azurewebsites.net/meals/chinese" 23 | }, 24 | { 25 | "id" : "french", 26 | "title" : "French", 27 | "url" : "http://nodescrumptious.azurewebsites.net/meals/french" 28 | }, 29 | { 30 | "id" : "hotdog", 31 | "title" : "Hot Dog", 32 | "url" : "http://nodescrumptious.azurewebsites.net/meals/hotdog" 33 | }, 34 | { 35 | "id" : "indian", 36 | "title" : "Indian", 37 | "url" : "http://nodescrumptious.azurewebsites.net/meals/indian" 38 | }, 39 | { 40 | "id" : "italian", 41 | "title" : "Italian", 42 | "url" : "http://nodescrumptious.azurewebsites.net/meals/italian" 43 | }, 44 | { 45 | "id" : "pizza", 46 | "title" : "Pizza", 47 | "url" : "http://nodescrumptious.azurewebsites.net/meals/pizza" 48 | } 49 | ]; 50 | 51 | // UTILITIES 52 | 53 | // For logging responses 54 | function logResponse(response) { 55 | if (typeof console !== 'undefined') 56 | console.log('The response was', response); 57 | } 58 | 59 | // DOCUMENT-READY FUNCTIONS 60 | $(function () { 61 | 62 | // Click handlers 63 | 64 | // Logout click handler 65 | $("#logout").click(function() { 66 | window.location = '/logout'; 67 | return false; 68 | }); 69 | 70 | // Announce click handler 71 | $("#announce").click(function() { 72 | publishOGAction(null); 73 | }); 74 | 75 | // Meal selection click handler 76 | $('#meal-list').on('click', 'li', function() { 77 | selectedMealIndex = $(this).index(); 78 | logResponse("Link in meal listview clicked... " + selectedMealIndex); 79 | displaySelectedMeal(); 80 | }); 81 | 82 | $('#detail-meal-select').click(function() { 83 | //logResponse("Meal selected"); 84 | $('#announce').removeClass('ui-disabled'); 85 | $('#select-meal').html(meals[selectedMealIndex].title); 86 | }); 87 | 88 | // Place selection click handler 89 | $('#places-list').on('click', 'li', function() { 90 | var selectionId = $(this).attr('data-name'); 91 | logResponse("Selected place " + selectionId); 92 | 93 | var selectionStatus = $(this).attr('data-icon'); 94 | if (selectionStatus == "false") { 95 | // De-select any previously selected place 96 | if (currentlySelectedPlaceElement) { 97 | currentlySelectedPlaceElement.buttonMarkup({ icon: false }); 98 | } 99 | // Place has been selected. 100 | $(this).buttonMarkup({ icon: "check" }); 101 | // Set the selected place info 102 | selectedPlaceID = selectionId; 103 | selectedPlaceIndex = $(this).index(); 104 | $('#select-location').html(nearbyPlaces[selectedPlaceIndex].name); 105 | // Set the currently selected place element 106 | currentlySelectedPlaceElement = $(this); 107 | } else { 108 | // Previously selected place has been deselected 109 | $(this).buttonMarkup({ icon: false }); 110 | // Reset the selected place info 111 | selectedPlaceID = null; 112 | selectedPlaceIndex = -1; 113 | $('#select-location').html("Select one"); 114 | } 115 | }); 116 | 117 | // Friend selection click handler 118 | $('#friends-list').on('click', 'li', function() { 119 | var selectionId = $(this).attr('data-name'); 120 | logResponse("Selected friend " + selectionId); 121 | var selectedIndex = $(this).index(); 122 | var selectionStatus = $(this).attr('data-icon'); 123 | if (selectionStatus == "false") { 124 | // Friend has been selected. 125 | $(this).buttonMarkup({ icon: "check" }); 126 | // Add to friend ID to selectedFriends associative array 127 | selectedFriends[selectionId] = myFriends[selectedIndex].name; 128 | } else { 129 | // Previously selected friend has been deselected 130 | $(this).buttonMarkup({ icon: false }); 131 | // Remove the friend id 132 | delete selectedFriends[selectionId]; 133 | } 134 | var friendNameArray = []; 135 | for (var friendId in selectedFriends) { 136 | if (selectedFriends.hasOwnProperty(friendId)) { 137 | friendNameArray.push(selectedFriends[friendId]); 138 | } 139 | } 140 | 141 | if (friendNameArray.length > 2) { 142 | var otherFriends = friendNameArray.length - 1; 143 | $('#select-friends').html(friendNameArray[0] + " and " + otherFriends + " others"); 144 | } else if (friendNameArray.length == 2) { 145 | $('#select-friends').html(friendNameArray[0] + " and " + friendNameArray[1]); 146 | } else if (friendNameArray.length == 1) { 147 | $('#select-friends').html(friendNameArray[0]); 148 | } else { 149 | $('#select-friends').html("Select friends"); 150 | } 151 | 152 | logResponse("Current select friends list: " + selectedFriends); 153 | }); 154 | 155 | }); 156 | 157 | $( document ).delegate("#meals", "pageinit", function() { 158 | displayMealList(); 159 | }); 160 | 161 | $('body').bind('hideOpenMenus', function(){ 162 | $("ul:jqmData(role='menu')").find('li > ul').hide(); 163 | }); 164 | 165 | var menuHandler = function(e) { 166 | $('body').trigger('hideOpenMenus'); 167 | $(this).find('li > ul').show(); 168 | e.stopPropagation(); 169 | }; 170 | $("ul:jqmData(role='menu') li > ul li").click(function(e) { 171 | $('body').trigger('hideOpenMenus'); 172 | e.stopPropagation(); 173 | }); 174 | $('body').delegate("ul:jqmData(role='menu')",'click',menuHandler); 175 | $('body').click(function(e){ 176 | $('body').trigger('hideOpenMenus'); 177 | }); 178 | 179 | // AUTHENTICATION 180 | 181 | // Handle status changes 182 | function handleStatusChange(response) { 183 | if (response.authResponse) { 184 | logResponse(response); 185 | window.location.hash = '#menu'; 186 | updateUserInfo(response); 187 | } else { 188 | window.location.hash = '#login'; 189 | } 190 | } 191 | 192 | function updateUserInfo(response) { 193 | FB.api('/me', 194 | {fields:"name,first_name,picture"}, 195 | function(response) { 196 | logResponse(response); 197 | var output = ''; 198 | output += ''; 199 | output += ' ' + response.first_name; 200 | $('#user-identity').html(output); 201 | }); 202 | } 203 | 204 | 205 | // GRAPH API (OPEN GRAPH) 206 | function handleOGSuccess() { 207 | logResponse("[handleOGSuccess] done."); 208 | showPublishConfirmation(); 209 | 210 | // Clear out selections 211 | selectedMealIndex = -1; 212 | selectedPlaceIndex = -1; 213 | selectedPlaceID = null; 214 | currentlySelectedPlaceElement = null; 215 | selectedFriends = {}; 216 | // Reset the placeholders 217 | $('#select-meal').html("Select one"); 218 | $('#select-location').html("Select one"); 219 | $('#select-friends').html("Select friends"); 220 | // Disable the announce button 221 | $('#announce').addClass('ui-disabled'); 222 | 223 | } 224 | 225 | function handleGenericError(e) { 226 | logResponse("Error ..."+JSON.stringify(e)); 227 | } 228 | 229 | function handlePublishOGError(e) { 230 | logResponse("Error publishing ..."+JSON.stringify(e)); 231 | var errorCode = e.code; 232 | logResponse("Error code ..."+errorCode); 233 | if (errorCode == "200") { 234 | // Request publish actions, probably missing piece here 235 | reauthorizeForPublishPermissions(); 236 | } 237 | } 238 | 239 | function reauthorizeForPublishPermissions() { 240 | logResponse("[reauthorizeForPublishPermissions] asking for additional permissions."); 241 | // If successful, try publishing action again 242 | // else, just show error 243 | FB.login( 244 | function (response) { 245 | if (!response || response.error) { 246 | handleGenericError(response.error); 247 | } else { 248 | publishOGAction(response); 249 | } 250 | }, {scope:'publish_actions'} 251 | ); 252 | } 253 | 254 | function publishOGAction(response) { 255 | var errorHandler = null; 256 | // Handle if we came in via a reauth. 257 | // Also avoid loops, set generic error 258 | // handler if already reauthed. 259 | if (!response || response.error) { 260 | errorHandler = handlePublishOGError; 261 | } else { 262 | errorHandler = handleGenericError; 263 | } 264 | logResponse("Publishing action..."); 265 | var params = { 266 | "meal" : meals[selectedMealIndex].url 267 | }; 268 | if (selectedPlaceID) { 269 | params.place = selectedPlaceID; 270 | } 271 | var friendIDArrays = []; 272 | for (var friendId in selectedFriends) { 273 | if (selectedFriends.hasOwnProperty(friendId)) { 274 | friendIDArrays.push(friendId); 275 | } 276 | } 277 | if (friendIDArrays.length > 0) { 278 | params.tags = friendIDArrays.join(); 279 | } 280 | logResponse("Publish params " + JSON.stringify(params)); 281 | 282 | $.ajax({ 283 | url: '/announce', 284 | type: 'post', 285 | data: params 286 | }).success(function(result) { 287 | handleOGSuccess(result); 288 | }).error(function (xhr) { 289 | try{ 290 | var response = JSON.parse(xhr.responseText); 291 | if(response.error && response.redirectUri) { 292 | window.location = response.redirectUri; 293 | } 294 | } catch(err) { 295 | } 296 | // todo: handle error 297 | }); 298 | // FB.api(";", 299 | // "POST", 300 | // params, 301 | // function (response) { 302 | // logResponse(response); 303 | // if (!response || response.error) { 304 | // errorHandler(response.error); 305 | // } else { 306 | // handleOGSuccess(response); 307 | // } 308 | // } 309 | // ); 310 | } 311 | 312 | function showPublishConfirmation() { 313 | $("#confirmation").append("

        Publish successful

        "); 314 | // Fade out the message 315 | $("#confirmation").fadeOut(3000, function() { 316 | $("#confirmation").html(""); 317 | $("#confirmation").show(); 318 | }); 319 | } 320 | 321 | // DATA FETCH AND DISPLAY 322 | 323 | // Meals 324 | function displayMealList() { 325 | // Meal list 326 | logResponse("[displayMealList] displaying meal list."); 327 | var tmpl = $("#meal_list_tmpl").html(); 328 | var output = Mustache.to_html(tmpl, meals); 329 | $("#meal-list").html(output).listview('refresh'); 330 | } 331 | 332 | function displaySelectedMeal() { 333 | logResponse("[displaySelectedMeal] displaying selected meal."); 334 | var meal = meals[selectedMealIndex]; 335 | // Set up meal display 336 | var tmpl = $("#selected_meal_tmpl").html(); 337 | var output = Mustache.to_html(tmpl, meal); 338 | $("#selected_meal").html(output); 339 | } 340 | 341 | // Nearby Places 342 | function getNearby() { 343 | // Check for and use cached data 344 | if (nearbyPlaces) 345 | return; 346 | 347 | logResponse("[getNearby] get nearby data."); 348 | 349 | // First use browser's geolocation API to obtain location 350 | navigator.geolocation.getCurrentPosition(function(location) { 351 | //curLocation = location; 352 | logResponse(location); 353 | 354 | $.ajax({ 355 | url: '/search', 356 | data: { 357 | type: 'place', 358 | q: 'restaurant', 359 | center: location.coords.latitude + ',' + location.coords.longitude, 360 | distance: 1000, 361 | fields: 'id,name,picture' 362 | } 363 | }).success(function (response) { 364 | nearbyPlaces = response.data; 365 | logResponse(nearbyPlaces); 366 | displayPlaces(nearbyPlaces); 367 | }).error(function(err) { 368 | logResponse("Error fetching nearby place data."); 369 | }); 370 | }); 371 | } 372 | 373 | function displayPlaces(places) { 374 | // Places list 375 | logResponse("[displayPlaces] displaying nearby list."); 376 | var tmpl = $("#places_list_tmpl").html(); 377 | var output = Mustache.to_html(tmpl, places); 378 | $("#places-list").html(output).listview('refresh'); 379 | } 380 | 381 | // Friends 382 | function getFriends() { 383 | // Check for and use cached data 384 | if (myFriends) 385 | return; 386 | 387 | logResponse("[getFriends] get friend data."); 388 | 389 | $.ajax({ 390 | url: '/friends' 391 | }).success(function (response) { 392 | myFriends = response.data; 393 | logResponse(myFriends); 394 | displayFriends(myFriends); 395 | }).error(function (err) { 396 | logResponse("Error fetching friend data."); 397 | }); 398 | } 399 | 400 | function displayFriends(friends) { 401 | // Friends list 402 | logResponse("[displayFriends] displaying friend list."); 403 | var tmpl = $("#friends_list_tmpl").html(); 404 | var output = Mustache.to_html(tmpl, friends); 405 | $("#friends-list").html(output).listview('refresh'); 406 | } 407 | -------------------------------------------------------------------------------- /samples/scrumptious/public/css/base.css: -------------------------------------------------------------------------------- 1 | body { background: #fff; padding:13px; } 2 | body.ui-mobile-viewport { padding: 0; } 3 | body, input, textarea, select, button { font-family: Helvetica, Arial, san-serif; } 4 | .ui-mobile .type-home { background: #2d4681; } 5 | .ui-mobile .type-home .ui-content { margin: 0; text-align: center; } 6 | .ui-mobile .type-home .login-button { text-align: center;} 7 | .type-home .ui-content { 8 | margin-top: 5px; 9 | overflow: hidden; 10 | position: relative; 11 | } 12 | .ui-mobile #login-header { padding: 5px 5px 0; margin: 0 auto; } 13 | #login-header img { width: 72px; height: 72px; margin-top:10px; } 14 | img { max-width: 100%; } 15 | 16 | span.topnav-app { 17 | font-size: 1.1em; 18 | font-family: Helvetica, Arial, san-serif; 19 | color: #ffffff; 20 | font-weight: normal; 21 | padding-left: 5px; 22 | padding-right: 10px; 23 | margin-bottom: 60px; 24 | } 25 | 26 | .ui-mobile #user-identity { 27 | font-size: 0.8em; 28 | font-family: Helvetica, Arial, san-serif; 29 | color: #ffffff; 30 | font-weight: normal; 31 | float: right; 32 | padding-right: 25px; 33 | } 34 | 35 | .ui-mobile #menu-anchor { 36 | margin-top: 0px; 37 | float: right; 38 | } 39 | 40 | span.tagline { 41 | font-size: 1.5em; 42 | font-family: Zapfino, Helvetica, Arial, san-serif; 43 | color: #99ccff; 44 | text-shadow: 0px 0px #fff; 45 | background: none; 46 | padding-left: 10px; 47 | vertical-align: text-bottom; 48 | } 49 | 50 | p.intro { 51 | font-size: 1.1em; 52 | line-height: 1.3; 53 | color: #ccc; 54 | text-shadow: 0px 0px #ccc; 55 | border-bottom: 0; 56 | background: none; 57 | text-align: center; 58 | margin: 1.5em 0; 59 | padding: 1.5em 15px 0; 60 | } 61 | 62 | .ui-mobile .type-menu { background: #fff; } 63 | .ui-mobile .type-menu .ui-content { margin: 0; text-align: center; } 64 | .type-menu .ui-content { 65 | margin-top: 5px; 66 | overflow: hidden; 67 | position: relative; 68 | } 69 | .ui-mobile .type-menu .ui-content .ui-listview li a img { 70 | width: 60px; 71 | height: 60px; 72 | margin-top: 10px; 73 | padding-right: 0px; 74 | } 75 | 76 | .ui-mobile .type-menu .ui-content .ui-listview li a p { 77 | font-size: 0.8em; 78 | color: #99ccff; 79 | } 80 | 81 | .ui-mobile .type-menu .ui-content .ui-listview li a h3 { 82 | margin-left: -20px; 83 | } 84 | 85 | .ui-mobile .type-menu .ui-content .ui-listview li a p { 86 | margin-left: -20px; 87 | } 88 | 89 | .ui-mobile .type-menu .submit-button { 90 | margin-left: 20px; 91 | margin-right: 20px; 92 | } 93 | 94 | .ui-mobile .type-menu-drilldown .ui-content .ui-listview li a img { 95 | width: 57px; 96 | height: 57px; 97 | margin-top: 10px; 98 | padding-left: 10px; 99 | } 100 | 101 | .ui-mobile #selected_meal { 102 | margin-left:auto; 103 | margin-right:auto; 104 | width:50%; 105 | } 106 | 107 | .ui-mobile #confirmation { 108 | margin-left:auto; 109 | margin-right:auto; 110 | width:50%; 111 | } 112 | 113 | .type-home .ui-content .jqm-version { 114 | display: block; 115 | position: absolute; 116 | width: 96px; 117 | border: solid #fff; 118 | border-width: 2px 1px; 119 | padding: .25em 2.25em; 120 | margin: 0 15px 0 0; 121 | right: 0; 122 | top: -2px; 123 | background: #f7cf57; 124 | color: #000; 125 | font-size: .8em; 126 | font-weight: bold; 127 | text-align: center; 128 | text-shadow: 0 1px 1px #fff; 129 | z-index: 9; 130 | -webkit-transform: rotate(45deg) translate(4.8em,-1em); 131 | -moz-transform: rotate(45deg) translate(4.8em,-1em); 132 | -ms-transform: rotate(45deg) translate(4.8em,-1em); 133 | -o-transform: rotate(45deg) translate(4.8em,-1em); 134 | transform: rotate(45deg); 135 | -webkit-box-shadow: 0 0 6px rgba(0,0,0,.40); 136 | -moz-box-shadow: 0 0 6px rgba(0,0,0,.40); 137 | -o-box-shadow: 0 0 6px rgba(0,0,0,.40); 138 | box-shadow: 0 0 6px rgba(0,0,0,.40); 139 | } 140 | .type-home .ui-content .jqm-version b { 141 | color: #a21412; 142 | font-weight: bold; 143 | } 144 | 145 | .footer-docs { 146 | padding: 5px 0; 147 | clear:both; 148 | color:#666; 149 | } 150 | .footer-docs p { 151 | font-weight: normal; 152 | font-size: .9em; 153 | } 154 | .ui-mobile-viewport .footer-docs p { 155 | margin: .5em 15px; 156 | } 157 | .ui-mobile-viewport .footer-docs p.jqm-version { 158 | font-weight: bold; 159 | } 160 | @media all and (min-width:650px) { 161 | .ui-mobile-viewport .footer-docs { 162 | overflow: hidden; 163 | } 164 | .ui-mobile-viewport .footer-docs p { 165 | margin: 1em 15px; 166 | float: left; 167 | } 168 | .ui-mobile-viewport .footer-docs p.jqm-version { 169 | float: right; 170 | font-weight: normal; 171 | } 172 | } 173 | 174 | h2 { margin:1.2em 0 .4em 0; } 175 | p code { font-size:1.2em; font-weight:bold; } 176 | h4 code {font-size:1.2em; font-weight:bold; } 177 | 178 | dt { font-weight: bold; margin: 2em 0 .5em; } 179 | dt code, dd code { font-size:1.3em; line-height:150%; } 180 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } 181 | strong { font-weight: bold; } 182 | 183 | /* fluid images moved from jquery.mobile.core.css*/ 184 | .ui-mobile img { 185 | max-width: 100%; 186 | } 187 | 188 | .ui-header .jqm-home { top: 0; } 189 | nav { margin: 0; } 190 | 191 | .type-interior .content-secondary { 192 | border-right: 0; 193 | border-left: 0; 194 | margin: 10px -15px 0; 195 | background: #fff; 196 | border-top: 1px solid #ccc; 197 | } 198 | .type-interior .ui-content { 199 | padding-bottom: 0; 200 | } 201 | .content-secondary .ui-collapsible { 202 | padding: 10px 15px; 203 | } 204 | .content-secondary .ui-collapsible-inset { 205 | margin: 0; 206 | } 207 | .content-secondary .ui-collapsible-content { 208 | padding: 0; 209 | background: none; 210 | } 211 | .content-secondary .ui-listview { 212 | margin: 0; 213 | } 214 | /* new API additions */ 215 | 216 | dt { 217 | margin: 35px 0 15px 0; 218 | background-color:#ddd; 219 | font-weight:normal; 220 | } 221 | dt code { 222 | display:inline-block; 223 | font-weight:bold; 224 | color:#56A00E; 225 | padding:3px 7px; 226 | margin-right:10px; 227 | background-color:#fff; 228 | } 229 | dd { 230 | margin-bottom:10px; 231 | } 232 | dd .default { font-weight:bold; } 233 | dd pre { 234 | margin:0 0 0 0; 235 | } 236 | dd code { font-weight: normal; } 237 | dd pre code { 238 | margin:0; 239 | border:none; 240 | font-weight:normal; 241 | font-size:100%; 242 | background-color:transparent; 243 | } 244 | dd h4 { margin:15px 0 0 0; } 245 | 246 | .localnav { 247 | margin:0 0 20px 0; 248 | } 249 | .ui-mobile-viewport .localnav li { 250 | float:left; 251 | } 252 | .localnav .ui-btn-inner { 253 | padding: .6em 10px; 254 | font-size:80%; 255 | } 256 | 257 | /* custom dialog for the photos sharing */ 258 | .ui-dialog.dialog-actionsheet .ui-dialog-contain { 259 | margin-top: 0; 260 | } 261 | 262 | /* popup examples */ 263 | .type-interior .ui-popup-container .ui-content { padding: 15px !important; } 264 | 265 | /* F bar theme - just for the docs overview headers */ 266 | .ui-bar-f { 267 | border: 1px solid #56A00E; 268 | background: #74b042; 269 | color: #fff; 270 | font-weight: bold; 271 | text-shadow: 0 1px 1px #335413; 272 | background-image: -webkit-gradient(linear, left top, left bottom, from(#74b042), to(#56A00E)); /* Saf4+, Chrome */ 273 | background-image: -webkit-linear-gradient(#74b042, #56A00E); /* Chrome 10+, Saf5.1+ */ 274 | background-image: -moz-linear-gradient(#74b042, #56A00E); /* FF3.6 */ 275 | background-image: -ms-linear-gradient(#74b042, #56A00E); /* IE10 */ 276 | background-image: -o-linear-gradient(#74b042, #56A00E); /* Opera 11.10+ */ 277 | background-image: linear-gradient(#74b042, #56A00E); 278 | } 279 | .ui-bar-f, 280 | .ui-bar-f input, 281 | .ui-bar-f select, 282 | .ui-bar-f textarea, 283 | .ui-bar-f button { 284 | font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/; 285 | } 286 | 287 | .ui-bar-f, 288 | .ui-bar-f .ui-link-inherit { 289 | color: #fff; 290 | } 291 | .ui-bar-f .ui-link { 292 | color: #fff; 293 | font-weight: bold; 294 | } 295 | .ui-btn-up-f { 296 | border: 1px solid #3B6F07; 297 | background: #56A00E; 298 | font-weight: bold; 299 | color: #fff; 300 | text-shadow: 0 1px 1px #234403; 301 | background-image: -webkit-gradient(linear, left top, left bottom, from(#74b042), to(#56A00E)); /* Saf4+, Chrome */ 302 | background-image: -webkit-linear-gradient(#74b042, #56A00E); /* Chrome 10+, Saf5.1+ */ 303 | background-image: -moz-linear-gradient(#74b042, #56A00E); /* FF3.6 */ 304 | background-image: -ms-linear-gradient(#74b042, #56A00E); /* IE10 */ 305 | background-image: -o-linear-gradient(#74b042, #56A00E); /* Opera 11.10+ */ 306 | background-image: linear-gradient(#74b042, #56A00E); 307 | } 308 | .ui-btn-up-f a.ui-link-inherit { 309 | color: #fff; 310 | } 311 | .ui-btn-hover-f { 312 | border: 1px solid #3B6F07; 313 | background: #6EBC1F; 314 | font-weight: bold; 315 | color: #fff; 316 | text-shadow: 0 1px 1px #234403; 317 | background-image: -webkit-gradient(linear, left top, left bottom, from(#8FC963), to(#6EBC1F)); /* Saf4+, Chrome */ 318 | background-image: -webkit-linear-gradient(#8FC963, #6EBC1F); /* Chrome 10+, Saf5.1+ */ 319 | background-image: -moz-linear-gradient(#8FC963, #6EBC1F); /* FF3.6 */ 320 | background-image: -ms-linear-gradient(#8FC963, #6EBC1F); /* IE10 */ 321 | background-image: -o-linear-gradient(#8FC963, #6EBC1F); /* Opera 11.10+ */ 322 | background-image: linear-gradient(#8FC963, #6EBC1F); 323 | } 324 | .ui-btn-hover-f a.ui-link-inherit { 325 | color: #fff; 326 | } 327 | .ui-btn-down-f { 328 | border: 1px solid #3B6F07; 329 | background: #3d3d3d; 330 | font-weight: bold; 331 | color: #fff; 332 | text-shadow: 0 1px 1px #234403; 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#56A00E), to(#64A234)); /* Saf4+, Chrome */ 334 | background-image: -webkit-linear-gradient(#56A00E, #64A234); /* Chrome 10+, Saf5.1+ */ 335 | background-image: -moz-linear-gradient(#56A00E, #64A234); /* FF3.6 */ 336 | background-image: -ms-linear-gradient(#56A00E, #64A234); /* IE10 */ 337 | background-image: -o-linear-gradient(#56A00E, #64A234); /* Opera 11.10+ */ 338 | background-image: linear-gradient(#56A00E, #64A234); 339 | } 340 | .ui-btn-down-f a.ui-link-inherit { 341 | color: #fff; 342 | } 343 | .ui-btn-up-f, 344 | .ui-btn-hover-f, 345 | .ui-btn-down-f { 346 | font-family: Helvetica, Arial, sans-serif; 347 | text-decoration: none; 348 | } 349 | 350 | 351 | /* docs site layout */ 352 | 353 | @media all and (min-width: 650px){ 354 | 355 | .type-home .ui-content { 356 | margin-top: 5px; 357 | } 358 | .ui-mobile #login-header { 359 | max-width: 340px; 360 | } 361 | p.intro { 362 | margin: 2em 0; 363 | } 364 | .type-home .ui-content, 365 | .type-interior > .ui-content { 366 | padding: 0; 367 | } 368 | .type-interior > .ui-content { 369 | overflow: hidden; 370 | } 371 | .content-secondary { 372 | text-align: left; 373 | float: left; 374 | width: 45%; 375 | } 376 | .content-secondary, 377 | .type-interior .content-secondary { 378 | margin: 30px 0 20px 2%; 379 | padding: 20px 4% 0 0; 380 | background: none; 381 | border-top: none; 382 | position: relative; 383 | } 384 | .type-interior .content-secondary::after { 385 | content: ""; 386 | display: inline-block; 387 | position: absolute; 388 | top: 0; 389 | right: 0; 390 | height: 1px; 391 | width: 1px; 392 | margin-bottom: -9999px; 393 | padding-bottom: 9999px; 394 | background: #bbb; 395 | } 396 | .type-index .content-secondary { 397 | padding: 0; 398 | } 399 | .content-secondary .ui-collapsible { 400 | margin: 0; 401 | padding: 0; 402 | } 403 | .content-secondary .ui-collapsible-content { 404 | border-left: none; 405 | border-right: none; 406 | } 407 | .type-index .content-secondary .ui-listview { 408 | margin: 0; 409 | } 410 | .ui-mobile #login-header { 411 | padding: 0; 412 | } 413 | .content-primary { 414 | width: 45%; 415 | float: right; 416 | margin-top: 30px; 417 | margin-right: 1%; 418 | padding-right: 1%; 419 | } 420 | .content-primary ul:first-child { 421 | margin-top: 0; 422 | } 423 | /* collapsible non-inset example */ 424 | .content-primary .ui-collapsible-content ul:first-child { 425 | margin-top: -10px; 426 | } 427 | .content-secondary h2 { 428 | position: absolute; 429 | left: -9999px; 430 | } 431 | .type-interior .content-primary { 432 | padding: 1.5em 6% 3em 0; 433 | margin: 0; 434 | } 435 | /* fix up the collapsibles - expanded on desktop */ 436 | .content-secondary .ui-collapsible-inset { 437 | margin-bottom: -1px; 438 | } 439 | .content-secondary .ui-collapsible-heading { 440 | display: none; 441 | } 442 | .content-secondary .ui-collapsible-contain { 443 | margin:0; 444 | } 445 | .content-secondary .ui-collapsible-content { 446 | display: block; 447 | margin: 0; 448 | padding: 0; 449 | } 450 | .content-secondary .ui-collapsible-content, 451 | .content-secondary .ui-collapsible-content > .ui-listview .ui-li-last { 452 | border-bottom-left-radius: 0; 453 | border-bottom-right-radius: 0; 454 | } 455 | .type-interior .content-secondary .ui-li-divider { 456 | padding-top: 1em; 457 | padding-bottom: 1em; 458 | } 459 | .type-interior .content-secondary { 460 | margin: 0; 461 | padding: 0; 462 | } 463 | 464 | } 465 | @media all and (min-width: 750px){ 466 | .type-home .ui-content { 467 | background-position: 39%; 468 | } 469 | .content-secondary { 470 | width: 34%; 471 | } 472 | .content-primary { 473 | width: 56%; 474 | padding-right: 1%; 475 | } 476 | } 477 | 478 | @media all and (min-width: 1200px){ 479 | .type-home .ui-content{ 480 | background-position: 38.5%; 481 | } 482 | .content-secondary { 483 | width: 30%; 484 | padding-right:6%; 485 | margin: 30px 0 20px 5%; 486 | } 487 | .type-interior .content-secondary { 488 | margin: 0; 489 | padding: 0; 490 | } 491 | .content-primary { 492 | width: 50%; 493 | margin-right: 5%; 494 | padding-right: 3%; 495 | } 496 | .type-interior .content-primary { 497 | width: 60%; 498 | } 499 | } 500 | 501 | /* reset for popup examples */ 502 | .type-interior .ui-popup.ui-content, 503 | .type-interior .ui-popup .ui-content { 504 | background-position: 0 0; 505 | background-repeat: no-repeat; 506 | overflow: visible; 507 | } 508 | 509 | /* tables documenting enum-type settings */ 510 | .enum-value { 511 | vertical-align: top; 512 | white-space: nowrap; 513 | } 514 | 515 | /* Navbar examples */ 516 | .content-primary > .ui-header, 517 | .content-primary > .ui-footer { 518 | overflow: hidden; 519 | } 520 | 521 | /* list styles */ 522 | ul { margin:0; padding-left:0; } 523 | ul li { list-style:none; padding:.7em 0; margin:0; border-bottom:1px solid #e3e3e3; } 524 | ul ul li { border: none;} 525 | 526 | dl dd ul { padding-left: 40px; } 527 | dl dd ul li { list-style: disc; } 528 | dl dd ul li li { list-style: circle; } 529 | dl dd ul li li li { list-style: square; } 530 | dl dd ul li { border: none; } 531 | 532 | .ui-mobile-viewport .localnav li { 533 | border: none; 534 | } 535 | 536 | .fb-login-button { 537 | background: url(images/fblogin.png); 538 | -moz-background-size: 776px 18px; 539 | -o-background-size: 776px 18px; 540 | -webkit-background-size: 776px 18px; 541 | background-size: 776px 18px; 542 | } -------------------------------------------------------------------------------- /samples/scrumptious/public/js/mustache.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mustache.js - Logic-less {{mustache}} templates with JavaScript 3 | * http://github.com/janl/mustache.js 4 | */ 5 | 6 | /*global define: false*/ 7 | 8 | (function (root, factory) { 9 | if (typeof exports === "object" && exports) { 10 | module.exports = factory; // CommonJS 11 | } else if (typeof define === "function" && define.amd) { 12 | define(factory); // AMD 13 | } else { 14 | root.Mustache = factory; //