├── assets ├── js │ └── .gitkeep ├── styles │ ├── .gitkeep │ └── style.less ├── templates │ └── .gitkeep └── mixins │ └── reset.css ├── api ├── adapters │ └── .gitkeep ├── models │ ├── .gitkeep │ └── Players.js ├── services │ └── .gitkeep ├── controllers │ ├── .gitkeep │ ├── PlayersController.js │ └── ExampleController.js └── policies │ └── authenticated.js ├── public ├── images │ └── .gitkeep ├── favicon.ico ├── robots.txt └── js │ ├── knockout.mapping-latest.js │ └── underscore.min.js ├── SampleAppiOS ├── sampleApp │ ├── api │ │ ├── models │ │ │ ├── .gitkeep │ │ │ ├── Messages.js │ │ │ └── User.js │ │ ├── adapters │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ ├── UserController.js │ │ │ └── MessagesController.js │ │ ├── services │ │ │ └── .gitkeep │ │ └── policies │ │ │ └── authenticated.js │ ├── assets │ │ ├── js │ │ │ ├── .gitkeep │ │ │ ├── app.js │ │ │ └── sails.io.js │ │ ├── images │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── .gitkeep │ │ ├── favicon.ico │ │ └── robots.txt │ ├── README.md │ ├── config │ │ ├── locales │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ └── _README.md │ │ ├── routes.js │ │ ├── session.js │ │ ├── i18n.js │ │ ├── bootstrap.js │ │ ├── 404.js │ │ ├── log.js │ │ ├── adapters.js │ │ ├── csrf.js │ │ ├── controllers.js │ │ ├── 500.js │ │ ├── views.js │ │ ├── policies.js │ │ └── sockets.js │ ├── app.js │ ├── package.json │ ├── .gitignore │ ├── views │ │ ├── layout.ejs │ │ ├── 404.ejs │ │ └── home │ │ │ └── index.ejs │ └── Gruntfile.js ├── SampleAppiOS │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ └── MainStoryboard.storyboard │ ├── Default.png │ ├── Default@2x.png │ ├── Default-568h@2x.png │ ├── ViewController.h │ ├── AppDelegate.h │ ├── CustomCollectionViewCell.m │ ├── SampleAppiOS-Prefix.pch │ ├── main.m │ ├── CustomCollectionViewCell.h │ ├── NSData+SRB64Additions.h │ ├── SocketIOJSONSerialization.h │ ├── SocketIOTransportWebsocket.h │ ├── SocketIOTransportXHR.h │ ├── base64.h │ ├── LICENSE │ ├── NSData+SRB64Additions.m │ ├── SampleAppiOS-Info.plist │ ├── SocketIOTransport.h │ ├── SocketIOPacket.h │ ├── AppDelegate.m │ ├── SocketIOPacket.m │ ├── SocketIOJSONSerialization.m │ ├── SocketIOTransportWebsocket.m │ ├── SRWebSocket.h │ ├── SocketIO.h │ ├── README.md │ ├── ViewController.m │ ├── SocketIOTransportXHR.m │ └── base64.c ├── SampleAppiOS.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── rfatahi.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── rfatahi.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── SampleAppiOS.xcscheme └── README.md ├── config ├── views.js ├── locales │ └── english.js ├── assets.js ├── local.ex.js ├── session.js ├── policies.js ├── application.js ├── bootstrap.js ├── adapters.js └── routes.js ├── README.md ├── app.js ├── package.json ├── views ├── home │ └── index.ejs ├── layout.ejs ├── example │ ├── jQuery │ │ └── index.ejs │ ├── angular.ejs │ ├── knockout.ejs │ ├── ember │ │ └── index.ejs │ └── backbone │ │ └── index.ejs ├── 404.ejs └── 500.ejs └── .gitignore /assets/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/adapters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/services/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/adapters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/services/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/views.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | viewEngine: 'ejs' 3 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/README.md: -------------------------------------------------------------------------------- 1 | # sampleApp 2 | ### a Sails application -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hello": "Sup" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FOR MORE UP-TO-DATE EXAMPLES, CHECK OUT https://sailsjs.com/support 2 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hello": "Hola" 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // Start sails and pass it command line arguments 2 | require('sails').lift(require('optimist').argv); 3 | -------------------------------------------------------------------------------- /config/locales/english.js: -------------------------------------------------------------------------------- 1 | /* 2 | * English string set 3 | * 4 | */ 5 | var english = { 6 | 7 | }; 8 | module.exports = english; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/SampleAppiOS/SampleAppiOS/Default.png -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/app.js: -------------------------------------------------------------------------------- 1 | // Start sails and pass it command line arguments 2 | require('sails').lift(require('optimist').argv); 3 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/SampleAppiOS/SampleAppiOS/Default@2x.png -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/routes.js: -------------------------------------------------------------------------------- 1 | module.exports.routes = { 2 | 3 | '/': { 4 | view: 'home/index' 5 | } 6 | 7 | }; 8 | 9 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/SampleAppiOS/sampleApp/assets/favicon.ico -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/SampleAppiOS/SampleAppiOS/Default-568h@2x.png -------------------------------------------------------------------------------- /api/controllers/PlayersController.js: -------------------------------------------------------------------------------- 1 | /*--------------------- 2 | :: Players 3 | -> controller 4 | ---------------------*/ 5 | var PlayersController = { 6 | 7 | }; 8 | module.exports = PlayersController; -------------------------------------------------------------------------------- /api/models/Players.js: -------------------------------------------------------------------------------- 1 | /*--------------------- 2 | :: Players 3 | -> model 4 | ---------------------*/ 5 | module.exports = { 6 | 7 | attributes : { 8 | 9 | name: 'STRING', 10 | score: 'INTEGER' 11 | 12 | } 13 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/session.js: -------------------------------------------------------------------------------- 1 | module.exports.session = { 2 | secret: '7309c3e86f54d10dbcdf2b4e113ab393', 3 | adapter: 'mongo', 4 | host: 'localhost', 5 | port: 27017, 6 | db: 'sails', 7 | collection: 'sessions' 8 | 9 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/i18n.js: -------------------------------------------------------------------------------- 1 | module.exports.i18n = { 2 | 3 | // Which locales are supported? 4 | locales: ['en', 'es'], 5 | 6 | // Where are your locale translations located? 7 | localesDirectory: '/config/locales' 8 | 9 | }; 10 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS.xcodeproj/project.xcworkspace/xcuserdata/rfatahi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-examples/HEAD/SampleAppiOS/SampleAppiOS.xcodeproj/project.xcworkspace/xcuserdata/rfatahi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # The robots.txt file is used to control how search engines index your live URLs. 2 | # See http://www.robotstxt.org/wc/norobots.html for more information. 3 | # 4 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 5 | # User-Agent: * 6 | # Disallow: / 7 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # The robots.txt file is used to control how search engines index your live URLs. 2 | # See http://www.robotstxt.org/wc/norobots.html for more information. 3 | # 4 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 5 | # User-Agent: * 6 | # Disallow: / 7 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/models/Messages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Messages 3 | * 4 | * @module :: Model 5 | * @description :: A short summary of how this model works and what it represents. 6 | * 7 | */ 8 | 9 | module.exports = { 10 | 11 | attributes: { 12 | 13 | 14 | text: 'string' 15 | 16 | } 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User 3 | * 4 | * @module :: Model 5 | * @description :: A short summary of how this model works and what it represents. 6 | * 7 | */ 8 | 9 | module.exports = { 10 | 11 | attributes: { 12 | 13 | 14 | nickname: 'string' 15 | 16 | 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UserController 3 | * 4 | * @module :: Controller 5 | * @description :: Contains logic for handling requests. 6 | */ 7 | 8 | module.exports = { 9 | 10 | 11 | sayHello: function (req, res) { 12 | res.send('hello world!'); 13 | } 14 | 15 | 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/controllers/MessagesController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MessagesController 3 | * 4 | * @module :: Controller 5 | * @description :: Contains logic for handling requests. 6 | */ 7 | 8 | module.exports = { 9 | 10 | 11 | sayHello: function (req, res) { 12 | res.send('hello world!'); 13 | } 14 | 15 | 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SocketIO.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /api/policies/authenticated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Allow any authenticated user. 3 | */ 4 | module.exports = function (req,res,ok) { 5 | 6 | // User is allowed, proceed to controller 7 | if (req.session.authenticated) { 8 | return ok(); 9 | } 10 | 11 | // User is not allowed 12 | else { 13 | return res.send('You are not permitted to perform this action.',403); 14 | } 15 | }; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/CustomCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCollectionViewCell.m 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import "CustomCollectionViewCell.h" 10 | 11 | @implementation CustomCollectionViewCell 12 | 13 | - (void)layoutSubviews 14 | { 15 | 16 | } 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/api/policies/authenticated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Allow any authenticated user. 3 | */ 4 | module.exports = function (req, res, ok) { 5 | 6 | // User is allowed, proceed to controller 7 | if (req.session.authenticated) { 8 | return ok(); 9 | } 10 | 11 | // User is not allowed 12 | else { 13 | return res.send("You are not permitted to perform this action.", 403); 14 | } 15 | }; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SampleAppiOS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SampleAppiOS' target in the 'SampleAppiOS' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sail-example", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "a Sails application", 6 | "dependencies": { 7 | "sails": "0.8.9", 8 | "optimist": "~0.4.0" 9 | }, 10 | "scripts": { 11 | "start": "node app.js", 12 | "debug": "node debug app.js" 13 | }, 14 | "main": "app.js", 15 | "repository": "", 16 | "author": "", 17 | "license": "MIT" 18 | } -------------------------------------------------------------------------------- /config/assets.js: -------------------------------------------------------------------------------- 1 | // Asset rack configuration 2 | module.exports.assets = { 3 | 4 | // A list of directories, in order, which will be recursively parsed for css, javascript, and templates 5 | // and then can be automatically injected in your layout/views via the view partials: 6 | // ( assets.css(), assets.js() and assets.templateLibrary() ) 7 | sequence: [ 8 | 'assets/mixins', 9 | 'assets/js', 10 | 'assets/styles', 11 | 'assets/templates' 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/CustomCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCollectionViewCell.h 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CustomCollectionViewCell : UICollectionViewCell 12 | { 13 | 14 | __weak IBOutlet UITableView *tableView; 15 | __weak IBOutlet UITextView *textView; 16 | 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /views/home/index.ejs: -------------------------------------------------------------------------------- 1 |
2 | Sails.JS Examples 3 |
4 |
5 |

Leaderboards:

6 | jQuery
7 | Ember
8 | Backbone
9 | Angular
10 |
11 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS.xcodeproj/xcuserdata/rfatahi.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /api/controllers/ExampleController.js: -------------------------------------------------------------------------------- 1 | /*--------------------- 2 | :: Example 3 | -> controller 4 | ---------------------*/ 5 | var ExampleController = { 6 | 7 | jQuery: function(req, res) { 8 | res.view(); 9 | }, 10 | 11 | backbone: function(req, res) { 12 | res.view(); 13 | }, 14 | 15 | angular: function(req, res) { 16 | res.view(); 17 | }, 18 | 19 | ember: function(req, res) { 20 | res.view(); 21 | }, 22 | 23 | knockout: function(req, res){ 24 | res.view(); 25 | } 26 | }; 27 | module.exports = ExampleController; -------------------------------------------------------------------------------- /config/local.ex.js: -------------------------------------------------------------------------------- 1 | // Local configuration 2 | // 3 | // Included in the .gitignore by default, 4 | // this is where you include configuration overrides for your local system 5 | // or for a production deployment. 6 | 7 | 8 | // For example, to use port 80 on the local machine, override the `port` config 9 | // module.exports.port = 80; 10 | 11 | // or to keep your db credentials out of the repo, but to use them on the local machine 12 | // override the `modelDefaults` config 13 | // module.exports.modelDefaults = { database: 'foo', user: 'bar', password: 'baZ'} -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sampleApp", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "a Sails application", 6 | "dependencies": { 7 | "sails": "0.9.3", 8 | "grunt": "0.4.1", 9 | "sails-disk": "~0.9.0", 10 | "ejs": "0.8.4", 11 | "optimist": "0.3.4" 12 | }, 13 | "scripts": { 14 | "start": "node app.js", 15 | "debug": "node debug app.js" 16 | }, 17 | "main": "app.js", 18 | "repository": "", 19 | "author": "", 20 | "license": "" 21 | } -------------------------------------------------------------------------------- /config/session.js: -------------------------------------------------------------------------------- 1 | module.exports.session = { 2 | 3 | // Session secret is automatically generated when your new app is created 4 | // It can be easily replaced here: 5 | secret: '2f6f6c69398ae61b2edea014ce78b1bb' 6 | 7 | // In production, uncomment the following lines to set up a shared redis session store 8 | // that can be shared across multiple Sails.js servers 9 | // adapter: 'redis' 10 | 11 | // By default, the local redis instance will be used on the default port 12 | // You can use the following config to override those settings 13 | // host: 'localhost', 14 | // port: 8888 15 | 16 | }; -------------------------------------------------------------------------------- /config/policies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Policy defines middleware that is run before each controller/controller. 3 | * Any policy dropped into the /middleware directory is made globally available through sails.middleware 4 | * Below, use the string name of the middleware 5 | */ 6 | module.exports.policies = { 7 | 8 | // Default policy (allow public access) 9 | '*': true 10 | 11 | /** Example mapping: 12 | someController: { 13 | 14 | // Apply the "authenticated" policy to all actions 15 | '*': 'authenticated', 16 | 17 | // For someAction, apply 'somePolicy' instead 18 | someAction: 'somePolicy' 19 | } 20 | */ 21 | }; -------------------------------------------------------------------------------- /views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- title %> 5 | 6 | 7 | 8 | 9 | <%- assets.js() %> 10 | 11 | <%- assets.css() %> 12 | 13 | 14 | 15 | <%- body %> 16 | 17 | 18 | <%- assets.templateLibrary() %> 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap 3 | * 4 | * An asynchronous boostrap function that runs before your Sails app gets lifted. 5 | * This gives you an opportunity to set up your data model, run jobs, or perform some special logic. 6 | * 7 | * For more information on bootstrapping your app, check out: 8 | * http://sailsjs.org/#documentation 9 | */ 10 | 11 | module.exports.bootstrap = function (cb) { 12 | 13 | // It's very important to trigger this callack method when you are finished 14 | // with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap) 15 | cb(); 16 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ######################## 2 | # sails 3 | ######################## 4 | .sails 5 | .waterline 6 | .rigging 7 | .tmp 8 | 9 | 10 | ######################## 11 | # node.js / npm 12 | ######################## 13 | lib-cov 14 | *.seed 15 | *.log 16 | *.csv 17 | *.dat 18 | *.out 19 | *.pid 20 | *.gz 21 | 22 | pids 23 | logs 24 | results 25 | 26 | node_modules 27 | 28 | npm-debug.log 29 | 30 | 31 | ######################## 32 | # misc / editors 33 | ######################## 34 | *~ 35 | *# 36 | .DS_STORE 37 | .netbeans 38 | nbproject 39 | .idea 40 | 41 | 42 | ######################## 43 | # local config 44 | ######################## 45 | config/local.js 46 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS.xcodeproj/xcuserdata/rfatahi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SampleAppiOS.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3455664117B387830007E588 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/.gitignore: -------------------------------------------------------------------------------- 1 | ######################## 2 | # sails 3 | ######################## 4 | .sails 5 | .waterline 6 | .rigging 7 | .tmp 8 | 9 | 10 | ######################## 11 | # node.js / npm 12 | ######################## 13 | lib-cov 14 | *.seed 15 | *.log 16 | *.csv 17 | *.dat 18 | *.out 19 | *.pid 20 | *.gz 21 | 22 | pids 23 | logs 24 | results 25 | 26 | node_modules 27 | 28 | npm-debug.log 29 | 30 | 31 | ######################## 32 | # misc / editors 33 | ######################## 34 | *~ 35 | *# 36 | .DS_STORE 37 | .netbeans 38 | nbproject 39 | .idea 40 | 41 | 42 | ######################## 43 | # local config 44 | ######################## 45 | config/local.js 46 | -------------------------------------------------------------------------------- /config/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | // Name of the application (used as default ) 4 | appName: 'Sails Examples', 5 | 6 | // Port this Sails application will live on 7 | port: 1339, 8 | 9 | // The environment the app is deployed in 10 | // (`development` or `production`) 11 | // 12 | // In `production` mode, all css and js are bundled up and minified 13 | // And your views and templates are cached in-memory. Gzip is also used. 14 | // The downside? Harder to debug, and the server takes longer to start. 15 | environment: 'development', 16 | 17 | // Logger 18 | // Valid `level` configs: 19 | // 20 | // - error 21 | // - warn 22 | // - debug 23 | // - info 24 | // - verbose 25 | // 26 | log: { 27 | level: 'verbose' 28 | } 29 | 30 | }; -------------------------------------------------------------------------------- /config/bootstrap.js: -------------------------------------------------------------------------------- 1 | // App bootstrap 2 | // Code to run before launching the app 3 | // 4 | // Make sure you call cb() when you're finished. 5 | module.exports.bootstrap = function (cb) { 6 | Players.countAll(function (err, count) { 7 | // Output 'No Players' if count is 0, or the value of count 8 | console.log(count ? count : 'No Players'); 9 | if (count === 0) { 10 | var names = [ 11 | 'Mickey Mouse', 12 | 'Minnie Mouse', 13 | 'Donald Duck', 14 | 'Daisey Duck', 15 | 'Goofy', 16 | 'Pluto' 17 | ]; 18 | for (var i = 0; i < names.length; i++) { 19 | Players.create({name: names[i], score: Math.floor(Math.random()*10)*5}).done(function(err, results){return console.log(results);}); 20 | } 21 | console.log('Added Players'); 22 | } 23 | }); 24 | cb(); 25 | }; -------------------------------------------------------------------------------- /assets/mixins/reset.css: -------------------------------------------------------------------------------- 1 | /* 2 | * The purpose of this CSS reset file is to a provide a sane starting place for stylesheet development by 3 | * normalizing browser differences, eliminating margin and padding, and providing a clear-fix. 4 | */ 5 | html, body {text-align:left;font-size: 1em;} 6 | html,body,img,form,textarea,input,fieldset,div,p,div,ul,li,ol,dl,dt,dd,h1,h2,h3,h4,h5,h6,pre,code { margin: 0;padding: 0;} 7 | ul,li {list-style: none;} 8 | img {display: block;} 9 | a img{border:none;} 10 | a {text-decoration:none;font-weight: normal;font-family: inherit;} 11 | *:active,*:focus { /* Clear mozilla/ie dotted line around active links */ 12 | outline: none; 13 | -moz-outline-style: none; 14 | } 15 | h1,h2,h3,h4,h5,h6,h7 {font-weight: normal; font-size:1em;} /* Fix heading font styles and size */ 16 | div.clear {clear:both;} -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/views/layout.ejs: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title><%- title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%- body %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/404.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default 404 (not found) handler 3 | * 4 | * If no matches are found, Sails will respond using this handler: 5 | * 6 | * For more information on 404/notfound handling in Sails/Express, check out: 7 | * http://expressjs.com/faq.html#404-handling 8 | */ 9 | 10 | module.exports[404] = function pageNotFound(req, res, express404Handler) { 11 | 12 | var statusCode = 404; 13 | var result = { 14 | status: statusCode 15 | }; 16 | 17 | // If the user-agent wants a JSON response, send json 18 | if (req.wantsJSON) { 19 | return res.json(result, result.statusCode); 20 | } 21 | 22 | // Otherwise, serve the `views/404.*` page 23 | var view = '404'; 24 | res.render(view, result, function (err) { 25 | if (err) { 26 | return express404Handler(); 27 | } 28 | res.render(view); 29 | }); 30 | 31 | }; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/NSData+SRB64Additions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | 19 | 20 | @interface NSData (SRB64Additions) 21 | 22 | - (NSString *)SR_stringByBase64Encoding; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOJSONSerialization.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOJSONSerialization.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | @interface SocketIOJSONSerialization : NSObject 27 | 28 | + (id) objectFromJSONData:(NSData *)data error:(NSError **)error; 29 | + (NSString *) JSONStringFromObject:(id)object error:(NSError **)error; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Logger configuration 3 | * 4 | * Configure the log level for your app, as well as the transport 5 | * (Underneath the covers, Sails uses Winston for logging, which 6 | * allows for some pretty neat custom transports/adapters for log messages) 7 | * 8 | * For more information on the Sails logger, check out: 9 | * http://sailsjs.org/#documentation 10 | */ 11 | 12 | module.exports = { 13 | 14 | // Valid `level` configs: 15 | // i.e. the minimum log level to capture with sails.log.*() 16 | // 17 | // 'error' : Display calls to `.error()` 18 | // 'warn' : Display calls from `.error()` to `.warn()` 19 | // 'debug' : Display calls from `.error()`, `.warn()` to `.debug()` 20 | // 'info' : Display calls from `.error()`, `.warn()`, `.debug()` to `.info()` 21 | // 'verbose': Display calls from `.error()`, `.warn()`, `.debug()`, `.info()` to `.verbose()` 22 | // 23 | log: { 24 | level: 'info' 25 | } 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOTransportWebsocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOTransportWebsocket.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | #import "SRWebSocket.h" 27 | #import "SocketIOTransport.h" 28 | 29 | @interface SocketIOTransportWebsocket : NSObject 30 | { 31 | SRWebSocket *_webSocket; 32 | } 33 | 34 | @property (nonatomic, unsafe_unretained) id delegate; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOTransportXHR.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOTransportXHR.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | #import "SocketIOTransport.h" 27 | 28 | @interface SocketIOTransportXHR : NSObject 29 | { 30 | NSString *_url; 31 | NSMutableData *_data; 32 | NSMutableDictionary *_polls; 33 | } 34 | 35 | @property (nonatomic, unsafe_unretained) id delegate; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/base64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Square Inc. 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 | // 15 | 16 | 17 | #ifndef SocketRocket_base64_h 18 | #define SocketRocket_base64_h 19 | 20 | #include 21 | 22 | extern int 23 | b64_ntop(u_char const *src, 24 | size_t srclength, 25 | char *target, 26 | size_t targsize); 27 | 28 | extern int 29 | b64_pton(char const *src, 30 | u_char *target, 31 | size_t targsize); 32 | 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-12 Philipp Kyeck 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /config/adapters.js: -------------------------------------------------------------------------------- 1 | // Configure installed adapters 2 | // If you define an attribute in your model definition, 3 | // it will override anything from this global config. 4 | module.exports.adapters = { 5 | 6 | // If you leave the adapter config unspecified 7 | // in a model definition, 'default' will be used. 8 | 'default': 'memory', 9 | 10 | // In-memory adapter for DEVELOPMENT ONLY 11 | // (data is NOT preserved when the server shuts down) 12 | memory: { 13 | module: 'sails-dirty', 14 | inMemory: true 15 | }, 16 | 17 | // Persistent adapter for DEVELOPMENT ONLY 18 | // (data IS preserved when the server shuts down) 19 | // PLEASE NOTE: disk adapter not compatible with node v0.10.0 currently 20 | // because of limitations in node-dirty 21 | // See https://github.com/felixge/node-dirty/issues/34 22 | disk: { 23 | module: 'sails-dirty', 24 | filePath: './.tmp/dirty.db', 25 | inMemory: false 26 | }, 27 | 28 | // MySQL is the world's most popular relational database. 29 | // Learn more: http://en.wikipedia.org/wiki/MySQL 30 | mysql: { 31 | module : 'sails-mysql', 32 | host : 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS', 33 | user : 'YOUR_MYSQL_USER', 34 | password : 'YOUR_MYSQL_PASSWORD', 35 | database : 'YOUR_MYSQL_DB' 36 | } 37 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/locales/_README.md: -------------------------------------------------------------------------------- 1 | # Translation (i18n) 2 | > _Note: You are viewing the Sails.js v0.9.0 documentation. If you're looking for information on v0.8.x, please visit [here](http://08x.sailsjs.org)._ 3 | 4 | ## Locale 5 | All locale files live under `config/locales`. Here is where you can add locale data as JSON key-value pairs. The name of the file should match the language that you are supporting, which allows for automatic language detection based on the user request. 6 | Here is an example locale file (`config/locales/es.json`): 7 | ```json 8 | { 9 | "Hello!": "Hola!", 10 | "Hello %s, how are you today?": "¿Hola %s, como estas?", 11 | } 12 | ``` 13 | ## Usage 14 | Locales can be accessed through either `res.i18n()`, or in views through the `i18n()` function. 15 | Remember that the keys are case sensitive and require exact key matches. 16 | e.g.: 17 | ```ejs 18 |

<%= i18n('Hello!') %>

19 |

<%= i18n('Hello %s, how are you today?', 'Mike') %>

20 | ``` 21 | 22 | ## config 23 | Locale config can be found in `config/i18n.js`, from which you can set your supported locales: 24 | ```javascript 25 | // Which locales are supported? 26 | locales: ['en', 'es'], 27 | 28 | // Where are your locale translations located? 29 | localesDirectory: '/config/locales' 30 | ``` 31 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/NSData+SRB64Additions.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import "NSData+SRB64Additions.h" 18 | #import "base64.h" 19 | 20 | 21 | @implementation NSData (SRB64Additions) 22 | 23 | - (NSString *)SR_stringByBase64Encoding; 24 | { 25 | size_t buffer_size = (([self length] * 3 + 2) / 2); 26 | 27 | char *buffer = (char *)malloc(buffer_size); 28 | 29 | int len = b64_ntop([self bytes], [self length], buffer, buffer_size); 30 | 31 | if (len == -1) { 32 | free(buffer); 33 | return nil; 34 | } else{ 35 | return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSUTF8StringEncoding freeWhenDone:YES]; 36 | } 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/adapters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Global adapter config 3 | * 4 | * The `adapters` configuration object lets you create different global "saved settings" 5 | * that you can mix and match in your models. The `default` option indicates which 6 | * "saved setting" should be used if a model doesn't have an adapter specified. 7 | * 8 | * Keep in mind that options you define directly in your model definitions 9 | * will override these settings. 10 | * 11 | * For more information on adapter configuration, check out: 12 | * http://sailsjs.org/#documentation 13 | */ 14 | 15 | module.exports.adapters = { 16 | 17 | // If you leave the adapter config unspecified 18 | // in a model definition, 'default' will be used. 19 | 'default': 'disk', 20 | 21 | // In-memory adapter for DEVELOPMENT ONLY 22 | memory: { 23 | module: 'sails-memory' 24 | }, 25 | 26 | // Persistent adapter for DEVELOPMENT ONLY 27 | // (data IS preserved when the server shuts down) 28 | disk: { 29 | module: 'sails-disk' 30 | }, 31 | 32 | // MySQL is the world's most popular relational database. 33 | // Learn more: http://en.wikipedia.org/wiki/MySQL 34 | mysql: { 35 | module: 'sails-mysql', 36 | host: 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS', 37 | user: 'YOUR_MYSQL_USER', 38 | password: 'YOUR_MYSQL_PASSWORD', 39 | database: 'YOUR_MYSQL_DB' 40 | } 41 | }; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SampleAppiOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | aug2uag.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOTransport.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOTransport.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | @protocol SocketIOTransportDelegate 27 | 28 | - (void) onData:(id)message; 29 | - (void) onDisconnect:(NSError*)error; 30 | - (void) onError:(NSError*)error; 31 | 32 | @property (nonatomic, readonly) NSString *host; 33 | @property (nonatomic, readonly) NSInteger port; 34 | @property (nonatomic, readonly) NSString *sid; 35 | @property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; 36 | @property (nonatomic) BOOL useSecure; 37 | 38 | @end 39 | 40 | @protocol SocketIOTransport 41 | 42 | - (id) initWithDelegate:(id )delegate; 43 | - (void) open; 44 | - (void) close; 45 | - (BOOL) isReady; 46 | - (void) send:(NSString *)request; 47 | 48 | @property (nonatomic, unsafe_unretained) id delegate; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOPacket.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOPacket.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | @interface SocketIOPacket : NSObject 27 | { 28 | NSString *type; 29 | NSString *pId; 30 | NSString *ack; 31 | NSString *name; 32 | NSString *data; 33 | NSArray *args; 34 | NSString *endpoint; 35 | NSArray *_types; 36 | } 37 | 38 | @property (nonatomic, copy) NSString *type; 39 | @property (nonatomic, copy) NSString *pId; 40 | @property (nonatomic, copy) NSString *ack; 41 | @property (nonatomic, copy) NSString *name; 42 | @property (nonatomic, copy) NSString *data; 43 | @property (nonatomic, copy) NSString *endpoint; 44 | @property (nonatomic, copy) NSArray *args; 45 | 46 | - (id) initWithType:(NSString *)packetType; 47 | - (id) initWithTypeIndex:(int)index; 48 | - (id) dataAsJSON; 49 | - (NSNumber *) typeAsNumber; 50 | - (NSString *) typeForIndex:(int)index; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /views/example/jQuery/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Sails.JS Examples: jQuery
4 | 5 |
6 |
7 |
8 | 9 |
10 |
Click a Player to select!
11 |
12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /views/example/angular.ejs: -------------------------------------------------------------------------------- 1 |
Sails.JS Examples: jQuery
2 | 3 |
4 |
5 |
6 | 7 |
8 |
Click a Player to select!
9 |
10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/csrf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-Site Request Forgery Protection 3 | * 4 | * CSRF tokens are like a tracking chip. While a session tells the server that a user 5 | * "is who they say they are", a csrf token tells the server "you are where you say you are". 6 | * 7 | * When enabled, all non-GET requests to the Sails server must be accompanied by 8 | * a special token, identified as the '_csrf' parameter. 9 | * 10 | * This option protects your Sails app against cross-site request forgery (or CSRF) attacks. 11 | * A would-be attacker needs not only a user's session cookie, but also this timestamped, 12 | * secret CSRF token, which is refreshed/granted when the user visits a URL on your app's domain. 13 | * 14 | * This allows us to have certainty that our users' requests haven't been hijacked, 15 | * and that the requests they're making are intentional and legitimate. 16 | * 17 | * This token has a short-lived expiration timeline, and must be acquired by either: 18 | * 19 | * (a) For traditional view-driven web apps: 20 | * Fetching it from one of your views, where it may be accessed as 21 | * a local variable, e.g.: 22 | *
23 | * 24 | *
25 | * 26 | * or (b) For AJAX/Socket-heavy and/or single-page apps: 27 | * Sending a GET request to the `/csrfToken` route, where it will be returned 28 | * as JSON, e.g.: 29 | * { _csrf: 'ajg4JD(JGdajhLJALHDa' } 30 | * 31 | * 32 | * Enabling this option requires managing the token in your front-end app. 33 | * For traditional web apps, it's as easy as passing the data from a view into a form action. 34 | * In AJAX/Socket-heavy apps, just send a GET request to the /csrfToken route to get a valid token. 35 | * 36 | * For more information on CSRF, check out: 37 | * http://en.wikipedia.org/wiki/Cross-site_request_forgery 38 | */ 39 | 40 | module.exports.csrf = false; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/controllers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Controllers 3 | * 4 | * By default, Sails controllers automatically bind routes for each of their functions. 5 | * Additionally, each controller will automatically bind routes for a CRUD API 6 | * controlling the model which matches its name, if one exists. 7 | * 8 | * NOTE: These settings are for the global configuration of controllers. 9 | * You may also override these settings on a per-controller basis 10 | * by modifying the 'blueprints' object in your controllers 11 | * 12 | * For more information on controller configuration and blueprints, check out: 13 | * http://sailsjs.org/#documentation 14 | */ 15 | 16 | module.exports.controllers = { 17 | 18 | 19 | blueprints: { 20 | 21 | // Optional mount path prefix for blueprints 22 | // (the automatically bound routes in your controllers) 23 | // e.g. '/api/v2' 24 | prefix: '', 25 | 26 | 27 | // Whether routes are automatically generated for every action in your controllers 28 | // (also maps `index` to /:controller) 29 | // '/:controller', '/:controller/index', and '/:controller/:action' 30 | actions: true, 31 | 32 | 33 | // ** NOTE ** 34 | // These CRUD shortcuts exist for your convenience during development, 35 | // but you'll want to disable them in production. 36 | // '/:controller/find/:id?' 37 | // '/:controller/create' 38 | // '/:controller/update/:id' 39 | // '/:controller/destroy/:id' 40 | shortcuts: true, 41 | 42 | 43 | // Automatic REST blueprints enabled? 44 | // e.g. 45 | // 'get /:controller/:id?' 46 | // 'post /:controller' 47 | // 'put /:controller/:id' 48 | // 'delete /:controller/:id' 49 | rest: true, 50 | 51 | 52 | // If a blueprint route catches a request, 53 | // only match :id param if it's an integer 54 | // 55 | // e.g. only trigger route handler if requests look like: 56 | // get /user/8 57 | // instead of: 58 | // get /user/a8j4g9jsd9ga4ghjasdha 59 | expectIntegerId: false 60 | } 61 | 62 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/500.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default error handler 3 | * 4 | * If an error is thrown, Sails will respond using this default error handler 5 | * 6 | * For more information on error handling in Sails/Express, check out: 7 | * http://expressjs.com/guide.html#error-handling 8 | */ 9 | 10 | module.exports[500] = function serverErrorOccurred(errors, req, res, expressErrorHandler) { 11 | 12 | var statusCode = 500; 13 | 14 | // Ensure that `errors` is a list 15 | var displayedErrors = (typeof errors !== 'object' || !errors.length) ? [errors] : errors; 16 | 17 | // Build data for response 18 | var response = { 19 | status: statusCode 20 | }; 21 | 22 | // Ensure that each error is formatted correctly 23 | var inspect = require('util').inspect; 24 | for (var i in displayedErrors) { 25 | 26 | // Make error easier to read, and normalize its type 27 | if (!(displayedErrors[i] instanceof Error)) { 28 | displayedErrors[i] = new Error(inspect(displayedErrors[i])); 29 | } 30 | 31 | displayedErrors[i] = { 32 | message: displayedErrors[i].message, 33 | stack: displayedErrors[i].stack 34 | }; 35 | 36 | // Log error to log adapter 37 | sails.log.error(displayedErrors[i].stack); 38 | } 39 | 40 | // In production, don't display any identifying information about the error(s) 41 | if (sails.config.environment === 'development') { 42 | response.errors = displayedErrors; 43 | } 44 | 45 | // If the user-agent wants a JSON response, 46 | // respond with a JSON-readable version of errors 47 | if (req.wantsJSON) { 48 | return res.json(response, response.status); 49 | } 50 | 51 | // Otherwise, if it can be rendered, the `views/500.*` page is rendered 52 | // If an error occurs rendering the 500 view ITSELF, 53 | // use the built-in Express error handler to render the errors 54 | var view = '500'; 55 | res.render(view, response, function (err) { 56 | if (err) { 57 | return expressErrorHandler(errors); 58 | } 59 | res.render(view, response); 60 | }); 61 | 62 | }; -------------------------------------------------------------------------------- /views/404.ejs: -------------------------------------------------------------------------------- 1 |

Page not found

2 | 3 |

The view you were trying to reach does not exist.

4 | 5 | 6 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /views/500.ejs: -------------------------------------------------------------------------------- 1 |

Error

2 | 3 | 4 | <% _.each(errors, function (error) { %> 5 | 6 |

 7 | <%= error %>
 8 | 
9 | 10 | <% }) %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | // Routes 2 | // ********************* 3 | // 4 | // This table routes urls to controllers/actions. 5 | // 6 | // If the URL is not specified here, the default route for a URL is: /:controller/:action/:id 7 | // where :controller, :action, and the :id request parameter are derived from the url 8 | // 9 | // If :action is not specified, Sails will redirect to the appropriate action 10 | // based on the HTTP verb: (using REST/Backbone conventions) 11 | // 12 | // GET: /:controller/read/:id 13 | // POST: /:controller/create 14 | // PUT: /:controller/update/:id 15 | // DELETE: /:controller/destroy/:id 16 | // 17 | // If the requested controller/action doesn't exist: 18 | // - if a view exists ( /views/:controller/:action.ejs ), Sails will render that view 19 | // - if no view exists, but a model exists, Sails will automatically generate a 20 | // JSON API for the model which matches :controller. 21 | // - if no view OR model exists, Sails will respond with a 404. 22 | // 23 | module.exports.routes = { 24 | 25 | // To route the home page to the "index" action of the "home" controller: 26 | '/' : { 27 | controller : 'home' 28 | } 29 | 30 | // If you want to set up a route only for a particular HTTP method/verb 31 | // (GET, POST, PUT, DELETE) you can specify the verb before the path: 32 | // 'post /signup': { 33 | // controller : 'user', 34 | // action : 'signup' 35 | // } 36 | 37 | // Keep in mind default routes exist for each of your controllers 38 | // So if you have a UserController with an action called "juggle" 39 | // a route will be automatically exist mapping it to /user/juggle. 40 | // 41 | // Additionally, unless you override them, new controllers will have 42 | // create(), find(), findAll(), update(), and destroy() actions, 43 | // and routes will exist for them as follows: 44 | /* 45 | 46 | // Standard RESTful routing 47 | // (if index is not defined, findAll will be used) 48 | 'get /user': { 49 | controller : 'user', 50 | action : 'index' 51 | }, 52 | 'get /user/:id': { 53 | controller : 'user', 54 | action : 'find' 55 | }, 56 | 'post /user': { 57 | controller : 'user', 58 | action : 'create' 59 | }, 60 | 'put /user/:id': { 61 | controller : 'user', 62 | action : 'update' 63 | }, 64 | 'delete /user/:id': { 65 | controller : 'user', 66 | action : 'destroy' 67 | } 68 | */ 69 | }; 70 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * app.js 3 | * 4 | * This file contains some conventional defaults for working with Socket.io + Sails. 5 | * It is designed to get you up and running fast, but is by no means anything special. 6 | * 7 | * Feel free to change none, some, or ALL of this file to fit your needs! 8 | */ 9 | 10 | 11 | (function (io) { 12 | 13 | // as soon as this file is loaded, connect automatically, 14 | var socket = io.connect(); 15 | if (typeof console !== 'undefined') { 16 | log('Connecting to Sails.js...'); 17 | } 18 | 19 | socket.on('connect', function socketConnected() { 20 | 21 | // Listen for Comet messages from Sails 22 | socket.on('message', function messageReceived(message) { 23 | 24 | /////////////////////////////////////////////////////////// 25 | // Replace the following with your own custom logic 26 | // to run when a new message arrives from the Sails.js 27 | // server. 28 | /////////////////////////////////////////////////////////// 29 | log('New comet message received :: ', message); 30 | ////////////////////////////////////////////////////// 31 | 32 | }); 33 | 34 | 35 | /////////////////////////////////////////////////////////// 36 | // Here's where you'll want to add any custom logic for 37 | // when the browser establishes its socket connection to 38 | // the Sails.js server. 39 | /////////////////////////////////////////////////////////// 40 | log( 41 | 'Socket is now connected and globally accessible as `socket`.\n' + 42 | 'e.g. to send a GET request to Sails, try \n' + 43 | '`socket.get("/", function (response) ' + 44 | '{ console.log(response); })`' 45 | ); 46 | /////////////////////////////////////////////////////////// 47 | 48 | 49 | }); 50 | 51 | 52 | // Expose connected `socket` instance globally so that it's easy 53 | // to experiment with from the browser console while prototyping. 54 | window.socket = socket; 55 | 56 | 57 | // Simple log function to keep the example simple 58 | function log () { 59 | if (typeof console !== 'undefined') { 60 | console.log.apply(console, arguments); 61 | } 62 | } 63 | 64 | 65 | })( 66 | 67 | // In case you're wrapping socket.io to prevent pollution of the global namespace, 68 | // you can replace `window.io` with your own `io` here: 69 | window.io 70 | 71 | ); 72 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOPacket.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOPacket.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import "SocketIOPacket.h" 25 | #import "SocketIOJSONSerialization.h" 26 | 27 | @implementation SocketIOPacket 28 | 29 | @synthesize type, pId, name, ack, data, args, endpoint; 30 | 31 | - (id) init 32 | { 33 | self = [super init]; 34 | if (self) { 35 | _types = [NSArray arrayWithObjects: @"disconnect", 36 | @"connect", 37 | @"heartbeat", 38 | @"message", 39 | @"json", 40 | @"event", 41 | @"ack", 42 | @"error", 43 | @"noop", 44 | nil]; 45 | } 46 | return self; 47 | } 48 | 49 | - (id) initWithType:(NSString *)packetType 50 | { 51 | self = [self init]; 52 | if (self) { 53 | self.type = packetType; 54 | } 55 | return self; 56 | } 57 | 58 | - (id) initWithTypeIndex:(int)index 59 | { 60 | self = [self init]; 61 | if (self) { 62 | self.type = [self typeForIndex:index]; 63 | } 64 | return self; 65 | } 66 | 67 | - (id) dataAsJSON 68 | { 69 | if (self.data) { 70 | NSData *utf8Data = [self.data dataUsingEncoding:NSUTF8StringEncoding]; 71 | return [SocketIOJSONSerialization objectFromJSONData:utf8Data error:nil]; 72 | } 73 | else { 74 | return nil; 75 | } 76 | } 77 | 78 | - (NSNumber *) typeAsNumber 79 | { 80 | NSUInteger index = [_types indexOfObject:self.type]; 81 | NSNumber *num = [NSNumber numberWithUnsignedInteger:index]; 82 | return num; 83 | } 84 | 85 | - (NSString *) typeForIndex:(int)index 86 | { 87 | return [_types objectAtIndex:index]; 88 | } 89 | 90 | - (void) dealloc 91 | { 92 | _types = nil; 93 | 94 | type = nil; 95 | pId = nil; 96 | name = nil; 97 | ack = nil; 98 | data = nil; 99 | args = nil; 100 | endpoint = nil; 101 | } 102 | 103 | @end 104 | 105 | -------------------------------------------------------------------------------- /views/example/knockout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
Sails.JS Examples: Knockout JS
6 | 7 |
8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 | 16 |
17 | 18 | Click a Player to select! 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/views.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Views 3 | * 4 | * Server-sent views are a classic and effective way to get your app up and running. 5 | * Views are normally served from controllers, but by default, Sails also exposes routes 6 | * to allow you to preview your views in a browser. This automatic routing can be disabled 7 | * using the `blueprint` config below. You can also configure your templating language/framework 8 | * of choice, and configure Sails' layout support. 9 | * 10 | * For more information on views and layouts, check out: 11 | * http://sailsjs.org/#documentation 12 | */ 13 | 14 | module.exports.views = { 15 | 16 | // View engine (aka template language) 17 | // to use for your app's *server-side* views 18 | // 19 | // Sails+Express supports all view engines which implement 20 | // TJ Holowaychuk's `consolidate.js`, including, but not limited to: 21 | // 22 | // ejs, jade, handlebars, mustache 23 | // underscore, hogan, haml, haml-coffee, dust 24 | // atpl, eco, ect, jazz, jqtpl, JUST, liquor, QEJS, 25 | // swig, templayed, toffee, walrus, & whiskers 26 | 27 | engine: 'ejs', 28 | 29 | 30 | 31 | // Layouts are simply top-level HTML templates you can use as wrappers 32 | // for your server-side views. If you're using ejs, you can take advantage of 33 | // Sails' built-in `layout` support. 34 | // 35 | // With using a layout, when one of your views is served, it is injected into 36 | // the <%- body %> partial defined in the layout. This lets you reuse header 37 | // and footer logic between views. 38 | // 39 | // NOTE: Layout support is only implemented for the `ejs` view engine! 40 | // For most other engines, it is not necessary, since they implement 41 | // partials/layouts themselves. In those cases, this config willwill be silently 42 | // ignored. 43 | // 44 | // The `layout` setting may be set to one of: 45 | // 46 | // If `true`, Sails will look for the default, located at `views/layout.ejs` 47 | // If `false`, layouts will be disabled. 48 | // Otherwise, if a string is specified, it will be interpreted as the relative path 49 | // to your layout from `views/` folder. 50 | // (the file extension, e.g. ".ejs", should be omitted) 51 | // 52 | 53 | layout: 'layout' 54 | 55 | 56 | 57 | // Using Multiple Layouts with EJS 58 | // 59 | // If you're using the default engine, `ejs`, Sails supports the use of multiple 60 | // `layout` files. To take advantage of this, before rendering a view, override 61 | // the `layout` local in your controller by setting `res.locals.layout`. 62 | // (this is handy if you parts of your app's UI look completely different from each other) 63 | // 64 | // e.g. your default might be 65 | // layout: 'layouts/public' 66 | // 67 | // But you might override that in some of your controllers with: 68 | // layout: 'layouts/internal' 69 | 70 | 71 | }; -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | Page Not Found 19 | 20 | 24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 | 32 |
33 |

34 | Something's fishy here. 35 |

36 |

37 | The page you were trying to reach doesn't exist. 38 |

39 |

40 | Why might this be happening? 41 |

42 |
43 | 44 | 49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/policies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Policies are simply Express middleware functions which run before your controllers. 3 | * You can apply one or more policies for a given controller or action. 4 | * 5 | * Any policy file (e.g. `authenticated.js`) can be dropped into the `/policies` folder, 6 | * at which point it can be accessed below by its filename, minus the extension, (e.g. `authenticated`) 7 | * 8 | * For more information on policies, check out: 9 | * http://sailsjs.org/#documentation 10 | */ 11 | 12 | 13 | module.exports.policies = { 14 | 15 | // Default policy for all controllers and actions 16 | // (`true` allows public access) 17 | '*': true 18 | 19 | /* 20 | // Here's an example of adding some policies to a controller 21 | RabbitController: { 22 | 23 | // Apply the `false` policy as the default for all of RabbitController's actions 24 | // (`false` prevents all access, which ensures that nothing bad happens to our rabbits) 25 | '*': false, 26 | 27 | // For the action `nurture`, apply the 'isRabbitMother' policy 28 | // (this overrides `false` above) 29 | nurture : 'isRabbitMother', 30 | 31 | // Apply the `isNiceToAnimals` AND `hasRabbitFood` policies 32 | // before letting any users feed our rabbits 33 | feed : ['isNiceToAnimals', 'hasRabbitFood'] 34 | } 35 | */ 36 | }; 37 | 38 | 39 | /** 40 | * Here's what the `isNiceToAnimals` policy from above might look like: 41 | * (this file would be located at `policies/isNiceToAnimals.js`) 42 | * 43 | * We'll make some educated guesses about whether our system will 44 | * consider this user someone who is nice to animals. 45 | * 46 | * Besides protecting rabbits (while a noble cause, no doubt), 47 | * here are a few other example use cases for policies: 48 | * 49 | * + cookie-based authentication 50 | * + role-based access control 51 | * + limiting file uploads based on MB quotas 52 | * + OAuth 53 | * + BasicAuth 54 | * + or any other kind of authentication scheme you can imagine 55 | * 56 | */ 57 | 58 | /* 59 | module.exports = function isNiceToAnimals (req, res, next) { 60 | 61 | // `req.session` contains a set of data specific to the user making this request. 62 | // It's kind of like our app's "memory" of the current user. 63 | 64 | // If our user has a history of animal cruelty, not only will we 65 | // prevent her from going even one step further (`return`), 66 | // we'll go ahead and redirect her to PETA (`res.redirect`). 67 | if ( req.session.user.hasHistoryOfAnimalCruelty ) { 68 | return res.redirect('http://PETA.org'); 69 | } 70 | 71 | // If the user has been seen frowning at puppies, we have to assume that 72 | // they might end up being mean to them, so we'll 73 | if ( req.session.user.frownsAtPuppies ) { 74 | return res.redirect('http://www.dailypuppy.com/'); 75 | } 76 | 77 | // Finally, if the user has a clean record, we'll call the `next()` function 78 | // to let them through to the next policy or our controller 79 | next(); 80 | }; 81 | */ 82 | -------------------------------------------------------------------------------- /views/example/ember/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 27 | 28 | 44 | 45 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS.xcodeproj/xcuserdata/rfatahi.xcuserdatad/xcschemes/SampleAppiOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /assets/styles/style.less: -------------------------------------------------------------------------------- 1 | body{ 2 | font-size:1em; 3 | line-height:1.5; 4 | background:#e7e7e7; 5 | font-family:'Helvetica Neue',Helvetica,Arial,serif; 6 | text-shadow:0 1px 0 rgba(255,255,255,0.8); 7 | color:#6d6d6d; 8 | } 9 | header{ 10 | width:100%; 11 | background:#fff; 12 | margin-bottom:25px 13 | } 14 | header a{ 15 | padding:25px 0; 16 | width:620px; 17 | margin:0 auto; 18 | color:#158ea1; 19 | display:block; 20 | font-weight:800; 21 | } 22 | #content,footer{ 23 | width:620px; 24 | margin:0 auto; 25 | } 26 | h2{ 27 | font-size:32px; 28 | } 29 | p{ 30 | font-weight:300; 31 | margin-bottom:20px; 32 | } 33 | a,a:hover{ 34 | color:#c5000c; 35 | } 36 | p a{ 37 | font-weight:bold; 38 | } 39 | a.button{ 40 | -moz-border-radius:30px; 41 | -webkit-border-radius:30px; 42 | border-radius:30px; 43 | border-top:solid 1px #cbcbcb; 44 | border-left:solid 1px #b7b7b7; 45 | border-right:solid 1px #b7b7b7; 46 | border-bottom:solid 1px #b3b3b3; 47 | color:#303030; 48 | line-height:25px; 49 | font-weight:bold; 50 | font-size:15px; 51 | padding:12px 8px 12px 8px; 52 | display:inline-block; 53 | width:179px; 54 | margin-right:14px; 55 | background:#fdfdfd; 56 | background:-moz-linear-gradient(top,#fdfdfd 0,#f2f2f2 100%); 57 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fdfdfd),color-stop(100%,#f2f2f2)); 58 | background:-webkit-linear-gradient(top,#fdfdfd 0,#f2f2f2 100%); 59 | background:-o-linear-gradient(top,#fdfdfd 0,#f2f2f2 100%); 60 | background:-ms-linear-gradient(top,#fdfdfd 0,#f2f2f2 100%); 61 | background:linear-gradient(top,#fdfdfd 0,#f2f2f2 100%); 62 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfdfd',endColorstr='#f2f2f2',GradientType=0); 63 | -webkit-box-shadow:10px 10px 5px #888; 64 | -moz-box-shadow:10px 10px 5px #888; 65 | box-shadow:0 1px 5px #e8e8e8; 66 | } 67 | a.button:hover{ 68 | border-top:solid 1px #b7b7b7; 69 | border-left:solid 1px #b3b3b3; 70 | border-right:solid 1px #b3b3b3; 71 | border-bottom:solid 1px #b3b3b3; 72 | background:#fafafa; 73 | background:-moz-linear-gradient(top,#fdfdfd 0,#f6f6f6 100%); 74 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fdfdfd),color-stop(100%,#f6f6f6)); 75 | background:-webkit-linear-gradient(top,#fdfdfd 0,#f6f6f6 100%); 76 | background:-o-linear-gradient(top,#fdfdfd 0,#f6f6f6 100%); 77 | background:-ms-linear-gradient(top,#fdfdfd 0,#f6f6f6 100%); 78 | background:linear-gradient(top,#fdfdfd 0,#f6f6f6,100%); 79 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfdfd',endColorstr='#f6f6f6',GradientType=0); 80 | } 81 | a.button span{ 82 | padding-left:30px; 83 | padding-right:30px; 84 | display:block; 85 | height:23px; 86 | text-align:center; 87 | } 88 | .highlight{ 89 | border-bottom:1px solid #158ea1; 90 | } 91 | 92 | footer{ 93 | border-top:1px solid #cacaca; 94 | margin-top:40px; 95 | padding-top:20px; 96 | padding-bottom:30px; 97 | font-size:13px; 98 | color:#AAA; 99 | } 100 | .float_right { 101 | float: right; 102 | } 103 | .float_left { 104 | float: left; 105 | } 106 | 107 | .players, 108 | .details { 109 | font-size: 24px; 110 | width: 360px; 111 | margin: auto; 112 | } 113 | 114 | .active { 115 | background-color: rgb(255,255,0); 116 | } 117 | 118 | .name { 119 | display: inline-block; 120 | width: 300px; 121 | margin-right: 10px; 122 | } 123 | 124 | .score { 125 | display: inline-block; 126 | width: 50px; 127 | } 128 | 129 | .details { 130 | margin-top: 10px; 131 | border-top: 4px dashed #444444; 132 | .name { 133 | color: #666666; 134 | } 135 | } -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOJSONSerialization.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOJSONSerialization.m 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import "SocketIOJSONSerialization.h" 25 | 26 | extern NSString * const SocketIOException; 27 | 28 | // covers the methods in SBJson and JSONKit 29 | @interface NSObject (SocketIOJSONSerialization) 30 | 31 | // used by both JSONKit and SBJson 32 | - (id) objectWithData:(NSData *)data; 33 | 34 | // Use by JSONKit serialization 35 | - (NSString *) JSONString; 36 | - (id) decoder; 37 | 38 | // Used by SBJsonWriter 39 | - (NSString *) stringWithObject:(id)object; 40 | 41 | @end 42 | 43 | @implementation SocketIOJSONSerialization 44 | 45 | + (id) objectFromJSONData:(NSData *)data error:(NSError **)error { 46 | Class serializer; 47 | 48 | // try SBJson first 49 | serializer = NSClassFromString(@"SBJsonParser"); 50 | if (serializer) { 51 | id parser; 52 | id object; 53 | 54 | parser = [[serializer alloc] init]; 55 | object = [parser objectWithData:data]; 56 | 57 | return object; 58 | } 59 | 60 | // try Foundation's JSON coder, available in OS X 10.7/iOS 5.0 61 | serializer = NSClassFromString(@"NSJSONSerialization"); 62 | if (serializer) { 63 | return [serializer JSONObjectWithData:data options:0 error:error]; 64 | } 65 | 66 | // lastly, try JSONKit 67 | serializer = NSClassFromString(@"JSONDecoder"); 68 | if (serializer) { 69 | return [[serializer decoder] objectWithData:data]; 70 | } 71 | 72 | // unable to find a suitable JSON deseralizer 73 | [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; 74 | 75 | return nil; 76 | } 77 | 78 | + (NSString *) JSONStringFromObject:(id)object error:(NSError **)error { 79 | Class serializer; 80 | NSString *jsonString; 81 | 82 | jsonString = nil; 83 | serializer = NSClassFromString(@"SBJsonWriter"); 84 | if (serializer) { 85 | id writer; 86 | 87 | writer = [[serializer alloc] init]; 88 | jsonString = [writer stringWithObject:object]; 89 | 90 | return jsonString; 91 | } 92 | 93 | serializer = NSClassFromString(@"NSJSONSerialization"); 94 | if (serializer) { 95 | NSData *data; 96 | 97 | data = [serializer dataWithJSONObject:object options:0 error:error]; 98 | 99 | jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 100 | 101 | return jsonString; 102 | } 103 | 104 | // lastly, try JSONKit 105 | if ([object respondsToSelector:@selector(JSONString)]) { 106 | return [object JSONString]; 107 | } 108 | 109 | // unable to find a suitable JSON seralizer 110 | [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; 111 | 112 | return nil; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOTransportWebsocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOTransportWebsocket.m 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import "SocketIOTransportWebsocket.h" 25 | #import "SocketIO.h" 26 | 27 | #define DEBUG_LOGS 0 28 | 29 | #if DEBUG_LOGS 30 | #define DEBUGLOG(...) NSLog(__VA_ARGS__) 31 | #else 32 | #define DEBUGLOG(...) 33 | #endif 34 | 35 | static NSString* kInsecureSocketURL = @"ws://%@/socket.io/1/websocket/%@"; 36 | static NSString* kSecureSocketURL = @"wss://%@/socket.io/1/websocket/%@"; 37 | static NSString* kInsecureSocketPortURL = @"ws://%@:%d/socket.io/1/websocket/%@"; 38 | static NSString* kSecureSocketPortURL = @"wss://%@:%d/socket.io/1/websocket/%@"; 39 | 40 | @implementation SocketIOTransportWebsocket 41 | 42 | @synthesize delegate; 43 | 44 | - (id) initWithDelegate:(id)delegate_ 45 | { 46 | self = [super init]; 47 | if (self) { 48 | self.delegate = delegate_; 49 | } 50 | return self; 51 | } 52 | 53 | - (BOOL) isReady 54 | { 55 | return _webSocket.readyState == SR_OPEN; 56 | } 57 | 58 | - (void) open 59 | { 60 | NSString *urlStr; 61 | NSString *format; 62 | if (delegate.port) { 63 | format = delegate.useSecure ? kSecureSocketPortURL : kInsecureSocketPortURL; 64 | urlStr = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; 65 | } 66 | else { 67 | format = delegate.useSecure ? kSecureSocketURL : kInsecureSocketURL; 68 | urlStr = [NSString stringWithFormat:format, delegate.host, delegate.sid]; 69 | } 70 | NSURL *url = [NSURL URLWithString:urlStr]; 71 | 72 | _webSocket = nil; 73 | 74 | _webSocket = [[SRWebSocket alloc] initWithURL:url]; 75 | _webSocket.delegate = self; 76 | DEBUGLOG(@"Opening %@", url); 77 | [_webSocket open]; 78 | } 79 | 80 | - (void) dealloc 81 | { 82 | [_webSocket setDelegate:nil]; 83 | } 84 | 85 | - (void) close 86 | { 87 | [_webSocket close]; 88 | } 89 | 90 | - (void) send:(NSString*)request 91 | { 92 | [_webSocket send:request]; 93 | } 94 | 95 | 96 | 97 | # pragma mark - 98 | # pragma mark WebSocket Delegate Methods 99 | 100 | - (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message 101 | { 102 | [delegate onData:message]; 103 | } 104 | 105 | - (void) webSocketDidOpen:(SRWebSocket *)webSocket 106 | { 107 | DEBUGLOG(@"Socket opened."); 108 | } 109 | 110 | - (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error 111 | { 112 | DEBUGLOG(@"Socket failed with error ... %@", [error localizedDescription]); 113 | // Assuming this resulted in a disconnect 114 | [delegate onDisconnect:error]; 115 | } 116 | 117 | - (void) webSocket:(SRWebSocket *)webSocket 118 | didCloseWithCode:(NSInteger)code 119 | reason:(NSString *)reason 120 | wasClean:(BOOL)wasClean 121 | { 122 | DEBUGLOG(@"Socket closed. %@", reason); 123 | [delegate onDisconnect:[NSError errorWithDomain:SocketIOError 124 | code:SocketIOWebSocketClosed 125 | userInfo:nil]]; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | #import 19 | 20 | typedef enum { 21 | SR_CONNECTING = 0, 22 | SR_OPEN = 1, 23 | SR_CLOSING = 2, 24 | SR_CLOSED = 3, 25 | } SRReadyState; 26 | 27 | @class SRWebSocket; 28 | 29 | extern NSString *const SRWebSocketErrorDomain; 30 | 31 | #pragma mark - SRWebSocketDelegate 32 | 33 | @protocol SRWebSocketDelegate; 34 | 35 | #pragma mark - SRWebSocket 36 | 37 | @interface SRWebSocket : NSObject 38 | 39 | @property (nonatomic, assign) id delegate; 40 | 41 | @property (nonatomic, readonly) SRReadyState readyState; 42 | @property (nonatomic, readonly, retain) NSURL *url; 43 | 44 | // This returns the negotiated protocol. 45 | // It will be nil until after the handshake completes. 46 | @property (nonatomic, readonly, copy) NSString *protocol; 47 | 48 | // Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. 49 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 50 | - (id)initWithURLRequest:(NSURLRequest *)request; 51 | 52 | // Some helper constructors. 53 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 54 | - (id)initWithURL:(NSURL *)url; 55 | 56 | // Delegate queue will be dispatch_main_queue by default. 57 | // You cannot set both OperationQueue and dispatch_queue. 58 | - (void)setDelegateOperationQueue:(NSOperationQueue*) queue; 59 | - (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; 60 | 61 | // By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. 62 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 63 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 64 | 65 | // SRWebSockets are intended for one-time-use only. Open should be called once and only once. 66 | - (void)open; 67 | 68 | - (void)close; 69 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 70 | 71 | // Send a UTF8 String or Data. 72 | - (void)send:(id)data; 73 | 74 | @end 75 | 76 | #pragma mark - SRWebSocketDelegate 77 | 78 | @protocol SRWebSocketDelegate 79 | 80 | // message will either be an NSString if the server is using text 81 | // or NSData if the server is using binary. 82 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 83 | 84 | @optional 85 | 86 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 87 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 88 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 89 | 90 | @end 91 | 92 | #pragma mark - NSURLRequest (CertificateAdditions) 93 | 94 | @interface NSURLRequest (CertificateAdditions) 95 | 96 | @property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; 97 | 98 | @end 99 | 100 | #pragma mark - NSMutableURLRequest (CertificateAdditions) 101 | 102 | @interface NSMutableURLRequest (CertificateAdditions) 103 | 104 | @property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; 105 | 106 | @end 107 | 108 | #pragma mark - NSRunLoop (SRWebSocket) 109 | 110 | @interface NSRunLoop (SRWebSocket) 111 | 112 | + (NSRunLoop *)SR_networkRunLoop; 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIO.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIO.h 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import 25 | 26 | #import "SocketIOTransport.h" 27 | 28 | @class SocketIO; 29 | @class SocketIOPacket; 30 | 31 | typedef void(^SocketIOCallback)(id argsData); 32 | 33 | extern NSString* const SocketIOError; 34 | 35 | typedef enum { 36 | SocketIOServerRespondedWithInvalidConnectionData = -1, 37 | SocketIOServerRespondedWithDisconnect = -2, 38 | SocketIOHeartbeatTimeout = -3, 39 | SocketIOWebSocketClosed = -4, 40 | SocketIOTransportsNotSupported = -5, 41 | SocketIOHandshakeFailed = -6, 42 | SocketIODataCouldNotBeSend = -7 43 | } SocketIOErrorCodes; 44 | 45 | 46 | @protocol SocketIODelegate 47 | @optional 48 | - (void) socketIODidConnect:(SocketIO *)socket; 49 | - (void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error; 50 | - (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet; 51 | - (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet; 52 | - (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet; 53 | - (void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet; 54 | - (void) socketIO:(SocketIO *)socket onError:(NSError *)error; 55 | 56 | // TODO: deprecated -> to be removed 57 | - (void) socketIO:(SocketIO *)socket failedToConnectWithError:(NSError *)error __attribute__((deprecated)); 58 | - (void) socketIOHandshakeFailed:(SocketIO *)socket __attribute__((deprecated)); 59 | @end 60 | 61 | 62 | @interface SocketIO : NSObject 63 | { 64 | NSString *_host; 65 | NSInteger _port; 66 | NSString *_sid; 67 | NSString *_endpoint; 68 | NSDictionary *_params; 69 | 70 | __unsafe_unretained id _delegate; 71 | 72 | NSObject *_transport; 73 | 74 | BOOL _isConnected; 75 | BOOL _isConnecting; 76 | BOOL _useSecure; 77 | 78 | NSURLConnection *_handshake; 79 | 80 | // heartbeat 81 | NSTimeInterval _heartbeatTimeout; 82 | dispatch_source_t _timeout; 83 | 84 | NSMutableArray *_queue; 85 | 86 | // acknowledge 87 | NSMutableDictionary *_acks; 88 | NSInteger _ackCount; 89 | 90 | // http request 91 | NSMutableData *_httpRequestData; 92 | 93 | // get all arguments from ack? (https://github.com/pkyeck/socket.IO-objc/pull/85) 94 | BOOL _returnAllDataFromAck; 95 | } 96 | 97 | @property (nonatomic, readonly) NSString *host; 98 | @property (nonatomic, readonly) NSInteger port; 99 | @property (nonatomic, readonly) NSString *sid; 100 | @property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; 101 | @property (nonatomic) BOOL useSecure; 102 | @property (nonatomic, readonly) BOOL isConnected, isConnecting; 103 | @property (nonatomic, unsafe_unretained) id delegate; 104 | @property (nonatomic) BOOL returnAllDataFromAck; 105 | 106 | - (id) initWithDelegate:(id)delegate; 107 | - (void) connectToHost:(NSString *)host onPort:(NSInteger)port; 108 | - (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params; 109 | - (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint; 110 | - (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint withConnectionTimeout: (NSTimeInterval) connectionTimeout; 111 | 112 | - (void) disconnect; 113 | - (void) disconnectForced; 114 | 115 | - (void) sendMessage:(NSString *)data; 116 | - (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function; 117 | - (void) sendJSON:(NSDictionary *)data; 118 | - (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function; 119 | - (void) sendEvent:(NSString *)eventName withData:(id)data; 120 | - (void) sendEvent:(NSString *)eventName withData:(id)data andAcknowledge:(SocketIOCallback)function; 121 | - (void) sendAcknowledgement:(NSString*)pId withArgs:(NSArray *)data; 122 | 123 | - (void) setResourceName:(NSString *)name; 124 | 125 | @end -------------------------------------------------------------------------------- /views/example/backbone/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
Sails.JS Examples: Backbone
7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 17 | 18 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/assets/js/sails.io.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sails.io.js 3 | * 4 | * This file is completely optional, and merely here for your convenience. 5 | * 6 | * It reduces the amount of browser code necessary to send and receive messages 7 | * to & from Sails by simulating a REST client interface on top of socket.io. 8 | * It models its API after the pattern in jQuery you might be familiar with. 9 | * 10 | * So to switch from using AJAX to Socket.io, instead of: 11 | * `$.post( url, [data], [cb] )` 12 | * 13 | * You would use: 14 | * `socket.post( url, [data], [cb] )` 15 | * 16 | * For more information, visit: 17 | * http://sailsjs.org/#documentation 18 | */ 19 | 20 | (function (io) { 21 | 22 | 23 | // We'll be adding methods to `io.SocketNamespace.prototype`, the prototype for the 24 | // Socket instance returned when the browser connects with `io.connect()` 25 | var Socket = io.SocketNamespace; 26 | 27 | 28 | 29 | /** 30 | * Simulate a GET request to sails 31 | * e.g. 32 | * `socket.get('/user/3', Stats.populate)` 33 | * 34 | * @param {String} url :: destination URL 35 | * @param {Object} params :: parameters to send with the request [optional] 36 | * @param {Function} cb :: callback function to call when finished [optional] 37 | */ 38 | 39 | Socket.prototype.get = function (url, data, cb) { 40 | return this.request(url, data, cb, 'get'); 41 | }; 42 | 43 | 44 | 45 | /** 46 | * Simulate a POST request to sails 47 | * e.g. 48 | * `socket.post('/event', newMeeting, $spinner.hide)` 49 | * 50 | * @param {String} url :: destination URL 51 | * @param {Object} params :: parameters to send with the request [optional] 52 | * @param {Function} cb :: callback function to call when finished [optional] 53 | */ 54 | 55 | Socket.prototype.post = function (url, data, cb) { 56 | return this.request(url, data, cb, 'post'); 57 | }; 58 | 59 | 60 | 61 | /** 62 | * Simulate a PUT request to sails 63 | * e.g. 64 | * `socket.post('/event/3', changedFields, $spinner.hide)` 65 | * 66 | * @param {String} url :: destination URL 67 | * @param {Object} params :: parameters to send with the request [optional] 68 | * @param {Function} cb :: callback function to call when finished [optional] 69 | */ 70 | 71 | Socket.prototype.put = function (url, data, cb) { 72 | return this.request(url, data, cb, 'put'); 73 | }; 74 | 75 | 76 | 77 | /** 78 | * Simulate a DELETE request to sails 79 | * e.g. 80 | * `socket.delete('/event', $spinner.hide)` 81 | * 82 | * @param {String} url :: destination URL 83 | * @param {Object} params :: parameters to send with the request [optional] 84 | * @param {Function} cb :: callback function to call when finished [optional] 85 | */ 86 | 87 | Socket.prototype['delete'] = function (url, data, cb) { 88 | return this.request(url, data, cb, 'delete'); 89 | }; 90 | 91 | 92 | 93 | 94 | /** 95 | * Simulate HTTP over Socket.io 96 | * @api private :: but exposed for backwards compatibility w/ <= sails@~0.8 97 | */ 98 | 99 | Socket.prototype.request = request; 100 | function request (url, data, cb, method) { 101 | 102 | var socket = this; 103 | 104 | var usage = 'Usage:\n socket.' + 105 | (method || 'request') + 106 | '( destinationURL, dataToSend, fnToCallWhenComplete )'; 107 | 108 | // Remove trailing slashes and spaces 109 | url = url.replace(/^(.+)\/*\s*$/, '$1'); 110 | 111 | // If method is undefined, use 'get' 112 | method = method || 'get'; 113 | 114 | 115 | if ( typeof url !== 'string' ) { 116 | throw new Error('Invalid or missing URL!\n' + usage); 117 | } 118 | 119 | // Allow data arg to be optional 120 | if ( typeof data === 'function' ) { 121 | cb = data; 122 | data = {}; 123 | } 124 | 125 | // Build to request 126 | var json = window.io.JSON.stringify({ 127 | url: url, 128 | data: data 129 | }); 130 | 131 | 132 | // Send the message over the socket 133 | socket.emit(method, json, function afterEmitted (result) { 134 | 135 | var parsedResult = result; 136 | try { 137 | parsedResult = window.io.JSON.parse(result); 138 | } catch (e) { 139 | if (typeof console !== 'undefined') { 140 | console.warn("Could not parse:", result, e); 141 | } 142 | throw new Error("Server response could not be parsed!\n" + result); 143 | } 144 | 145 | // TODO: Handle errors more effectively 146 | if (parsedResult === 404) throw new Error("404: Not found"); 147 | if (parsedResult === 403) throw new Error("403: Forbidden"); 148 | if (parsedResult === 500) throw new Error("500: Server error"); 149 | 150 | cb && cb(parsedResult); 151 | 152 | }); 153 | } 154 | 155 | 156 | 157 | 158 | }) ( 159 | 160 | // In case you're wrapping socket.io to prevent pollution of the global namespace, 161 | // you can replace `window.io` with your own `io` here: 162 | window.io 163 | 164 | ); 165 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/README.md: -------------------------------------------------------------------------------- 1 | # Socket.IO / Objective C Library 2 | 3 | Interface to communicate between Objective C and [Socket.IO](http://socket.io/) 4 | with the help of websockets or [Long-Polling](http://en.wikipedia.org/wiki/Push_technology#Long_polling). Originally based on fpotter's [socketio-cocoa](https://github.com/fpotter/socketio-cocoa) 5 | it uses other libraries/classes like 6 | 7 | * [SocketRocket](https://github.com/square/SocketRocket) 8 | Look [here](https://github.com/square/SocketRocket#installing-ios) for further instructions how to use/install SocketRocket. 9 | 10 | JSON serialization can be provided by SBJson (json-framework), JSONKit or by Foundation in OS X 10.7/iOS 5.0. These are selected at runtime and introduce no source-level dependencies. 11 | * [json-framework](https://github.com/stig/json-framework/) (optional) 12 | * [JSONKit](https://github.com/johnezang/JSONKit/) (optional) 13 | 14 | ## Requirements 15 | 16 | As of version 0.4, this library requires at least OS X 10.7 or iOS 5.0. 17 | 18 | 19 | ## Non-ARC version 20 | 21 | If you're old school - there's still the [non-ARC version](https://github.com/pkyeck/socket.IO-objc/tree/non-arc) for you. 22 | This version (the non-ARC one) is out-of-date and won't be maintained any further (at least not by me). 23 | 24 | ## Usage 25 | 26 | The easiest way to connect to your Socket.IO / node.js server is 27 | 28 | ``` objective-c 29 | SocketIO *socketIO = [[SocketIO alloc] initWithDelegate:self]; 30 | [socketIO connectToHost:@"localhost" onPort:3000]; 31 | ``` 32 | 33 | If required, additional parameters can be included in the handshake by adding an `NSDictionary` to the `withParams` option: 34 | 35 | ``` objective-c 36 | [socketIO connectToHost:@"localhost" 37 | onPort:3000 38 | withParams:[NSDictionary dictionaryWithObjectsAndKeys:@"1234", @"auth_token", nil] 39 | ]; 40 | ``` 41 | 42 | A namespace can also be defined in the connection details: 43 | 44 | ``` objective-c 45 | [socketIO connectToHost:@"localhost" onPort:3000 withParams:nil withNamespace:@"/users"]; 46 | ``` 47 | 48 | There are different methods to send data to the server 49 | 50 | ``` objective-c 51 | - (void) sendMessage:(NSString *)data; 52 | - (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function; 53 | - (void) sendJSON:(NSDictionary *)data; 54 | - (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function; 55 | - (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data; 56 | - (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data andAcknowledge:(SocketIOCallback)function; 57 | ``` 58 | 59 | So you could send a normal Message like this 60 | 61 | ``` objective-c 62 | [socketIO sendMessage:@"hello world"]; 63 | ``` 64 | 65 | or an Event (including some data) like this 66 | 67 | ``` objective-c 68 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 69 | [dict setObject:@"test1" forKey:@"key1"]; 70 | [dict setObject:@"test2" forKey:@"key2"]; 71 | 72 | [socketIO sendEvent:@"welcome" withData:dict]; 73 | ``` 74 | 75 | If you want the server to acknowledge your Message/Event you would also pass a SocketIOCallback block 76 | 77 | ``` objective-c 78 | SocketIOCallback cb = ^(id argsData) { 79 | NSDictionary *response = argsData; 80 | // do something with response 81 | }; 82 | [socketIO sendEvent:@"welcomeAck" withData:dict andAcknowledge:cb]; 83 | ``` 84 | 85 | All delegate methods are optional - you could implement the following 86 | 87 | ``` objective-c 88 | - (void) socketIODidConnect:(SocketIO *)socket; 89 | - (void) socketIODidDisconnect:(SocketIO *)socket; // deprecated 90 | - (void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error; 91 | - (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet; 92 | - (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet; 93 | - (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet; 94 | - (void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet; 95 | - (void) socketIO:(SocketIO *)socket onError:(NSError *)error; 96 | ``` 97 | 98 | These two callbacks are deprecated - please don't use them anymore - they will 99 | be removed in upcoming releases: 100 | 101 | ``` objective-c 102 | - (void) socketIOHandshakeFailed:(SocketIO *)socket; 103 | - (void) socketIO:(SocketIO *)socket failedToConnectWithError:(NSError *)error; 104 | ``` 105 | 106 | To process an incoming Message just 107 | 108 | ``` objective-c 109 | - (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet 110 | { 111 | NSLog(@"didReceiveMessage() >>> data: %@", packet.data); 112 | } 113 | ``` 114 | 115 | ## Authors 116 | 117 | Initial project by Philipp Kyeck . 118 | Namespace support by Sam Lown at Cabify. 119 | SSL support by kayleg . 120 | Different Socket Libraries + Error Handling by taiyangc . 121 | 122 | ## License 123 | 124 | (The MIT License) 125 | 126 | Copyright (c) 2011-13 Philipp Kyeck 127 | 128 | Permission is hereby granted, free of charge, to any person obtaining a copy 129 | of this software and associated documentation files (the "Software"), to deal 130 | in the Software without restriction, including without limitation the rights 131 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 132 | copies of the Software, and to permit persons to whom the Software is 133 | furnished to do so, subject to the following conditions: 134 | 135 | The above copyright notice and this permission notice shall be included in 136 | all copies or substantial portions of the Software. 137 | 138 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 139 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 140 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 141 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 142 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 143 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 144 | THE SOFTWARE. 145 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SampleAppiOS 4 | // 5 | // Created by Reza Fatahi on 8/8/13. 6 | // Copyright (c) 2013 Rex Fatahi. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | 12 | @interface ViewController () 13 | { 14 | SocketIO* socketIO; 15 | 16 | __weak IBOutlet UILabel *oLabel; 17 | __weak IBOutlet UITextField *oTextField; 18 | __weak IBOutlet UITextView *oTextView; 19 | __weak IBOutlet UITableView *oTableView; 20 | 21 | NSMutableData* mutableData; 22 | NSString* suggestedFilename; 23 | 24 | NSInputStream* inputStream; 25 | NSOutputStream* outputStream; 26 | NSArray* sailsArray; 27 | } 28 | 29 | - (IBAction)submitText:(id)sender; 30 | 31 | @end 32 | 33 | @implementation ViewController 34 | 35 | - (void)viewDidLoad 36 | { 37 | [super viewDidLoad]; 38 | oLabel.hidden = YES; 39 | //handshake 40 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:1337/"] 41 | cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData 42 | timeoutInterval:10]; 43 | 44 | [request setHTTPMethod: @"GET"]; 45 | 46 | NSError *requestError; 47 | NSURLResponse *urlResponse = nil; 48 | NSData *response1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError]; 49 | 50 | //converts response to string (index.html) 51 | NSString* stringFromData = [[NSString alloc] initWithData:response1 encoding:NSUTF8StringEncoding]; 52 | NSLog(@"data converted to string ==> string = %@", stringFromData); 53 | 54 | //init socket.io 55 | socketIO = [[SocketIO alloc] initWithDelegate:self]; 56 | [socketIO connectToHost:@"localhost" onPort:1337]; 57 | 58 | } 59 | 60 | #pragma mark-table view data source 61 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 62 | { 63 | return 1; 64 | } 65 | 66 | 67 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 68 | { 69 | if (sailsArray.count == 0) { 70 | return 1; 71 | } 72 | double delayInSeconds = 2.0; 73 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 74 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 75 | oLabel.hidden = YES; 76 | }); 77 | return sailsArray.count; 78 | } 79 | 80 | 81 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 82 | { 83 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"id"]; 84 | 85 | if (cell == nil) { 86 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"id"]; 87 | } 88 | 89 | if (sailsArray.count == 0) { 90 | cell.textLabel.text = @"array does not exist (yet)"; 91 | return cell; 92 | } 93 | //declare string, assign to value at indexPath from array 94 | //array may be made from [dictionary allKeys]; 95 | NSString* string = [[sailsArray objectAtIndex:indexPath.row] valueForKey:@"text"]; 96 | NSString* subString = [[sailsArray objectAtIndex:indexPath.row] valueForKey:@"createdAt"]; 97 | 98 | //set string to textLabel of cell 99 | [cell.textLabel setText:string]; 100 | [cell.detailTextLabel setText:subString]; 101 | 102 | return cell; 103 | } 104 | 105 | #pragma mark -textField delegate 106 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { 107 | NSUInteger newLength = [textField.text length] + [string length] - range.length; 108 | if (newLength>30) { 109 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Your Text" message:@"is too lengthy" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 110 | [alert show]; 111 | return NO; 112 | } 113 | return YES; 114 | } 115 | 116 | - (BOOL)textFieldShouldReturn:(UITextField *)textField 117 | { 118 | [textField resignFirstResponder]; 119 | return YES; 120 | } 121 | 122 | #pragma mark - NSURLConnectionDelegate 123 | -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 124 | mutableData = [NSMutableData data]; 125 | //suggestedFilename = response.suggestedFilename; 126 | } 127 | 128 | -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 129 | [mutableData appendData:data]; 130 | } 131 | 132 | -(void)connectionDidFinishLoading:(NSURLConnection *)connection { 133 | oTextView.text = [[NSString alloc] initWithData:mutableData encoding:NSUTF8StringEncoding]; 134 | NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://localhost:1337/messages"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10]; 135 | [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { 136 | sailsArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 137 | [oTableView reloadData]; 138 | }]; 139 | } 140 | 141 | -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 142 | NSLog(@"Error"); 143 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:error.localizedDescription delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 144 | [alert show]; 145 | } 146 | 147 | #pragma mark -action methods 148 | - (IBAction)submitText:(id)sender 149 | { 150 | [oTextField resignFirstResponder]; 151 | oLabel.hidden = NO; 152 | NSMutableURLRequest *request = [NSMutableURLRequest 153 | requestWithURL:[NSURL URLWithString:@"http://localhost:1337/messages"]]; 154 | NSString* inputString = oTextField.text; 155 | NSString *params = [[NSString alloc] initWithFormat:@"text=%@", inputString]; 156 | oTextField.text = nil; 157 | [request setHTTPMethod:@"POST"]; 158 | [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; 159 | [[NSURLConnection alloc] initWithRequest:request delegate:self]; 160 | 161 | 162 | } 163 | 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/views/home/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 |

Welcome

23 |

You are currently viewing this with view engine: <% if (locals.defaultEngine) {;%><%- locals.defaultEngine %><%}%>

24 |

Getting started

25 |

Don't worry, we've got your back.

26 |
27 |
    28 |
  • 29 |
    30 |
    31 |

    Get your API going.

    32 |

    33 | Run sails generate foo. This will create a model Foo and controller FooController 34 |

    35 |
    36 |
  • 37 |
  • 38 |
    39 |
    40 |

    41 | Lift your app. 42 |

    43 |

    44 | Run sails lift to start up your app. If you visit http://localhost:1337/foo in your browser, you'll see a socket.io-compatible REST API was generated for your 'Foo' model. 45 |

    46 |
    47 |
  • 48 |
  • 49 |
    50 |
    51 |

    52 | Dive in and start building. 53 |

    54 |

    From here, you can modify your models, create custom controller methods as Express middleware, and create custom routes (routes are set up in config/routes.js). Visit the Sails website for more information on next steps.

    55 |
    56 |
  • 57 |
58 |
59 |
60 |
You're looking at: views/home/index.ejs
61 |
62 |
63 | 93 |
94 | -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/config/sockets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Socket Configuration 3 | * 4 | * These configuration options provide transparent access to Sails' encapsulated 5 | * pubsub/socket server for complete customizability. 6 | * 7 | * For more information on using Sails with Sockets, check out: 8 | * http://sailsjs.org/#documentation 9 | */ 10 | 11 | module.exports.sockets = { 12 | 13 | // A array of allowed transport methods which the clients will try to use. 14 | // The flashsocket transport is disabled by default 15 | // You can enable flashsockets by adding 'flashsocket' to this list: 16 | transports: [ 17 | 'websocket', 18 | 'htmlfile', 19 | 'xhr-polling', 20 | 'jsonp-polling' 21 | ], 22 | 23 | // The data store where socket.io will store its message queue 24 | // and answer pubsub logic 25 | adapter: 'memory', 26 | 27 | 28 | 29 | 30 | // Node.js (and consequently Sails.js) apps scale horizontally. 31 | // It's a powerful, efficient approach, but it involves a tiny bit of planning. 32 | // At scale, you'll want to be able to copy your app onto multiple Sails.js servers 33 | // and throw them behind a load balancer. 34 | // 35 | // One of the big challenges of scaling an application is that these sorts of clustered 36 | // deployments cannot share memory, since they are on physically different machines. 37 | // On top of that, there is no guarantee that a user will "stick" with the same server between 38 | // requests, since the load balancer will route each request to the server with the 39 | // least impact on load. All pubsub processing and shared memory has to be offloaded 40 | // to a shared, remote messaging queue (usually Redis) 41 | // 42 | // Luckily, Sails provides production MQ support for Redis by default! 43 | 44 | // To enable a remote redis pubsub server: 45 | // adapter: 'redis', 46 | 47 | // The IP address and configuration of your redis host: 48 | // (if left unset, Sails will try to connect to a redis via port 6379 on localhost) 49 | // 50 | // host: '127.0.0.1', 51 | // port: 6379, 52 | // db: 'sails', 53 | // pass: '' 54 | 55 | 56 | 57 | // Match string representing the origins that are allowed to connect to the Socket.IO server 58 | origins: '*:*', 59 | 60 | // Should we use heartbeats to check the health of Socket.IO connections? 61 | heartbeats: true, 62 | 63 | // When client closes connection, the # of seconds to wait before attempting a reconnect. 64 | // This value is sent to the client after a successful handshake. 65 | 'close timeout': 60, 66 | 67 | // The # of seconds between heartbeats sent from the client to the server 68 | // This value is sent to the client after a successful handshake. 69 | 'heartbeat timeout': 60, 70 | 71 | // The max # of seconds to wait for an expcted heartbeat before declaring the pipe broken 72 | // This number should be less than the `heartbeat timeout` 73 | 'heartbeat interval': 25, 74 | 75 | // The maximum duration of one HTTP poll- 76 | // if it exceeds this limit it will be closed. 77 | 'polling duration': 20, 78 | 79 | // Enable the flash policy server if the flashsocket transport is enabled 80 | // 'flash policy server': true, 81 | 82 | // By default the Socket.IO client will check port 10843 on your server 83 | // to see if flashsocket connections are allowed. 84 | // The Adobe Flash Player normally uses 843 as default port, 85 | // but Socket.io defaults to a non root port (10843) by default 86 | // 87 | // If you are using a hosting provider that doesn't allow you to start servers 88 | // other than on port 80 or the provided port, and you still want to support flashsockets 89 | // you can set the `flash policy port` to -1 90 | 'flash policy port': 10843, 91 | 92 | // Used by the HTTP transports. The Socket.IO server buffers HTTP request bodies up to this limit. 93 | // This limit is not applied to websocket or flashsockets. 94 | 'destroy buffer size': '10E7', 95 | 96 | // Do we need to destroy non-socket.io upgrade requests? 97 | 'destroy upgrade': true, 98 | 99 | // Should Sails/Socket.io serve the `socket.io.js` client? 100 | // (as well as WebSocketMain.swf for Flash sockets, etc.) 101 | 'browser client': true, 102 | 103 | // Cache the Socket.IO file generation in the memory of the process 104 | // to speed up the serving of the static files. 105 | 'browser client cache': true, 106 | 107 | // Does Socket.IO need to send a minified build of the static client script? 108 | 'browser client minification': false, 109 | 110 | // Does Socket.IO need to send an ETag header for the static requests? 111 | 'browser client etag': false, 112 | 113 | // Adds a Cache-Control: private, x-gzip-ok="", max-age=31536000 header to static requests, 114 | // but only if the file is requested with a version number like /socket.io/socket.io.v0.9.9.js. 115 | 'browser client expires': 315360000, 116 | 117 | // Does Socket.IO need to GZIP the static files? 118 | // This process is only done once and the computed output is stored in memory. 119 | // So we don't have to spawn a gzip process for each request. 120 | 'browser client gzip': false, 121 | 122 | // Optional override function to serve all static files, 123 | // including socket.io.js et al. 124 | // Of the form :: function (req, res) { /* serve files */ } 125 | 'browser client handler': false, 126 | 127 | // Meant to be used when running socket.io behind a proxy. 128 | // Should be set to true when you want the location handshake to match the protocol of the origin. 129 | // This fixes issues with terminating the SSL in front of Node 130 | // and forcing location to think it's wss instead of ws. 131 | 'match origin protocol': false, 132 | 133 | // Global authorization for Socket.IO access, 134 | // this is called when the initial handshake is performed with the server. 135 | // 136 | // By default, Sails verifies that a valid cookie was sent with the upgrade request 137 | // However, in the case of cross-domain requests, no cookies are sent for some transports, 138 | // so sockets will fail to connect. You might also just want to allow anyone to connect w/o a cookie! 139 | // 140 | // To bypass this cookie check, you can set `authorization: false`, 141 | // which will silently create an anonymous cookie+session for the user 142 | // 143 | // `authorization: true` indicates that Sails should use the built-in logic 144 | // 145 | // You can also use your own custom logic with: 146 | // `authorization: function (data, accept) { ... }` 147 | authorization: true, 148 | 149 | // Direct access to the socket.io MQ store config 150 | // The 'adapter' property is the preferred method 151 | // (`undefined` indicates that Sails should defer to the 'adapter' config) 152 | store: undefined, 153 | 154 | // A logger instance that is used to output log information. 155 | // (`undefined` indicates deferment to the main Sails log config) 156 | logger: undefined, 157 | 158 | // The amount of detail that the server should output to the logger. 159 | // (`undefined` indicates deferment to the main Sails log config) 160 | 'log level': undefined, 161 | 162 | // Whether to color the log type when output to the logger. 163 | // (`undefined` indicates deferment to the main Sails log config) 164 | 'log colors': undefined, 165 | 166 | // A Static instance that is used to serve the socket.io client and its dependencies. 167 | // (`undefined` indicates use default) 168 | 'static': undefined, 169 | 170 | // The entry point where Socket.IO starts looking for incoming connections. 171 | // This should be the same between the client and the server. 172 | resource: '/socket.io' 173 | 174 | }; -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/SocketIOTransportXHR.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOTransportXHR.m 3 | // v0.4 ARC 4 | // 5 | // based on 6 | // socketio-cocoa https://github.com/fpotter/socketio-cocoa 7 | // by Fred Potter 8 | // 9 | // using 10 | // https://github.com/square/SocketRocket 11 | // https://github.com/stig/json-framework/ 12 | // 13 | // reusing some parts of 14 | // /socket.io/socket.io.js 15 | // 16 | // Created by Philipp Kyeck http://beta-interactive.de 17 | // 18 | // Updated by 19 | // samlown https://github.com/samlown 20 | // kayleg https://github.com/kayleg 21 | // taiyangc https://github.com/taiyangc 22 | // 23 | 24 | #import "SocketIOTransportXHR.h" 25 | #import "SocketIO.h" 26 | 27 | #define DEBUG_LOGS 0 28 | 29 | #if DEBUG_LOGS 30 | #define DEBUGLOG(...) NSLog(__VA_ARGS__) 31 | #else 32 | #define DEBUGLOG(...) 33 | #endif 34 | 35 | static NSString* kInsecureXHRURL = @"http://%@/socket.io/1/xhr-polling/%@"; 36 | static NSString* kSecureXHRURL = @"https://%@/socket.io/1/xhr-polling/%@"; 37 | static NSString* kInsecureXHRPortURL = @"http://%@:%d/socket.io/1/xhr-polling/%@"; 38 | static NSString* kSecureXHRPortURL = @"https://%@:%d/socket.io/1/xhr-polling/%@"; 39 | 40 | @interface SocketIOTransportXHR (Private) 41 | - (void) checkAndStartPoll; 42 | - (void) poll:(NSString *)data; 43 | - (void) poll:(NSString *)data retryNumber:(int)retry; 44 | @end 45 | 46 | @implementation SocketIOTransportXHR 47 | 48 | @synthesize delegate; 49 | 50 | - (id) initWithDelegate:(id)delegate_ 51 | { 52 | self = [super init]; 53 | if (self) { 54 | self.delegate = delegate_; 55 | _data = [[NSMutableData alloc] init]; 56 | _polls = [[NSMutableDictionary alloc] init]; 57 | } 58 | return self; 59 | } 60 | 61 | - (void) open 62 | { 63 | NSString *format; 64 | if (delegate.port) { 65 | format = delegate.useSecure ? kSecureXHRPortURL : kInsecureXHRPortURL; 66 | _url = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; 67 | } 68 | else { 69 | format = delegate.useSecure ? kSecureXHRURL : kInsecureXHRURL; 70 | _url = [NSString stringWithFormat:format, delegate.host, delegate.sid]; 71 | } 72 | DEBUGLOG(@"Opening XHR @ %@", _url); 73 | [self poll:nil]; 74 | } 75 | 76 | - (void) close 77 | { 78 | NSMutableDictionary *pollData; 79 | NSURLConnection *conn; 80 | for (NSString *key in _polls) { 81 | pollData = [_polls objectForKey:key]; 82 | conn = [pollData objectForKey:@"connection"]; 83 | [conn cancel]; 84 | } 85 | [_polls removeAllObjects]; 86 | } 87 | 88 | - (BOOL) isReady 89 | { 90 | return YES; 91 | } 92 | 93 | - (void) send:(NSString *)request 94 | { 95 | [self poll:request]; 96 | } 97 | 98 | 99 | #pragma mark - 100 | #pragma mark private methods 101 | 102 | - (void) checkAndStartPoll 103 | { 104 | BOOL restart = NO; 105 | // no polls currently running -> start one 106 | if ([_polls count] == 0) { 107 | restart = YES; 108 | } 109 | else { 110 | restart = YES; 111 | // look for polls w/o data -> if there, no need to restart 112 | for (NSString *key in _polls) { 113 | NSMutableDictionary *pollData = [_polls objectForKey:key]; 114 | if ([pollData objectForKey:@"data"] == nil) { 115 | restart = NO; 116 | break; 117 | } 118 | } 119 | } 120 | 121 | if (restart) { 122 | [self poll:nil]; 123 | } 124 | } 125 | 126 | - (void) poll:(NSString *)data 127 | { 128 | [self poll:data retryNumber:0]; 129 | } 130 | 131 | - (void) poll:(NSString *)data retryNumber:(int)retry 132 | { 133 | NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; 134 | double unix = timeStamp * 1000; 135 | NSString *url = [_url stringByAppendingString:[NSString stringWithFormat:@"?t=%.0f", unix]]; 136 | 137 | DEBUGLOG(@"---------------------------------------------------------------------------------------"); 138 | DEBUGLOG(@"poll() %@", url); 139 | 140 | NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] 141 | cachePolicy:NSURLRequestUseProtocolCachePolicy 142 | timeoutInterval:[delegate heartbeatTimeout]]; 143 | if (data != nil) { 144 | DEBUGLOG(@"poll() %@", data); 145 | [req setHTTPMethod:@"POST"]; 146 | [req setValue:@"text/plain; charset=UTF-8" forHTTPHeaderField:@"Content-Type"]; 147 | [req setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]]; 148 | } 149 | [req setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; 150 | 151 | NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self]; 152 | 153 | // add pollData to polls dictionary 154 | NSMutableDictionary *pollData = [[NSMutableDictionary alloc] init]; 155 | [pollData setObject:[NSNumber numberWithInt:retry] forKey:@"retries"]; 156 | [pollData setObject:conn forKey:@"connection"]; 157 | [pollData setValue:data forKey:@"data"]; 158 | [_polls setObject:pollData forKey:conn.description]; 159 | 160 | [conn start]; 161 | } 162 | 163 | 164 | #pragma mark - 165 | #pragma mark NSURLConnection delegate methods 166 | 167 | 168 | - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 169 | { 170 | [_data setLength:0]; 171 | } 172 | 173 | - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 174 | { 175 | DEBUGLOG(@"didReceiveData(): %@", data); 176 | [_data appendData:data]; 177 | } 178 | 179 | - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 180 | { 181 | DEBUGLOG(@"didFailWithError: %@", [error localizedDescription]); 182 | 183 | // retry 3 times or throw error 184 | NSMutableDictionary *pollData = [_polls objectForKey:connection.description]; 185 | NSString *data = [pollData objectForKey:@"data"]; 186 | [_polls removeObjectForKey:connection.description]; 187 | 188 | NSNumber *retries = [pollData objectForKey:@"retries"]; 189 | if ([retries intValue] < 2) { 190 | [self poll:data retryNumber:[retries intValue] + 1]; 191 | } 192 | else { 193 | NSMutableDictionary *errorInfo = [[NSMutableDictionary alloc] init]; 194 | [errorInfo setValue:[error localizedDescription] forKey:@"reason"]; 195 | [errorInfo setValue:data forKey:@"data"]; 196 | 197 | if ([delegate respondsToSelector:@selector(onError:)]) { 198 | [delegate onError:[NSError errorWithDomain:SocketIOError 199 | code:SocketIODataCouldNotBeSend 200 | userInfo:errorInfo]]; 201 | } 202 | } 203 | } 204 | 205 | // Sometimes Socket.IO "batches" up messages in one packet, 206 | // so we have to split them. 207 | // 208 | - (NSArray *)packetsFromPayload:(NSString *)payload 209 | { 210 | // "Batched" format is: 211 | // �[packet_0 length]�[packet_0]�[packet_1 length]�[packet_1]�[packet_n length]�[packet_n] 212 | 213 | if([payload hasPrefix:@"\ufffd"]) { 214 | // Payload has multiple packets, split based on the '�' character 215 | // Skip the first character, then split 216 | NSArray *split = [[payload substringFromIndex:1] componentsSeparatedByString:@"\ufffd"]; 217 | 218 | // Init array with [split count] / 2 because we only need the odd-numbered 219 | NSMutableArray *packets = [NSMutableArray arrayWithCapacity:[split count]/2]; 220 | 221 | // Now all of the odd-numbered indices are the packets (1, 3, 5, etc.) 222 | [split enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 223 | if(idx % 2 != 0) { 224 | [packets addObject:obj]; 225 | } 226 | }]; 227 | 228 | NSLog(@"Parsed a payload!"); 229 | return packets; 230 | } else { 231 | // Regular single-packet payload 232 | return @[payload]; 233 | } 234 | } 235 | 236 | - (void) connectionDidFinishLoading:(NSURLConnection *)connection 237 | { 238 | NSString *message = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]; 239 | DEBUGLOG(@"response: __%@__", message); 240 | 241 | if (![message isEqualToString:@"1"]) { 242 | NSArray *messages = [self packetsFromPayload:message]; 243 | [messages enumerateObjectsUsingBlock:^(NSString *message, NSUInteger idx, BOOL *stop) { 244 | [delegate onData:message]; 245 | }]; 246 | } 247 | 248 | // remove current connection from pool 249 | [_polls removeObjectForKey:connection.description]; 250 | 251 | [self checkAndStartPoll]; 252 | } 253 | 254 | 255 | @end 256 | -------------------------------------------------------------------------------- /public/js/knockout.mapping-latest.js: -------------------------------------------------------------------------------- 1 | /// Knockout Mapping plugin v2.4.1 2 | /// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/ 3 | /// License: MIT (http://www.opensource.org/licenses/mit-license.php) 4 | (function(e){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?e(require("knockout"),exports):"function"===typeof define&&define.amd?define(["knockout","exports"],e):e(ko,ko.mapping={})})(function(e,f){function y(b,c){var a,d;for(d in c)if(c.hasOwnProperty(d)&&c[d])if(a=f.getType(b[d]),d&&b[d]&&"array"!==a&&"string"!==a)y(b[d],c[d]);else if("array"===f.getType(b[d])&&"array"===f.getType(c[d])){a=b;for(var e=d,l=b[d],n=c[d],t={},g=l.length-1;0<=g;--g)t[l[g]]=l[g];for(g= 5 | n.length-1;0<=g;--g)t[n[g]]=n[g];l=[];n=void 0;for(n in t)l.push(t[n]);a[e]=l}else b[d]=c[d]}function E(b,c){var a={};y(a,b);y(a,c);return a}function z(b,c){for(var a=E({},b),e=L.length-1;0<=e;e--){var f=L[e];a[f]&&(a[""]instanceof Object||(a[""]={}),a[""][f]=a[f],delete a[f])}c&&(a.ignore=h(c.ignore,a.ignore),a.include=h(c.include,a.include),a.copy=h(c.copy,a.copy),a.observe=h(c.observe,a.observe));a.ignore=h(a.ignore,j.ignore);a.include=h(a.include,j.include);a.copy=h(a.copy,j.copy);a.observe=h(a.observe, 6 | j.observe);a.mappedProperties=a.mappedProperties||{};a.copiedProperties=a.copiedProperties||{};return a}function h(b,c){"array"!==f.getType(b)&&(b="undefined"===f.getType(b)?[]:[b]);"array"!==f.getType(c)&&(c="undefined"===f.getType(c)?[]:[c]);return e.utils.arrayGetDistinctValues(b.concat(c))}function F(b,c,a,d,k,l,n){var t="array"===f.getType(e.utils.unwrapObservable(c));l=l||"";if(f.isMapped(b)){var g=e.utils.unwrapObservable(b)[p];a=E(g,a)}var j=n||k,h=function(){return a[d]&&a[d].create instanceof 7 | Function},x=function(b){var f=G,g=e.dependentObservable;e.dependentObservable=function(a,b,c){c=c||{};a&&"object"==typeof a&&(c=a);var d=c.deferEvaluation,M=!1;c.deferEvaluation=!0;a=new H(a,b,c);if(!d){var g=a,d=e.dependentObservable;e.dependentObservable=H;a=e.isWriteableObservable(g);e.dependentObservable=d;d=H({read:function(){M||(e.utils.arrayRemoveItem(f,g),M=!0);return g.apply(g,arguments)},write:a&&function(a){return g(a)},deferEvaluation:!0});d.__DO=g;a=d;f.push(a)}return a};e.dependentObservable.fn= 8 | H.fn;e.computed=e.dependentObservable;b=e.utils.unwrapObservable(k)instanceof Array?a[d].create({data:b||c,parent:j,skip:N}):a[d].create({data:b||c,parent:j});e.dependentObservable=g;e.computed=e.dependentObservable;return b},u=function(){return a[d]&&a[d].update instanceof Function},v=function(b,f){var g={data:f||c,parent:j,target:e.utils.unwrapObservable(b)};e.isWriteableObservable(b)&&(g.observable=b);return a[d].update(g)};if(n=I.get(c))return n;d=d||"";if(t){var t=[],s=!1,m=function(a){return a}; 9 | a[d]&&a[d].key&&(m=a[d].key,s=!0);e.isObservable(b)||(b=e.observableArray([]),b.mappedRemove=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.remove(function(a){return c(m(a))})},b.mappedRemoveAll=function(a){var c=C(a,m);return b.remove(function(a){return-1!=e.utils.arrayIndexOf(c,m(a))})},b.mappedDestroy=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.destroy(function(a){return c(m(a))})},b.mappedDestroyAll=function(a){var c=C(a,m);return b.destroy(function(a){return-1!= 10 | e.utils.arrayIndexOf(c,m(a))})},b.mappedIndexOf=function(a){var c=C(b(),m);a=m(a);return e.utils.arrayIndexOf(c,a)},b.mappedGet=function(a){return b()[b.mappedIndexOf(a)]},b.mappedCreate=function(a){if(-1!==b.mappedIndexOf(a))throw Error("There already is an object with the key that you specified.");var c=h()?x(a):a;u()&&(a=v(c,a),e.isWriteableObservable(c)?c(a):c=a);b.push(c);return c});n=C(e.utils.unwrapObservable(b),m).sort();g=C(c,m);s&&g.sort();s=e.utils.compareArrays(n,g);n={};var J,A=e.utils.unwrapObservable(c), 11 | y={},z=!0,g=0;for(J=A.length;g 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 48 | 49 | 50 | 51 | 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 | -------------------------------------------------------------------------------- /SampleAppiOS/SampleAppiOS/base64.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1996 by Internet Software Consortium. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 11 | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 12 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 13 | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 16 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 17 | * SOFTWARE. 18 | */ 19 | 20 | /* 21 | * Portions Copyright (c) 1995 by International Business Machines, Inc. 22 | * 23 | * International Business Machines, Inc. (hereinafter called IBM) grants 24 | * permission under its copyrights to use, copy, modify, and distribute this 25 | * Software with or without fee, provided that the above copyright notice and 26 | * all paragraphs of this notice appear in all copies, and that the name of IBM 27 | * not be used in connection with the marketing of any product incorporating 28 | * the Software or modifications thereof, without specific, written prior 29 | * permission. 30 | * 31 | * To the extent it has a right to do so, IBM grants an immunity from suit 32 | * under its patents, if any, for the use, sale or manufacture of products to 33 | * the extent that such products are used for performing Domain Name System 34 | * dynamic updates in TCP/IP networks by means of the Software. No immunity is 35 | * granted for any product per se or for any other function of any product. 36 | * 37 | * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, 38 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 39 | * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, 40 | * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING 41 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN 42 | * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. 43 | */ 44 | 45 | /* OPENBSD ORIGINAL: lib/libc/net/base64.c */ 46 | 47 | 48 | #if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | 62 | #include "base64.h" 63 | 64 | static const char Base64[] = 65 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 66 | static const char Pad64 = '='; 67 | 68 | /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) 69 | The following encoding technique is taken from RFC 1521 by Borenstein 70 | and Freed. It is reproduced here in a slightly edited form for 71 | convenience. 72 | 73 | A 65-character subset of US-ASCII is used, enabling 6 bits to be 74 | represented per printable character. (The extra 65th character, "=", 75 | is used to signify a special processing function.) 76 | 77 | The encoding process represents 24-bit groups of input bits as output 78 | strings of 4 encoded characters. Proceeding from left to right, a 79 | 24-bit input group is formed by concatenating 3 8-bit input groups. 80 | These 24 bits are then treated as 4 concatenated 6-bit groups, each 81 | of which is translated into a single digit in the base64 alphabet. 82 | 83 | Each 6-bit group is used as an index into an array of 64 printable 84 | characters. The character referenced by the index is placed in the 85 | output string. 86 | 87 | Table 1: The Base64 Alphabet 88 | 89 | Value Encoding Value Encoding Value Encoding Value Encoding 90 | 0 A 17 R 34 i 51 z 91 | 1 B 18 S 35 j 52 0 92 | 2 C 19 T 36 k 53 1 93 | 3 D 20 U 37 l 54 2 94 | 4 E 21 V 38 m 55 3 95 | 5 F 22 W 39 n 56 4 96 | 6 G 23 X 40 o 57 5 97 | 7 H 24 Y 41 p 58 6 98 | 8 I 25 Z 42 q 59 7 99 | 9 J 26 a 43 r 60 8 100 | 10 K 27 b 44 s 61 9 101 | 11 L 28 c 45 t 62 + 102 | 12 M 29 d 46 u 63 / 103 | 13 N 30 e 47 v 104 | 14 O 31 f 48 w (pad) = 105 | 15 P 32 g 49 x 106 | 16 Q 33 h 50 y 107 | 108 | Special processing is performed if fewer than 24 bits are available 109 | at the end of the data being encoded. A full encoding quantum is 110 | always completed at the end of a quantity. When fewer than 24 input 111 | bits are available in an input group, zero bits are added (on the 112 | right) to form an integral number of 6-bit groups. Padding at the 113 | end of the data is performed using the '=' character. 114 | 115 | Since all base64 input is an integral number of octets, only the 116 | ------------------------------------------------- 117 | following cases can arise: 118 | 119 | (1) the final quantum of encoding input is an integral 120 | multiple of 24 bits; here, the final unit of encoded 121 | output will be an integral multiple of 4 characters 122 | with no "=" padding, 123 | (2) the final quantum of encoding input is exactly 8 bits; 124 | here, the final unit of encoded output will be two 125 | characters followed by two "=" padding characters, or 126 | (3) the final quantum of encoding input is exactly 16 bits; 127 | here, the final unit of encoded output will be three 128 | characters followed by one "=" padding character. 129 | */ 130 | 131 | #if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) 132 | int 133 | b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) 134 | { 135 | size_t datalength = 0; 136 | u_char input[3]; 137 | u_char output[4]; 138 | u_int i; 139 | 140 | while (2 < srclength) { 141 | input[0] = *src++; 142 | input[1] = *src++; 143 | input[2] = *src++; 144 | srclength -= 3; 145 | 146 | output[0] = input[0] >> 2; 147 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 148 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 149 | output[3] = input[2] & 0x3f; 150 | 151 | if (datalength + 4 > targsize) 152 | return (-1); 153 | target[datalength++] = Base64[output[0]]; 154 | target[datalength++] = Base64[output[1]]; 155 | target[datalength++] = Base64[output[2]]; 156 | target[datalength++] = Base64[output[3]]; 157 | } 158 | 159 | /* Now we worry about padding. */ 160 | if (0 != srclength) { 161 | /* Get what's left. */ 162 | input[0] = input[1] = input[2] = '\0'; 163 | for (i = 0; i < srclength; i++) 164 | input[i] = *src++; 165 | 166 | output[0] = input[0] >> 2; 167 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 168 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 169 | 170 | if (datalength + 4 > targsize) 171 | return (-1); 172 | target[datalength++] = Base64[output[0]]; 173 | target[datalength++] = Base64[output[1]]; 174 | if (srclength == 1) 175 | target[datalength++] = Pad64; 176 | else 177 | target[datalength++] = Base64[output[2]]; 178 | target[datalength++] = Pad64; 179 | } 180 | if (datalength >= targsize) 181 | return (-1); 182 | target[datalength] = '\0'; /* Returned value doesn't count \0. */ 183 | return (datalength); 184 | } 185 | #endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ 186 | 187 | #if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) 188 | 189 | /* skips all whitespace anywhere. 190 | converts characters, four at a time, starting at (or after) 191 | src from base - 64 numbers into three 8 bit bytes in the target area. 192 | it returns the number of data bytes stored at the target, or -1 on error. 193 | */ 194 | 195 | int 196 | b64_pton(char const *src, u_char *target, size_t targsize) 197 | { 198 | u_int tarindex, state; 199 | int ch; 200 | char *pos; 201 | 202 | state = 0; 203 | tarindex = 0; 204 | 205 | while ((ch = *src++) != '\0') { 206 | if (isspace(ch)) /* Skip whitespace anywhere. */ 207 | continue; 208 | 209 | if (ch == Pad64) 210 | break; 211 | 212 | pos = strchr(Base64, ch); 213 | if (pos == 0) /* A non-base64 character. */ 214 | return (-1); 215 | 216 | switch (state) { 217 | case 0: 218 | if (target) { 219 | if (tarindex >= targsize) 220 | return (-1); 221 | target[tarindex] = (pos - Base64) << 2; 222 | } 223 | state = 1; 224 | break; 225 | case 1: 226 | if (target) { 227 | if (tarindex + 1 >= targsize) 228 | return (-1); 229 | target[tarindex] |= (pos - Base64) >> 4; 230 | target[tarindex+1] = ((pos - Base64) & 0x0f) 231 | << 4 ; 232 | } 233 | tarindex++; 234 | state = 2; 235 | break; 236 | case 2: 237 | if (target) { 238 | if (tarindex + 1 >= targsize) 239 | return (-1); 240 | target[tarindex] |= (pos - Base64) >> 2; 241 | target[tarindex+1] = ((pos - Base64) & 0x03) 242 | << 6; 243 | } 244 | tarindex++; 245 | state = 3; 246 | break; 247 | case 3: 248 | if (target) { 249 | if (tarindex >= targsize) 250 | return (-1); 251 | target[tarindex] |= (pos - Base64); 252 | } 253 | tarindex++; 254 | state = 0; 255 | break; 256 | } 257 | } 258 | 259 | /* 260 | * We are done decoding Base-64 chars. Let's see if we ended 261 | * on a byte boundary, and/or with erroneous trailing characters. 262 | */ 263 | 264 | if (ch == Pad64) { /* We got a pad char. */ 265 | ch = *src++; /* Skip it, get next. */ 266 | switch (state) { 267 | case 0: /* Invalid = in first position */ 268 | case 1: /* Invalid = in second position */ 269 | return (-1); 270 | 271 | case 2: /* Valid, means one byte of info */ 272 | /* Skip any number of spaces. */ 273 | for (; ch != '\0'; ch = *src++) 274 | if (!isspace(ch)) 275 | break; 276 | /* Make sure there is another trailing = sign. */ 277 | if (ch != Pad64) 278 | return (-1); 279 | ch = *src++; /* Skip the = */ 280 | /* Fall through to "single trailing =" case. */ 281 | /* FALLTHROUGH */ 282 | 283 | case 3: /* Valid, means two bytes of info */ 284 | /* 285 | * We know this char is an =. Is there anything but 286 | * whitespace after it? 287 | */ 288 | for (; ch != '\0'; ch = *src++) 289 | if (!isspace(ch)) 290 | return (-1); 291 | 292 | /* 293 | * Now make sure for cases 2 and 3 that the "extra" 294 | * bits that slopped past the last full byte were 295 | * zeros. If we don't check them, they become a 296 | * subliminal channel. 297 | */ 298 | if (target && target[tarindex] != 0) 299 | return (-1); 300 | } 301 | } else { 302 | /* 303 | * We ended by seeing the end of the string. Make sure we 304 | * have no partial bytes lying around. 305 | */ 306 | if (state != 0) 307 | return (-1); 308 | } 309 | 310 | return (tarindex); 311 | } 312 | 313 | #endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */ 314 | #endif 315 | -------------------------------------------------------------------------------- /public/js/underscore.min.js: -------------------------------------------------------------------------------- 1 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /SampleAppiOS/README.md: -------------------------------------------------------------------------------- 1 | Sails Tutorial for iOS 2 | ================================ 3 | 4 | How to get started developing full stack applications with Sails 5 | ------------------------- 6 | 7 | We are going to make an application for the iPhone in XCode v4.6 that displays messages from the Sails server to the iPhone. To get started, install Sails: 8 | 9 | *Click [here to get started with installing Sails](https://github.com/balderdashy/sails).* 10 | 11 | Now that you have Sails installed, let's create a Sails project. The first thing we will do is create the Xcode project. In Xcode, create a Single View application, and give it the name SampleAppSails. 12 | 13 | Open Terminal, and go to your Xcode project's folder. Mine was on the desktop, and my path was ```/User/aug2uag/desktop/SampleAppSails/```. Once you are in your Xcode project directory, create the Sails application. Note, that we could have initially created the Sails application, and included the Xcode project in that directory. Since the Sails directory includes multiple other directories, I opted to organize it as mentioned above. 14 | 15 | To create your new Sails project, in terminal type: 16 | 17 | sails new sailsXcodeProject 18 | 19 | You will notice Sails installs with many features right away. Among these are automagically generated RESTful API requests that we will be using for our application. Let's open our Sails project files, and inspect these elements. 20 | 21 | In ```/config``` you will find the filename controllers.js that includes instructions for a blueprint. The blueprint is a prototype for the actual Sails application. Here, the blueprint for the app is specified to include: 22 | 23 | // Automatic REST blueprints enabled? 24 | // e.g. 25 | // 'get /:controller/:id?' 26 | // 'post /:controller' 27 | // 'put /:controller/:id' 28 | // 'delete /:controller/:id' 29 | rest: true 30 | 31 | If you like to disable the default methods, and write your own custom methods, then you can set the parameter to false. 32 | 33 | For the purposes of the application we are developing, the default controller methods will be selected to perform our actions. Specifically, we will want to: 34 | 35 | * POST a message to the server, that includes our text and userId 36 | * GET the messages from the server, and display them as a list on the iPhone 37 | 38 | We will create a single Messages entity: 39 | 40 | sails g Messages 41 | 42 | This has created the Messages model, and controller. Let's open the model from the /api directory of your Sails application, and add the following attributes: 43 | 44 | module.exports = { 45 | attributes: {< 46 | text: 'string' 47 | } 48 | }; 49 | 50 | This says that we will be storing Messages objects on the server, and that the messages object will consist of text and timestamp. Note that a timestamp will be generated automatically each time a new object is made. For now, let's move on, and take a look at the MessagesController.js file found in the ```/api``` directory of your Sails application: 51 | 52 | module.exports = { 53 | sayHello: function (req, res) { 54 | res.send('hello world!'); 55 | } 56 | }; 57 | 58 | There is a simple Hello World function named ```sayHello``` to demonstrate the functional style language used in Sails that is common to Node. Each function operates in a block, and that makes it possible for the program to continue without the need for the result of the function to return. We will be sticking with the default controller methods, and those are abstracted away from us, unless we had decided to write our own in the controller. 59 | 60 | We can demonstrate the use of the functions, by calling a HTTP GET request to the server. Since the function resides in the MessagesController in the /messages route, it will be accessible via the route ```http://localhost:1337/messages/sayHello``` and you can make a request or use a REST client such as 'Postman' to demonstrate the output. 61 | 62 | Let's link up a Mongo database to hold our information. I already have my Mongo shell running on port 27017, fire up your mongod, and in the /config/session.js file add the following: 63 | 64 | module.exports.session = { 65 | secret: '7309c3e86f54d10dbcdf2b4e113ab393', 66 | adapter: 'mongo', 67 | host: 'localhost', 68 | port: 27017, 69 | db: 'sails', 70 | collection: 'sessions' 71 | }; 72 | 73 | We are telling Sails that for our project (secret: value that is automatically generated for each application), we are going to be using (adapter: mongo), in path (host & port: local/1337), a database named 'sails' that is stored with other databases in a collection. 74 | 75 | Save everything, and run your Sails application, and you should see your Sails application operating on localhost:1337. You can also verify your app is running by opening a new shell, and running:```curl http://localhost:1337``` 76 | 77 | Let's move on, and create the iPhone client. 78 | 79 | Your SampleApplicationSails.xcodeproj should be open on XCode. Go to your storyboard or xib file, and add: 80 | * tableView 81 | * textField 82 | * button 83 | 84 | Connect these UI elements to the @interface of the ViewController.m file. Make sure you connect your tableView data source outlet to the ViewController. Create an NSArray instance variable, and your ViewController.m @interface should look something like: 85 | 86 | @interface ViewController () 87 | { 88 | SocketIO* socketIO; 89 | 90 | __weak IBOutlet UITextField *oTextField; 91 | __weak IBOutlet UITableView *oTableView; 92 | 93 | NSArray* sailsArray; 94 | } 95 | 96 | - (IBAction)submitText:(id)sender; 97 | 98 | @end 99 | 100 | Create your tableViewDataSource methods, with logic to handle the empty array (i.e. since it may be initially empty): 101 | 102 | #pragma mark-table view data source 103 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 104 | { 105 | return 1; 106 | } 107 | 108 | 109 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 110 | { 111 | if (sailsArray.count == 0) { 112 | return 1; 113 | } 114 | return sailsArray.count; 115 | } 116 | 117 | 118 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 119 | { 120 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"id"]; 121 | 122 | if (cell == nil) { 123 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"id"]; 124 | } 125 | 126 | if (sailsArray.count == 0) { 127 | cell.textLabel.text = @"array does not exist (yet)"; 128 | return cell; 129 | } 130 | //declare string, assign to value at indexPath from array 131 | //array may be made from [dictionary allKeys]; 132 | NSString* string = [[sailsArray objectAtIndex:indexPath.row] valueForKey:@"text"]; 133 | NSString* subString = [[sailsArray objectAtIndex:indexPath.row] valueForKey:@"createdAt"]; 134 | 135 | //set string to textLabel of cell 136 | [cell.textLabel setText:string]; 137 | [cell.detailTextLabel setText:subString]; 138 | 139 | return cell; 140 | } 141 | 142 | To connect to our server (that should be running in the background), we need to create a socket to communicate to the server, and establish connection to the server's controller methods. Since Sails ships with Socket.io, we will establish the socket with Socket.io for Objective-C. 143 | *Add the Socket.io files from [Socket.IO / Objective C Library on Github](href=https://github.com/pkyeck/socket.IO-objc).* 144 | 145 | To your ViewController.h, add: 146 | 147 | #import "SocketIO.h" 148 | 149 | Make ViewController conform to the SocketIO delegate methods by adding SocketIODelegate next to your ViewController. 150 | 151 | In your ViewController.m file, create a SocketIO instance variable name socketIO. In the ViewController.m @implementation viewDidLoad, add the following: 152 | 153 | socketIO = [[SocketIO alloc] initWithDelegate:self]; 154 | [socketIO connectToHost:@"localhost" onPort:1337]; 155 | 156 | Run your Xcode project, and let's see what happens. Take a look at your shell running your Sails application, and you will notice that we received an error that no handshake occured. To address this, we will make a call to the Sails application prior to establishing the socket. In your ViewController.m, add the following before your code that instantiates your SocketIO object: 157 | 158 | //handshake 159 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:1337/"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10]; 160 | 161 | [request setHTTPMethod: @"GET"]; 162 | 163 | NSError *requestError; 164 | NSURLResponse *urlResponse = nil; 165 | NSData *response1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];``` 166 | 167 | Also add the following to view the results of the GET request: 168 | 169 | //converts response to string (index.html) 170 | NSString* stringFromData = [[NSString alloc] initWithData:response1 encoding:NSUTF8StringEncoding]; 171 | NSLog(@"data converted to string ==> string = %@", stringFromData); 172 | 173 | Run your Xcode project again, and you should see a log that displays the contents of your ```/views/home/index.ejs``` file in your Sails directory. 174 | 175 | We will send a POST request to the server for the message we will type in our TextField. We will limit the size of our input by using the UITextField delegate method: 176 | 177 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 178 | 179 | In your viewDidLoad set the textField delegate to self, and in the ViewController.m file, add the following: 180 | 181 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { 182 | NSUInteger newLength = [textField.text length] + [string length] - range.length; 183 | if (newLength>30) { 184 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Your Text" message:@"is too lengthy" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 185 | [alert show]; 186 | return NO; 187 | } 188 | return YES; 189 | } 190 | 191 | We are limiting the textField to 30 characters, and sending an alert if the count is higher than our limit. So once we have that in place, we can create our submit POST action. To your button action method, add the following: 192 | 193 | #pragma mark - textField delegate 194 | - (IBAction)submitText:(id)sender 195 | { 196 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:1337/messages"]]; 197 | NSString* inputString = oTextField.text; 198 | NSString *params = [[NSString alloc] initWithFormat:@"text=%@", inputString]; 199 | oTextField.text = nil; 200 | [request setHTTPMethod:@"POST"]; 201 | [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; 202 | [[NSURLConnection alloc] initWithRequest:request delegate:self]; 203 | } 204 | 205 | We will evaluate the response by logging it to our console via the NSURLConnection delegate. Declare a NSMutableData variable to your ViewController.m @interface. Add the following to implement your NSURLConnection delegate: 206 | 207 | #pragma mark - NSURLConnectionDelegate 208 | -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 209 | mutableData = [NSMutableData data]; 210 | } 211 | 212 | -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 213 | [mutableData appendData:data]; 214 | } 215 | 216 | -(void)connectionDidFinishLoading:(NSURLConnection *)connection { 217 | NSLog(@"response => %@",[[NSString alloc] initWithData:mutableData encoding:NSUTF8StringEncoding]); 218 | 219 | } 220 | 221 | -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 222 | NSLog(@"Error"); 223 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:error.localizedDescription delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 224 | [alert show]; 225 | } 226 | 227 | Run your Xcode project again, and you should see the status of your POST request to the server. 228 | 229 | Now that we can access when the POST request did finish, we will call the server to recieve a list of the messages on the server to display on the tableView. To do this, we will send a GET request to the Messages controller of our Sails application, and assign the result to our NSArray variable we defined a while back. When we refresh our tableView, the results are now displayed. 230 | 231 | In Summary 232 | ---------- 233 | Sails is an amazingly efficient, and user-friendly technology to turn your iOS applications in to something so much more. I hope this tutorial was a chance for getting your feet wet in iOS with Sails. For more references, please browse the online documentation. Cheers! -------------------------------------------------------------------------------- /SampleAppiOS/sampleApp/Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gruntfile 3 | * 4 | * If you created your Sails app with `sails new foo --linker`, 5 | * the following files will be automatically injected (in order) 6 | * into the EJS and HTML files in your `views` and `assets` folders. 7 | * 8 | * At the top part of this file, you'll find a few of the most commonly 9 | * configured options, but Sails' integration with Grunt is also fully 10 | * customizable. If you'd like to work with your assets differently 11 | * you can change this file to do anything you like! 12 | * 13 | * More information on using Grunt to work with static assets: 14 | * http://gruntjs.com/configuring-tasks 15 | */ 16 | 17 | module.exports = function (grunt) { 18 | 19 | 20 | 21 | /** 22 | * CSS files to inject in order 23 | * (uses Grunt-style wildcard/glob/splat expressions) 24 | * 25 | * By default, Sails also supports LESS in development and production. 26 | * To use SASS/SCSS, Stylus, etc., edit the `sails-linker:devStyles` task 27 | * below for more options. For this to work, you may need to install new 28 | * dependencies, e.g. `npm install grunt-contrib-sass` 29 | */ 30 | 31 | var cssFilesToInject = [ 32 | 'linker/**/*.css' 33 | ]; 34 | 35 | 36 | /** 37 | * Javascript files to inject in order 38 | * (uses Grunt-style wildcard/glob/splat expressions) 39 | * 40 | * To use client-side CoffeeScript, TypeScript, etc., edit the 41 | * `sails-linker:devJs` task below for more options. 42 | */ 43 | 44 | var jsFilesToInject = [ 45 | 46 | // Below, as a demonstration, you'll see the built-in dependencies 47 | // linked in the proper order order 48 | 49 | // Bring in the socket.io client 50 | 'linker/js/socket.io.js', 51 | 52 | // then beef it up with some convenience logic for talking to Sails.js 53 | 'linker/js/sails.io.js', 54 | 55 | // A simpler boilerplate library for getting you up and running w/ an 56 | // automatic listener for incoming messages from Socket.io. 57 | 'linker/js/app.js', 58 | 59 | // *-> put other dependencies here <-* 60 | 61 | // All of the rest of your app scripts imported here 62 | 'linker/**/*.js' 63 | ]; 64 | 65 | 66 | /** 67 | * Client-side HTML templates are injected using the sources below 68 | * The ordering of these templates shouldn't matter. 69 | * (uses Grunt-style wildcard/glob/splat expressions) 70 | * 71 | * By default, Sails uses JST templates and precompiles them into 72 | * functions for you. If you want to use jade, handlebars, dust, etc., 73 | * edit the relevant sections below. 74 | */ 75 | 76 | var templateFilesToInject = [ 77 | 'linker/**/*.html' 78 | ]; 79 | 80 | 81 | 82 | ///////////////////////////////////////////////////////////////// 83 | ///////////////////////////////////////////////////////////////// 84 | ///////////////////////////////////////////////////////////////// 85 | ///////////////////////////////////////////////////////////////// 86 | ///////////////////////////////////////////////////////////////// 87 | ///////////////////////////////////////////////////////////////// 88 | ///////////////////////////////////////////////////////////////// 89 | ///////////////////////////////////////////////////////////////// 90 | ///////////////////////////////////////////////////////////////// 91 | ///////////////////////////////////////////////////////////////// 92 | // 93 | // DANGER: 94 | // 95 | // With great power comes great responsibility. 96 | // 97 | ///////////////////////////////////////////////////////////////// 98 | ///////////////////////////////////////////////////////////////// 99 | ///////////////////////////////////////////////////////////////// 100 | ///////////////////////////////////////////////////////////////// 101 | ///////////////////////////////////////////////////////////////// 102 | ///////////////////////////////////////////////////////////////// 103 | ///////////////////////////////////////////////////////////////// 104 | ///////////////////////////////////////////////////////////////// 105 | ///////////////////////////////////////////////////////////////// 106 | ///////////////////////////////////////////////////////////////// 107 | 108 | // Modify css file injection paths to use 109 | cssFilesToInject = cssFilesToInject.map(function (path) { 110 | return '.tmp/public/' + path; 111 | }); 112 | 113 | // Modify js file injection paths to use 114 | jsFilesToInject = jsFilesToInject.map(function (path) { 115 | return '.tmp/public/' + path; 116 | }); 117 | 118 | 119 | templateFilesToInject = templateFilesToInject.map(function (path) { 120 | return 'assets/' + path; 121 | }); 122 | 123 | 124 | // Get path to core grunt dependencies from Sails 125 | var depsPath = grunt.option('gdsrc') || 'node_modules/sails/node_modules'; 126 | grunt.loadTasks(depsPath + '/grunt-contrib-clean/tasks'); 127 | grunt.loadTasks(depsPath + '/grunt-contrib-copy/tasks'); 128 | grunt.loadTasks(depsPath + '/grunt-contrib-concat/tasks'); 129 | grunt.loadTasks(depsPath + '/grunt-sails-linker/tasks'); 130 | grunt.loadTasks(depsPath + '/grunt-contrib-jst/tasks'); 131 | grunt.loadTasks(depsPath + '/grunt-contrib-watch/tasks'); 132 | grunt.loadTasks(depsPath + '/grunt-contrib-uglify/tasks'); 133 | grunt.loadTasks(depsPath + '/grunt-contrib-cssmin/tasks'); 134 | grunt.loadTasks(depsPath + '/grunt-contrib-less/tasks'); 135 | 136 | // Project configuration. 137 | grunt.initConfig({ 138 | pkg: grunt.file.readJSON('package.json'), 139 | 140 | copy: { 141 | dev: { 142 | files: [ 143 | { 144 | expand: true, 145 | cwd: './assets', 146 | src: ['**/*'], 147 | dest: '.tmp/public' 148 | } 149 | ] 150 | }, 151 | build: { 152 | files: [ 153 | { 154 | expand: true, 155 | cwd: '.tmp/public', 156 | src: ['**/*'], 157 | dest: 'www' 158 | } 159 | ] 160 | } 161 | }, 162 | 163 | clean: { 164 | dev: ['.tmp/public/**'], 165 | build: ['www'] 166 | }, 167 | 168 | jst: { 169 | dev: { 170 | options: { 171 | templateSettings: { 172 | interpolate: /\{\{(.+?)\}\}/g 173 | } 174 | }, 175 | files: { 176 | '.tmp/public/jst.js': templateFilesToInject 177 | } 178 | } 179 | }, 180 | 181 | less: { 182 | dev: { 183 | files: [ 184 | { 185 | expand: true, 186 | cwd: 'assets/styles/', 187 | src: ['*.less'], 188 | dest: '.tmp/public/styles/', 189 | ext: '.css' 190 | }, { 191 | expand: true, 192 | cwd: 'assets/linker/styles/', 193 | src: ['*.less'], 194 | dest: '.tmp/public/linker/styles/', 195 | ext: '.css' 196 | } 197 | ] 198 | } 199 | }, 200 | 201 | concat: { 202 | js: { 203 | src: jsFilesToInject, 204 | dest: '.tmp/public/concat/production.js' 205 | }, 206 | css: { 207 | src: cssFilesToInject, 208 | dest: '.tmp/public/concat/production.css' 209 | } 210 | }, 211 | 212 | uglify: { 213 | dist: { 214 | src: ['.tmp/public/concat/production.js'], 215 | dest: '.tmp/public/min/production.js' 216 | } 217 | }, 218 | 219 | cssmin: { 220 | dist: { 221 | src: ['.tmp/public/concat/production.css'], 222 | dest: '.tmp/public/min/production.css' 223 | } 224 | }, 225 | 226 | 'sails-linker': { 227 | 228 | devJs: { 229 | options: { 230 | startTag: '', 231 | endTag: '', 232 | fileTmpl: '', 233 | appRoot: '.tmp/public' 234 | }, 235 | files: { 236 | '.tmp/public/**/*.html': jsFilesToInject, 237 | 'views/**/*.html': jsFilesToInject, 238 | 'views/**/*.ejs': jsFilesToInject 239 | } 240 | }, 241 | 242 | prodJs: { 243 | options: { 244 | startTag: '', 245 | endTag: '', 246 | fileTmpl: '', 247 | appRoot: '.tmp/public' 248 | }, 249 | files: { 250 | '.tmp/public/**/*.html': ['.tmp/public/min/production.js'], 251 | 'views/**/*.html': ['.tmp/public/min/production.js'], 252 | 'views/**/*.ejs': ['.tmp/public/min/production.js'] 253 | } 254 | }, 255 | 256 | devStyles: { 257 | options: { 258 | startTag: '', 259 | endTag: '', 260 | fileTmpl: '', 261 | appRoot: '.tmp/public' 262 | }, 263 | 264 | // cssFilesToInject defined up top 265 | files: { 266 | '.tmp/public/**/*.html': cssFilesToInject, 267 | 'views/**/*.html': cssFilesToInject, 268 | 'views/**/*.ejs': cssFilesToInject 269 | } 270 | }, 271 | 272 | prodStyles: { 273 | options: { 274 | startTag: '', 275 | endTag: '', 276 | fileTmpl: '', 277 | appRoot: '.tmp/public' 278 | }, 279 | files: { 280 | '.tmp/public/index.html': ['.tmp/public/min/production.css'], 281 | 'views/**/*.html': ['.tmp/public/min/production.css'], 282 | 'views/**/*.ejs': ['.tmp/public/min/production.css'] 283 | } 284 | }, 285 | 286 | // Bring in JST template object 287 | devTpl: { 288 | options: { 289 | startTag: '', 290 | endTag: '', 291 | fileTmpl: '', 292 | appRoot: '.tmp/public' 293 | }, 294 | files: { 295 | '.tmp/public/index.html': ['.tmp/public/jst.js'], 296 | 'views/**/*.html': ['.tmp/public/jst.js'], 297 | 'views/**/*.ejs': ['.tmp/public/jst.js'] 298 | } 299 | }, 300 | 301 | 302 | /******************************************* 303 | * Jade linkers (TODO: clean this up) 304 | *******************************************/ 305 | 306 | devJsJADE: { 307 | options: { 308 | startTag: '// SCRIPTS', 309 | endTag: '// SCRIPTS END', 310 | fileTmpl: 'script(type="text/javascript", src="%s")', 311 | appRoot: '.tmp/public' 312 | }, 313 | files: { 314 | 'views/**/*.jade': jsFilesToInject 315 | } 316 | }, 317 | 318 | prodJsJADE: { 319 | options: { 320 | startTag: '// SCRIPTS', 321 | endTag: '// SCRIPTS END', 322 | fileTmpl: 'script(type="text/javascript", src="%s")', 323 | appRoot: '.tmp/public' 324 | }, 325 | files: { 326 | 'views/**/*.jade': ['.tmp/public/min/production.js'] 327 | } 328 | }, 329 | 330 | devStylesJADE: { 331 | options: { 332 | startTag: '// STYLES', 333 | endTag: '// STYLES END', 334 | fileTmpl: 'link(rel="stylesheet", href="%s")', 335 | appRoot: '.tmp/public' 336 | }, 337 | files: { 338 | 'views/**/*.jade': cssFilesToInject 339 | } 340 | }, 341 | 342 | prodStylesJADE: { 343 | options: { 344 | startTag: '// STYLES', 345 | endTag: '// STYLES END', 346 | fileTmpl: 'link(rel="stylesheet", href="%s")', 347 | appRoot: '.tmp/public' 348 | }, 349 | files: { 350 | 'views/**/*.jade': ['.tmp/public/min/production.css'] 351 | } 352 | }, 353 | 354 | // Bring in JST template object 355 | devTplJADE: { 356 | options: { 357 | startTag: '// TEMPLATES', 358 | endTag: '// TEMPLATES END', 359 | fileTmpl: 'script(type="text/javascript", src="%s")', 360 | appRoot: '.tmp/public' 361 | }, 362 | files: { 363 | 'views/**/*.jade': ['.tmp/public/jst.js'] 364 | } 365 | } 366 | /************************************ 367 | * Jade linker end 368 | ************************************/ 369 | }, 370 | 371 | watch: { 372 | api: { 373 | 374 | // API files to watch: 375 | files: ['api/**/*'] 376 | }, 377 | assets: { 378 | 379 | // Assets to watch: 380 | files: ['assets/**/*'], 381 | 382 | // When assets are changed: 383 | tasks: ['compileAssets', 'linkAssets'] 384 | } 385 | } 386 | }); 387 | 388 | // When Sails is lifted: 389 | grunt.registerTask('default', [ 390 | 'compileAssets', 391 | 'linkAssets', 392 | 'watch' 393 | ]); 394 | 395 | grunt.registerTask('compileAssets', [ 396 | 'clean:dev', 397 | 'jst:dev', 398 | 'less:dev', 399 | 'copy:dev' 400 | ]); 401 | 402 | grunt.registerTask('linkAssets', [ 403 | 404 | // Update link/script/template references in `assets` index.html 405 | 'sails-linker:devJs', 406 | 'sails-linker:devStyles', 407 | 'sails-linker:devTpl', 408 | 'sails-linker:devJsJADE', 409 | 'sails-linker:devStylesJADE', 410 | 'sails-linker:devTplJADE' 411 | ]); 412 | 413 | 414 | // Build the assets into a web accessible folder. 415 | // (handy for phone gap apps, chrome extensions, etc.) 416 | grunt.registerTask('build', [ 417 | 'compileAssets', 418 | 'linkAssets', 419 | 'clean:build', 420 | 'copy:build' 421 | ]); 422 | 423 | // When sails is lifted in production 424 | grunt.registerTask('prod', [ 425 | 'clean:dev', 426 | 'jst:dev', 427 | 'less:dev', 428 | 'copy:dev', 429 | 'concat', 430 | 'uglify', 431 | 'cssmin', 432 | 'sails-linker:prodJs', 433 | 'sails-linker:prodStyles', 434 | 'sails-linker:devTpl', 435 | 'sails-linker:prodJsJADE', 436 | 'sails-linker:prodStylesJADE', 437 | 'sails-linker:devTplJADE' 438 | ]); 439 | 440 | // When API files are changed: 441 | // grunt.event.on('watch', function(action, filepath) { 442 | // grunt.log.writeln(filepath + ' has ' + action); 443 | 444 | // // Send a request to a development-only endpoint on the server 445 | // // which will reuptake the file that was changed. 446 | // var baseurl = grunt.option('baseurl'); 447 | // var gruntSignalRoute = grunt.option('signalpath'); 448 | // var url = baseurl + gruntSignalRoute + '?action=' + action + '&filepath=' + filepath; 449 | 450 | // require('http').get(url) 451 | // .on('error', function(e) { 452 | // console.error(filepath + ' has ' + action + ', but could not signal the Sails.js server: ' + e.message); 453 | // }); 454 | // }); 455 | }; --------------------------------------------------------------------------------