├── example ├── basic │ ├── api │ │ ├── models │ │ │ ├── .gitkeep │ │ │ └── Todo.js │ │ ├── adapters │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── TodoController.js │ │ ├── services │ │ │ └── .gitkeep │ │ └── policies │ │ │ └── authenticated.js │ ├── assets │ │ ├── images │ │ │ └── .gitkeep │ │ ├── linker │ │ │ ├── js │ │ │ │ ├── .gitkeep │ │ │ │ ├── Dog.js │ │ │ │ ├── Body.js │ │ │ │ ├── app.js │ │ │ │ └── sails.io.js │ │ │ ├── styles │ │ │ │ └── .gitkeep │ │ │ └── templates │ │ │ │ ├── .gitkeep │ │ │ │ ├── Cat.html │ │ │ │ ├── About.html │ │ │ │ ├── Home.html │ │ │ │ ├── Body.html │ │ │ │ └── Dog.html │ │ ├── favicon.ico │ │ └── robots.txt │ ├── README.md │ ├── config │ │ ├── locales │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ └── _README.md │ │ ├── i18n.js │ │ ├── bootstrap.js │ │ ├── 404.js │ │ ├── log.js │ │ ├── adapters.js │ │ ├── csrf.js │ │ ├── controllers.js │ │ ├── session.js │ │ ├── 500.js │ │ ├── views.js │ │ ├── policies.js │ │ ├── routes.js │ │ └── sockets.js │ ├── app.js │ ├── views │ │ ├── home │ │ │ └── index.ejs │ │ ├── 404.ejs │ │ └── layout.ejs │ ├── package.json │ ├── .gitignore │ └── Gruntfile.js ├── old │ ├── api │ │ ├── adapters │ │ │ └── .gitkeep │ │ ├── models │ │ │ ├── .gitkeep │ │ │ └── Rainbow.js │ │ ├── services │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── RainbowController.js │ │ └── policies │ │ │ └── authenticated.js │ ├── assets │ │ ├── images │ │ │ └── .gitkeep │ │ ├── styles │ │ │ ├── .gitkeep │ │ │ └── layout.css │ │ ├── favicon.ico │ │ ├── components │ │ │ ├── Red.js │ │ │ ├── Colors.js │ │ │ ├── Row.js │ │ │ └── Rainbow.js │ │ ├── robots.txt │ │ ├── util.js │ │ ├── mixins │ │ │ └── reset.css │ │ └── index.html │ ├── simple │ │ ├── api │ │ │ ├── models │ │ │ │ └── .gitkeep │ │ │ ├── adapters │ │ │ │ └── .gitkeep │ │ │ ├── controllers │ │ │ │ └── .gitkeep │ │ │ ├── services │ │ │ │ └── .gitkeep │ │ │ └── policies │ │ │ │ └── authenticated.js │ │ ├── assets │ │ │ ├── js │ │ │ │ └── .gitkeep │ │ │ ├── styles │ │ │ │ └── .gitkeep │ │ │ ├── templates │ │ │ │ └── .gitkeep │ │ │ └── mixins │ │ │ │ └── reset.css │ │ ├── public │ │ │ ├── images │ │ │ │ └── .gitkeep │ │ │ ├── favicon.ico │ │ │ ├── robots.txt │ │ │ ├── components │ │ │ │ ├── App.js │ │ │ │ └── Nav.js │ │ │ └── index.html │ │ ├── README.md │ │ ├── config │ │ │ ├── views.js │ │ │ ├── locales │ │ │ │ └── english.js │ │ │ ├── bootstrap.js │ │ │ ├── assets.js │ │ │ ├── local.ex.js │ │ │ ├── session.js │ │ │ ├── policies.js │ │ │ ├── io.js │ │ │ ├── application.js │ │ │ ├── adapters.js │ │ │ └── routes.js │ │ ├── app.js │ │ ├── package.json │ │ ├── views │ │ │ ├── layout.ejs │ │ │ ├── 404.ejs │ │ │ ├── 500.ejs │ │ │ └── home │ │ │ │ └── index.ejs │ │ └── .gitignore │ ├── README.md │ ├── config │ │ ├── views.js │ │ ├── locales │ │ │ └── english.js │ │ ├── bootstrap.js │ │ ├── local.js │ │ ├── local.ex.js │ │ ├── session.js │ │ ├── policies.js │ │ ├── io.js │ │ ├── application.js │ │ ├── adapters.js │ │ └── routes.js │ ├── app.js │ ├── package.json │ ├── views │ │ ├── layout.ejs │ │ ├── 404.ejs │ │ ├── 500.ejs │ │ └── home │ │ │ └── index.ejs │ └── Gruntfile.js └── .gitignore ├── lib ├── Comet.js ├── Data.js ├── ready.js ├── Router.js ├── Framework.js ├── Logger.js ├── define.js ├── touch.js ├── README.md ├── Region.js ├── raise.js ├── Util.js └── deps │ └── _.js ├── .gitignore ├── .editorconfig ├── example.html ├── LICENSE.md ├── Gruntfile.js └── package.json /example/basic/api/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/api/adapters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/api/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/api/services/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/api/adapters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/api/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/api/services/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/assets/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/api/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/assets/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/assets/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/api/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/assets/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/assets/linker/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/assets/linker/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/api/adapters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/api/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/api/services/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/assets/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/public/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/old/simple/assets/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/basic/README.md: -------------------------------------------------------------------------------- 1 | # basic 2 | ### a Sails application -------------------------------------------------------------------------------- /example/old/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | ### a Sails application -------------------------------------------------------------------------------- /example/old/simple/README.md: -------------------------------------------------------------------------------- 1 | # simple 2 | ### a Sails application -------------------------------------------------------------------------------- /example/basic/config/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hello": "Sup" 3 | } 4 | -------------------------------------------------------------------------------- /example/basic/config/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hello": "Hola" 3 | } 4 | -------------------------------------------------------------------------------- /example/old/config/views.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'viewEngine': 'ejs' 3 | }; -------------------------------------------------------------------------------- /example/old/simple/config/views.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'viewEngine': 'ejs' 3 | }; -------------------------------------------------------------------------------- /example/old/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/mast/2.x/example/old/assets/favicon.ico -------------------------------------------------------------------------------- /example/basic/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/mast/2.x/example/basic/assets/favicon.ico -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/Cat.html: -------------------------------------------------------------------------------- 1 |
2 |
My Cat
3 |

Description goes here...

4 |
-------------------------------------------------------------------------------- /example/old/simple/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkh44/mast/2.x/example/old/simple/public/favicon.ico -------------------------------------------------------------------------------- /example/basic/app.js: -------------------------------------------------------------------------------- 1 | // Start sails and pass it command line arguments 2 | require('sails').lift(require('optimist').argv); 3 | -------------------------------------------------------------------------------- /example/old/app.js: -------------------------------------------------------------------------------- 1 | // Start sails and pass it command line arguments 2 | require('sails').lift(require('optimist').argv); 3 | -------------------------------------------------------------------------------- /example/old/simple/app.js: -------------------------------------------------------------------------------- 1 | // Start sails and pass it command line arguments 2 | require('sails').lift(require('optimist').argv); 3 | -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/About.html: -------------------------------------------------------------------------------- 1 |

About

2 | 3 |

Other stuff here probably

4 | 5 | Next -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/Home.html: -------------------------------------------------------------------------------- 1 |

Check out all my cool dogs

2 | 3 | -------------------------------------------------------------------------------- /example/old/config/locales/english.js: -------------------------------------------------------------------------------- 1 | /* 2 | * English string set 3 | * 4 | */ 5 | var english = { 6 | 7 | }; 8 | module.exports = english; -------------------------------------------------------------------------------- /example/old/simple/config/locales/english.js: -------------------------------------------------------------------------------- 1 | /* 2 | * English string set 3 | * 4 | */ 5 | var english = { 6 | 7 | }; 8 | module.exports = english; -------------------------------------------------------------------------------- /lib/Comet.js: -------------------------------------------------------------------------------- 1 | // TODO: resolve how loading will work 2 | var Framework = Mast; 3 | //////////////////////////////////////////////////////////////////////////// 4 | 5 | -------------------------------------------------------------------------------- /example/old/assets/components/Red.js: -------------------------------------------------------------------------------- 1 | Mast.define('Red', function () { 2 | 3 | return { 4 | redify: function () { 5 | this.color = '#f00'; 6 | } 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /example/basic/assets/linker/js/Dog.js: -------------------------------------------------------------------------------- 1 | Mast.define('Dog', function () { 2 | return { 3 | 4 | // Toggle highlighting on this dog's $el 5 | click: '! .highlighted.' 6 | }; 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /example/old/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 | cb(); 7 | }; -------------------------------------------------------------------------------- /example/old/simple/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 | cb(); 7 | }; -------------------------------------------------------------------------------- /example/old/api/models/Rainbow.js: -------------------------------------------------------------------------------- 1 | /*--------------------- 2 | :: Rainbow 3 | -> model 4 | ---------------------*/ 5 | module.exports = { 6 | 7 | attributes : { 8 | 9 | color: 'string' 10 | 11 | } 12 | 13 | }; -------------------------------------------------------------------------------- /example/old/api/controllers/RainbowController.js: -------------------------------------------------------------------------------- 1 | /*--------------------- 2 | :: Rainbow 3 | -> controller 4 | ---------------------*/ 5 | var RainbowController = { 6 | 7 | 8 | 9 | }; 10 | module.exports = RainbowController; -------------------------------------------------------------------------------- /example/old/assets/components/Colors.js: -------------------------------------------------------------------------------- 1 | Mast.define('Colors', function () { 2 | 3 | return { 4 | greenify: function () { 5 | this.color = '#0f0' 6 | }, 7 | bluify: function () { 8 | this.color = '#00f' 9 | } 10 | }; 11 | }); 12 | -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/Body.html: -------------------------------------------------------------------------------- 1 |
2 |

Mast Example

3 | 4 | 8 | 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /example/basic/views/home/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/old/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 | -------------------------------------------------------------------------------- /example/basic/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 | -------------------------------------------------------------------------------- /example/old/simple/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 | -------------------------------------------------------------------------------- /example/basic/api/models/Todo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Todo 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 | /* e.g. 14 | nickname: 'string' 15 | */ 16 | 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /example/basic/api/controllers/TodoController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TodoController 3 | * 4 | * @module :: Controller 5 | * @description :: Contains logic for handling requests. 6 | */ 7 | 8 | module.exports = { 9 | 10 | /* e.g. 11 | sayHello: function (req, res) { 12 | res.send('hello world!'); 13 | } 14 | */ 15 | 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /example/old/simple/public/components/App.js: -------------------------------------------------------------------------------- 1 | Mast.define('App', function () { 2 | return { 3 | 4 | '#': function () { 5 | this.content.attach('Home'); 6 | }, 7 | '#aboutUs': function () { 8 | this.content.attach('AboutUs'); 9 | }, 10 | '#contact': function () { 11 | this.content.attach('Contact'); 12 | } 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /example/old/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 | }; -------------------------------------------------------------------------------- /example/old/simple/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 | }; -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *# 2 | node_modules 3 | ssl 4 | .DS_STORE 5 | *~ 6 | .idea 7 | 8 | # Ignore config file w/ sensitive information 9 | config/db.js 10 | 11 | # Ignore dev minified/compiled assets 12 | public/stylesheets/_*.css 13 | public/js/_*.js 14 | 15 | # Ignore production minified/compiled assets 16 | public/_target/*.js 17 | public/_target/*.css 18 | 19 | # Ignore cmd line build output 20 | example/public/mast.dev.js -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | # Tabs in JS unless otherwise specified 13 | [**.js] 14 | indent_style = tab 15 | 16 | [**.html] 17 | indent_style = tab 18 | 19 | [**.css] 20 | indent_style = tab 21 | -------------------------------------------------------------------------------- /example/old/assets/util.js: -------------------------------------------------------------------------------- 1 | // App-level utilities 2 | Mast.randomPastelColor = function () { 3 | var color = '#'; 4 | for (var i=0;i<6;i++) { 5 | switch (Math.floor(Math.random() * 6)) { 6 | case 0: color += 'a'; break; 7 | case 1: color += 'b'; break; 8 | case 2: color += 'c'; break; 9 | case 3: color += 'd'; break; 10 | case 4: color += 'e'; break; 11 | case 5: color += 'f'; break; 12 | } 13 | } 14 | return color; 15 | }; -------------------------------------------------------------------------------- /example/old/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "a Sails application", 6 | "dependencies": { 7 | "sails": "0.8.91", 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 | } -------------------------------------------------------------------------------- /example/old/simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "a Sails application", 6 | "dependencies": { 7 | "sails": "0.8.91", 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 | } -------------------------------------------------------------------------------- /example/basic/config/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internationalization / Localization Settings 3 | * 4 | * If your app will touch people from all over the world, i18n (or internationalization) 5 | * may be an important part of your international strategy. 6 | * 7 | * 8 | * For more information, check out: 9 | * http://sailsjs.org/#documentation 10 | */ 11 | 12 | module.exports.i18n = { 13 | 14 | // Which locales are supported? 15 | locales: ['en', 'es'] 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /example/old/simple/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 | -------------------------------------------------------------------------------- /lib/Data.js: -------------------------------------------------------------------------------- 1 | // TODO: resolve how loading will work 2 | var Framework = Mast; 3 | //////////////////////////////////////////////////////////////////////////// 4 | 5 | var Datastore = function () {}; 6 | 7 | Datastore.prototype.saveToLocalStorage = function () { 8 | throw new Error('TODO'); 9 | }; 10 | 11 | Datastore.prototype.loadFromLocalStorage = function () { 12 | throw new Error('TODO'); 13 | }; 14 | 15 | 16 | Framework.Data = new Datastore(); 17 | _.extend(Framework.Data, Framework.Events); -------------------------------------------------------------------------------- /lib/ready.js: -------------------------------------------------------------------------------- 1 | // TODO: resolve how loading will work 2 | var Framework = Mast; 3 | //////////////////////////////////////////////////////////////////////////// 4 | 5 | // Fire callback if/when Framework core is ready 6 | Framework.ready = function (cb) { 7 | 8 | if (Framework.isReady) { 9 | return cb(); 10 | } 11 | 12 | async.auto({ 13 | $: function (cb) { 14 | $(function() { 15 | cb(); 16 | }); 17 | } 18 | }, function (err) { 19 | if (err) return cb(err); 20 | 21 | Framework.isReady = true; 22 | cb(); 23 | }); 24 | }; -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mast Example 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic", 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 | } -------------------------------------------------------------------------------- /example/old/config/local.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'} -------------------------------------------------------------------------------- /example/old/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'} -------------------------------------------------------------------------------- /lib/Router.js: -------------------------------------------------------------------------------- 1 | // TODO: resolve how loading will work 2 | var Framework = Mast; 3 | //////////////////////////////////////////////////////////////////////////// 4 | 5 | 6 | // Wildcard routes to global event delegator 7 | var router = new Framework.Router(); 8 | router.route(/(.*)/, 'route', function (route) { 9 | 10 | // Normalize home routes (# or null) to '' 11 | if (!route) { 12 | route = ''; 13 | } 14 | 15 | // Trigger route 16 | Framework.trigger('#' + route); 17 | }); 18 | 19 | // Expose `navigate()` method 20 | Framework.navigate = Framework.history.navigate; -------------------------------------------------------------------------------- /example/basic/assets/linker/templates/Dog.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
My Dog
4 |

Description goes here...

5 | 6 | 25 |
26 | 27 | -------------------------------------------------------------------------------- /example/old/simple/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'} -------------------------------------------------------------------------------- /example/old/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 | 17 | }; -------------------------------------------------------------------------------- /example/old/simple/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 | 17 | }; -------------------------------------------------------------------------------- /example/old/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 | }; -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /example/old/simple/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 | }; -------------------------------------------------------------------------------- /example/old/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- title %> 5 | 6 | 7 | 8 | 9 | 10 | <%- assets.css() %> 11 | 12 | 13 | 14 | <%- body %> 15 | 16 | 17 | <%- assets.templateLibrary() %> 18 | 19 | <%- assets.js() %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/old/simple/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- title %> 5 | 6 | 7 | 8 | 9 | 10 | <%- assets.css() %> 11 | 12 | 13 | 14 | <%- body %> 15 | 16 | 17 | <%- assets.templateLibrary() %> 18 | 19 | <%- assets.js() %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/.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 | -------------------------------------------------------------------------------- /example/basic/.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 | -------------------------------------------------------------------------------- /example/old/simple/.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 | -------------------------------------------------------------------------------- /example/old/config/io.js: -------------------------------------------------------------------------------- 1 | // Configuration for internal socket.io server 2 | module.exports.io = { 3 | 4 | // Socket.io transport config 5 | transports: [ 6 | 'websocket', 7 | 'flashsocket', 8 | 'htmlfile', 9 | 'xhr-polling', 10 | 'jsonp-polling' 11 | ] 12 | 13 | // In production, to scale horizontally to multiple Sails.js servers, 14 | // you'll need to defer pubsub processing to a messaging queue. 15 | // By default, Sails provides support for Redis. 16 | // adapter: 'redis', 17 | 18 | // By default, the local redis instance will be used on the default port 19 | // You can use the following config to override those settings 20 | // host: 'localhost', 21 | // port: 8888 22 | }; -------------------------------------------------------------------------------- /example/old/simple/config/io.js: -------------------------------------------------------------------------------- 1 | // Configuration for internal socket.io server 2 | module.exports.io = { 3 | 4 | // Socket.io transport config 5 | transports: [ 6 | 'websocket', 7 | 'flashsocket', 8 | 'htmlfile', 9 | 'xhr-polling', 10 | 'jsonp-polling' 11 | ] 12 | 13 | // In production, to scale horizontally to multiple Sails.js servers, 14 | // you'll need to defer pubsub processing to a messaging queue. 15 | // By default, Sails provides support for Redis. 16 | // adapter: 'redis', 17 | 18 | // By default, the local redis instance will be used on the default port 19 | // You can use the following config to override those settings 20 | // host: 'localhost', 21 | // port: 8888 22 | }; -------------------------------------------------------------------------------- /example/old/simple/public/components/Nav.js: -------------------------------------------------------------------------------- 1 | Mast.define('Nav', function () { 2 | return { 3 | 4 | '#': function () { 5 | this.$('li').removeClass('selected'); 6 | this.$('li[href="#"]').addClass('selected'); 7 | }, 8 | '#aboutUs': function () { 9 | this.$('li').removeClass('selected'); 10 | this.$('li[href="#aboutUs"]').addClass('selected'); 11 | }, 12 | '#contact': function () { 13 | this.$('li').removeClass('selected'); 14 | this.$('li[href="#contact"]').addClass('selected'); 15 | }, 16 | 17 | events: { 18 | 'click li': function (event) { 19 | var $theThingThatWasClicked = $(event.currentTarget); 20 | window.location.hash = $theThingThatWasClicked.attr('href'); 21 | }, 22 | 'click button': '#signUp' 23 | } 24 | 25 | }; 26 | }); -------------------------------------------------------------------------------- /example/old/config/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | // Name of the application (used as default ) 4 | appName: "Sails Application", 5 | 6 | // Port this Sails application will live on 7 | port: 1337, 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: 'info' 28 | } 29 | 30 | }; -------------------------------------------------------------------------------- /example/old/simple/config/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | // Name of the application (used as default <title>) 4 | appName: "Sails Application", 5 | 6 | // Port this Sails application will live on 7 | port: 1337, 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: 'info' 28 | } 29 | 30 | }; -------------------------------------------------------------------------------- /lib/Framework.js: -------------------------------------------------------------------------------- 1 | (function (frameworkId, $, _, Backbone) { 2 | var Framework = Backbone; 3 | 4 | // Remember the frameworkId for usage examples in warnings and errors 5 | Framework.id = frameworkId; 6 | 7 | // Start w/ empty templates, components, and data 8 | Framework.templates = {}; 9 | Framework.components = {}; 10 | Framework.data = {}; 11 | 12 | // Region cache 13 | Framework.regions = {}; 14 | 15 | // Shortcut defaults 16 | Framework.shortcut = {}; 17 | Framework.shortcut.template = true; 18 | Framework.shortcut.count = true; 19 | 20 | // Expose framework global on window object 21 | window[Framework.id] = Framework; 22 | })( 23 | // Name your framework 24 | 'Mast', 25 | 26 | // Pass dependencies as args to keep global namespace clean 27 | $, 28 | _, 29 | Backbone 30 | ); -------------------------------------------------------------------------------- /example/old/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;} -------------------------------------------------------------------------------- /example/old/simple/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;} -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /example/basic/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 | -------------------------------------------------------------------------------- /example/basic/config/locales/_README.md: -------------------------------------------------------------------------------- 1 | # Internationalization / Localization Settings 2 | 3 | ## Locale 4 | 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. 5 | 6 | Here is an example locale stringfile for the Spanish language (`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, e.g. 16 | 17 | ```ejs 18 | <h1> <%= i18n('Hello!') %> </h1> 19 | <h1> <%= i18n('Hello %s, how are you today?', 'Mike') %> </h1> 20 | ``` 21 | 22 | ## Configuration 23 | Localization/internationalization config can be found in `config/i18n.js`, from where you can set your supported locales. -------------------------------------------------------------------------------- /example/old/assets/components/Row.js: -------------------------------------------------------------------------------- 1 | Mast.define('Row', function () { 2 | 3 | return { 4 | extendComponents: ['Colors', 'Red'], 5 | 6 | // Model is passed down by parent component when this component is created 7 | 8 | events: { 9 | click: function () { 10 | this.model.set({ 11 | color: Mast.randomPastelColor() 12 | }); 13 | } 14 | }, 15 | 16 | beforeClose: function (cb) { 17 | var self = this; 18 | 19 | self.$el.removeClass('lightSpeedIn'); 20 | self.$el.addClass('lightSpeedOut'); 21 | 22 | setTimeout(function () { 23 | self.$el.animate({ 24 | opacity: 0, 25 | height: 0, 26 | paddingTop: 0, 27 | paddingBottom: 0 28 | }, 200, cb); 29 | },400); 30 | }, 31 | 32 | afterRender: function () { 33 | this.$el.addClass('animated'); 34 | this.$el.addClass('lightSpeedIn'); 35 | }, 36 | 37 | // Standard model binding 38 | afterChange: { 39 | color: function (model, value, options) { 40 | this.$el.css({ 41 | 'background-color': value 42 | }); 43 | } 44 | } 45 | }; 46 | }); 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | -- 3 | 4 | 5 | Copyright © 2012 Mike McNeil 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /example/old/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': 'disk', 9 | 10 | // In-memory adapter for DEVELOPMENT ONLY 11 | // (data is NOT preserved when the server shuts down) 12 | memory: { 13 | module: 'sails-memory' 14 | }, 15 | 16 | // Persistent adapter for DEVELOPMENT ONLY 17 | // (data IS preserved when the server shuts down) 18 | // PLEASE NOTE: disk adapter not compatible with node v0.10.0 currently 19 | // because of limitations in node-dirty 20 | // See https://github.com/felixge/node-dirty/issues/34 21 | disk: { 22 | module: 'sails-disk' 23 | }, 24 | 25 | // MySQL is the world's most popular relational database. 26 | // Learn more: http://en.wikipedia.org/wiki/MySQL 27 | mysql: { 28 | module : 'sails-mysql', 29 | host : 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS', 30 | user : 'YOUR_MYSQL_USER', 31 | password : 'YOUR_MYSQL_PASSWORD', 32 | database : 'YOUR_MYSQL_DB' 33 | } 34 | }; -------------------------------------------------------------------------------- /example/old/simple/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': 'disk', 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 | }; -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | 'use strict'; 4 | 5 | // Order of the source files that we want to concat 6 | var srcFiles = [ 7 | // Dependencies 8 | 'lib/deps/$.js', 9 | 'lib/deps/_.js', 10 | 'lib/deps/Backbone.js', 11 | 'lib/deps/async.js', 12 | 13 | // Core 14 | 'lib/Framework.js', 15 | 'lib/Logger.js', 16 | 'lib/Util.js', 17 | 'lib/touch.js', 18 | 'lib/define.js', 19 | 'lib/Component.js', 20 | 'lib/Region.js', 21 | 'lib/Data.js', 22 | 'lib/Router.js', 23 | 'lib/Comet.js', 24 | 'lib/ready.js', 25 | 'lib/raise.js' 26 | ]; 27 | 28 | grunt.initConfig({ 29 | pkg: grunt.file.readJSON('package.json'), 30 | 31 | concat: { 32 | dist: { 33 | src: srcFiles, 34 | dest: 'mast.dev.js' 35 | } 36 | }, 37 | 38 | uglify: { 39 | options: { 40 | report: 'min', 41 | preserveComments: false 42 | }, 43 | files: { 44 | src: 'mast.dev.js', 45 | dest: 'mast.min.js' 46 | } 47 | } 48 | }); 49 | 50 | grunt.loadNpmTasks('grunt-contrib-concat'); 51 | grunt.loadNpmTasks('grunt-contrib-uglify'); 52 | 53 | 54 | grunt.registerTask('default', [ 55 | 'build:dev', 56 | 'build:production' 57 | ]); 58 | 59 | grunt.registerTask('build:dev', ['concat']); 60 | grunt.registerTask('build:production', ['concat', 'uglify']); 61 | }; 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mast", 3 | "author": "Mike McNeil <mike@balderdash.co>", 4 | "contributors": [ 5 | { 6 | "name": "Balderdash Design Co.", 7 | "email": "dev@balderdash.co" 8 | }, 9 | { 10 | "name": "Mike McNeil", 11 | "email": "mike@balderdash.co" 12 | }, 13 | { 14 | "name": "Gabe Hernandez", 15 | "email": "gabe@balderdash.co" 16 | } 17 | ], 18 | "version": "0.0.1", 19 | "description": "Convention-over-configuration framework for Backbone.js", 20 | "homepage": "http://sailsjs.com", 21 | "keywords": [ 22 | "Mast", 23 | "Backbone", 24 | "Realtime", 25 | "Socket.io" 26 | ], 27 | "main": "lib/export", 28 | "directories": { 29 | "lib": "lib", 30 | "example": "example" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git://github.com/balderdashy/mast.git" 35 | }, 36 | "licenses": [ 37 | { 38 | "type": "MIT", 39 | "url": "http://mast.mit-license.org/" 40 | } 41 | ], 42 | "bugs": { 43 | "url": "http://github.com/balderdashy/mast/issues" 44 | }, 45 | "devDependencies": { 46 | "mocha": "~1.10.0", 47 | "grunt-contrib-concat": "~0.3.0", 48 | "grunt-contrib-uglify": "~0.2.2", 49 | "grunt": "~0.4.1" 50 | }, 51 | "dependencies": { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/old/assets/styles/layout.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Arial, helvetica, sans-serif; 3 | } 4 | 5 | .no-select { 6 | -webkit-touch-callout: none; 7 | -webkit-user-select: none; 8 | -khtml-user-select: none; 9 | -moz-user-select: none; 10 | -ms-user-select: none; 11 | user-select: none; 12 | } 13 | 14 | h1 { 15 | float: left; 16 | } 17 | 18 | h1.selected { 19 | color: blue; 20 | } 21 | 22 | h2 { 23 | clear: both; 24 | } 25 | 26 | .nav > a { 27 | transition: all 250ms; 28 | -webkit-transition: all 250ms; 29 | -moz-transition: all 250ms; 30 | 31 | cursor: pointer; 32 | background: #eee; 33 | padding: 15px; 34 | margin: 5px; 35 | border: 1px solid #aaa; 36 | float: right; 37 | border-radius: 15px; 38 | } 39 | .nav > a:hover { 40 | background: #aca; 41 | box-shadow: 0 0 5px 3px rgba(0,50,0,0.3); 42 | } 43 | 44 | .nav ul { 45 | clear: both; 46 | width: 100%; 47 | 48 | margin: 0; 49 | padding: 0; 50 | 51 | } 52 | 53 | .nav li { 54 | cursor:pointer; 55 | list-style-type: none; 56 | margin: 0; 57 | padding: 15px 3%; 58 | display:block; 59 | 60 | transition: background-color 350ms; 61 | -webkit-transition: background-color 350ms; 62 | -moz-transition: background-color 350ms; 63 | -o-transition: background-color 350ms; 64 | 65 | -webkit-animation-duration: 400ms; 66 | -moz-animation-duration: 400ms; 67 | -o-animation-duration: 400ms; 68 | 69 | } -------------------------------------------------------------------------------- /example/basic/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 | 36 | module: 'sails-mysql', 37 | host: 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS', 38 | user: 'YOUR_MYSQL_USER', 39 | // Psst.. You can put your password in config/local.js instead 40 | // so you don't inadvertently push it up if you're using version control 41 | password: 'YOUR_MYSQL_PASSWORD', 42 | database: 'YOUR_MYSQL_DB' 43 | } 44 | }; -------------------------------------------------------------------------------- /lib/Logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | */ 5 | 6 | function Logger (Framework) { 7 | 8 | // If log is specified, use it, otherwise use the console 9 | if (Framework.logger) { 10 | Framework.error = Framework.logger.error; 11 | Framework.warn = Framework.logger.warn; 12 | Framework.log = Framework.logger.debug || Framework.logger; 13 | Framework.verbose = Framework.logger.verbose; 14 | } 15 | // In IE, we can't default to the browser console because there IS NO BROWSER CONSOLE 16 | else if (typeof console !== 'undefined') { 17 | Framework.error = console && console.error && console.error.bind(console); 18 | Framework.warn = console && console.warn && console.warn.bind(console); 19 | Framework.log = console && console.log && console.log.bind(console); 20 | Framework.verbose = console && console.debug && console.debug.bind(console); 21 | } 22 | 23 | // Support for `debug` for backwards compatibility 24 | Framework.debug = Framework.log; 25 | 26 | // Turn off all logs in production 27 | if (Framework.production) { 28 | Framework.logLevel = 'silent'; 29 | } 30 | 31 | 32 | // Use log level config if provided 33 | var noop = function () {}; 34 | switch ( Framework.logLevel ) { 35 | 36 | case 'verbose': break; 37 | 38 | case 'debug': Framework.verbose = noop; 39 | break; 40 | 41 | case 'warn': Framework.verbose = Framework.log = Framework.debug = noop; 42 | break; 43 | 44 | case 'error': Framework.verbose = Framework.log = Framework.debug = 45 | Framework.warn = noop; 46 | break; 47 | 48 | case 'silent': Framework.verbose = Framework.log = Framework.debug = 49 | Framework.warn = Framework.error = noop; 50 | break; 51 | 52 | default: throw new Error ('Unrecognized logging level config ' + 53 | '(' + Framework.id + '.logLevel = "' + Framework.logLevel + '")'); 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /example/basic/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 | * <form> 23 | * <input type="hidden" name="_csrf" value="<%= _csrf %>" /> 24 | * </form> 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; -------------------------------------------------------------------------------- /lib/define.js: -------------------------------------------------------------------------------- 1 | // TODO: resolve how loading will work 2 | var Framework = Mast; 3 | //////////////////////////////////////////////////////////////////////////// 4 | 5 | // Optional method to require app components 6 | // Require.js can be used instead as needed 7 | 8 | Framework._defineQueue = []; 9 | 10 | 11 | 12 | 13 | /** 14 | * Optional method to require app components. Require.js can be used instead 15 | * as needed. 16 | * @param {string} id Component id override. 17 | * @param {Function} definitionFn Definition of component 18 | */ 19 | 20 | Framework.define = function (id, definitionFn) { 21 | // Id param is optional 22 | if (!definitionFn) { 23 | definitionFn = id; 24 | id = null; 25 | } 26 | 27 | Framework._defineQueue.push({ 28 | definition: definitionFn, 29 | idOverride: id 30 | }); 31 | }; 32 | 33 | 34 | 35 | 36 | /** 37 | * Run definition methods to get actual component definitions. 38 | * This is deferred to avoid having to use Backbone's function () {} approach for things 39 | * like collections. 40 | * 41 | * @api private 42 | */ 43 | 44 | Framework._buildComponentDefinitions = function () { 45 | 46 | // Iterate through the define queue and create each definition 47 | _.each(Framework._defineQueue, function (component) { 48 | var componentDef = component.definition(); 49 | 50 | if (component.idOverride) { 51 | if (componentDef.id) { 52 | throw new Error(component.idOverride + ':: Cannot specify an idOverride in .define() if an id property ('+componentDef.id+') is already set in your component definition!\nUsage: .define([idOverride], definition)'); 53 | } 54 | componentDef.id = component.idOverride; 55 | } 56 | if (!componentDef.id) { 57 | throw new Error('Cannot use .define() without defining an id property or override in your component!\nUsage: .define([idOverride], definition)'); 58 | } 59 | Framework.components[componentDef.id] = componentDef; 60 | }); 61 | }; 62 | -------------------------------------------------------------------------------- /example/basic/assets/linker/js/Body.js: -------------------------------------------------------------------------------- 1 | Mast.define('Body', function () { 2 | return { 3 | 4 | // Trigger the %test subscription whenever a <h1> 5 | // inside this component's $el is clicked 6 | // 7 | // Also stop propagation (.) so the event doesn't 8 | // bubble and trigger the next handler (click) 9 | 'click h1' : '%test.', 10 | 11 | // if anywhere ELSE inside this component's $el is clicked, 12 | // log a note to the console 13 | 'click' : '>>> Note: The click event of the Body component fired.', 14 | 15 | 16 | 17 | // When %test subscription is triggered, 18 | '%test' : '!!! You fired the %test trigger!', 19 | 20 | 21 | // Route navigation menu to change the URL appropriately 22 | // NOTE: This could be done w/o javascript using <a href=""></a> 23 | // In this case, we used a `destination` HTML attribute to indicate 24 | // the URL target. 25 | 'click [destination="#about"]': '#about', 26 | 'click [destination="#home"]': '#home', 27 | 28 | // Change `content` region according to URL 29 | '#about' : 'content@About', 30 | '#home' : 'content@Home', 31 | 32 | // Redirect empty URL to '#home' handler (changes URL) 33 | '#' : '#home', 34 | 35 | 36 | 37 | // TODO 38 | //////////////////////////////////////////// 39 | 40 | // Increment/decrement % 5 41 | 'click .next-page': function () { 42 | var newVal = this.model.get('pageIndex'); 43 | newVal++; 44 | newVal %= 5; 45 | this.model.set('pageIndex', newVal); 46 | }, 47 | 48 | // Bindings 49 | afterChange: { 50 | pageIndex: function (newVal) { 51 | var page = this.model.get('pages')[newVal]; 52 | this.content.attach(page); 53 | } 54 | }, 55 | 56 | model: function () { 57 | return new Mast.Model({ 58 | pageIndex: 0, 59 | pages: [ 60 | 'Project_1', 61 | 'Project_2', 62 | 'Project_3', 63 | 'Project_4', 64 | 'Project_5' 65 | ] 66 | }); 67 | } 68 | }; 69 | }); 70 | 71 | -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /example/basic/config/session.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Session 3 | * 4 | * Sails session integration leans heavily on the great work already done by Express, but also unifies 5 | * Socket.io with the Connect session store. It uses Connect's cookie parser to normalize configuration 6 | * differences between Express and Socket.io and hooks into Sails' middleware interpreter to allow you 7 | * to access and auto-save to `req.session` with Socket.io the same way you would with Express. 8 | * 9 | * For more information on configuring the session, check out: 10 | * http://sailsjs.org/#documentation 11 | */ 12 | 13 | module.exports.session = { 14 | 15 | // Session secret is automatically generated when your new app is created 16 | // Replace at your own risk in production-- you will invalidate the cookies of your users, 17 | // forcing them to log in again. 18 | secret: 'ad473d7cc0551ada766308d3d72fb85e' 19 | 20 | 21 | // In production, uncomment the following lines to set up a shared redis session store 22 | // that can be shared across multiple Sails.js servers 23 | // adapter: 'redis', 24 | // 25 | // The following values are optional, if no options are set a redis instance running 26 | // on localhost is expected. 27 | // Read more about options at: https://github.com/visionmedia/connect-redis 28 | // 29 | // host: 'localhost', 30 | // port: 6379, 31 | // ttl: <redis session TTL in seconds>, 32 | // db: 0, 33 | // pass: <redis auth password> 34 | // prefix: 'sess:' 35 | 36 | 37 | // Uncomment the following lines to use your Mongo adapter as a session store 38 | // adapter: 'mongo', 39 | // 40 | // host: 'localhost', 41 | // port: 27017, 42 | // db: 'sails', 43 | // collection: 'sessions', 44 | // 45 | // Optional Values: 46 | // 47 | // # Note: url will override other connection settings 48 | // url: 'mongodb://user:pass@host:port/database/collection', 49 | // 50 | // username: '', 51 | // password: '', 52 | // auto_reconnect: false, 53 | // ssl: false, 54 | // stringify: true 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /example/old/simple/public/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 34 | 35 | 36 |

Hello world

37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 | 52 | 53 | 57 | 58 | 62 | 63 | 68 | 69 | 73 | 74 | 75 | 78 | 79 | 80 | 85 | -------------------------------------------------------------------------------- /example/old/views/404.ejs: -------------------------------------------------------------------------------- 1 |

Page not found

2 | 3 |

The view you were trying to reach does not exist.

4 | 5 | 6 | -------------------------------------------------------------------------------- /example/old/simple/views/404.ejs: -------------------------------------------------------------------------------- 1 |

Page not found

2 | 3 |

The view you were trying to reach does not exist.

4 | 5 | 6 | -------------------------------------------------------------------------------- /example/old/views/500.ejs: -------------------------------------------------------------------------------- 1 |

Error

2 | 3 | 4 | <% _.each(errors, function (error) { %> 5 | 6 | 7 | <%= error %> 8 | 9 | 10 | <% }) %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/old/simple/views/500.ejs: -------------------------------------------------------------------------------- 1 |

Error

2 | 3 | 4 | <% _.each(errors, function (error) { %> 5 | 6 | 7 | <%= error %> 8 | 9 | 10 | <% }) %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/old/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 | 26 | // If you want to set up a route only for a particular HTTP method/verb 27 | // (GET, POST, PUT, DELETE) you can specify the verb before the path: 28 | // 'post /signup': { 29 | // controller : 'user', 30 | // action : 'signup' 31 | // } 32 | 33 | // Keep in mind default routes exist for each of your controllers 34 | // So if you have a UserController with an action called "juggle" 35 | // a route will be automatically exist mapping it to /user/juggle. 36 | // 37 | // Additionally, unless you override them, new controllers will have 38 | // create(), find(), findAll(), update(), and destroy() actions, 39 | // and routes will exist for them as follows: 40 | /* 41 | 42 | // Standard RESTful routing 43 | // (if index is not defined, findAll will be used) 44 | 'get /user': { 45 | controller : 'user', 46 | action : 'index' 47 | }, 48 | 'get /user/:id': { 49 | controller : 'user', 50 | action : 'find' 51 | }, 52 | 'post /user': { 53 | controller : 'user', 54 | action : 'create' 55 | }, 56 | 'put /user/:id': { 57 | controller : 'user', 58 | action : 'update' 59 | }, 60 | 'delete /user/:id': { 61 | controller : 'user', 62 | action : 'destroy' 63 | } 64 | */ 65 | }; 66 | -------------------------------------------------------------------------------- /example/old/simple/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 | -------------------------------------------------------------------------------- /example/basic/assets/linker/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 | -------------------------------------------------------------------------------- /example/old/assets/components/Rainbow.js: -------------------------------------------------------------------------------- 1 | Mast.define('Rainbow', function () { 2 | 3 | return { 4 | 5 | '#test': function (x,y,z) { 6 | alert('#test routed successfully! got args:\nx:' + x + '\ny:' + y + '\nz:'+ z); 7 | }, 8 | 9 | '#test/:id': function (x,y,z) { 10 | alert('#test/:id routed successfully! got args:\nx:' + x + '\ny:' + y + '\nz:'+ z); 11 | }, 12 | 13 | events: { 14 | 15 | 'click .add-thing': function addThing () { 16 | this.collection.create({ 17 | color: Mast.randomPastelColor() 18 | }); 19 | }, 20 | 21 | 'click .remove-thing': function removeFirstThing () { 22 | var model = this.collection.shift(); 23 | model.destroy(); 24 | }, 25 | 26 | 'click .wipe-things': function removeAllThings () { 27 | while (this.collection.length) { 28 | this.collection.at(0).destroy(); 29 | } 30 | } 31 | }, 32 | 33 | // Doing it this way ensures that this component is bound to a global collection set, 34 | // and if one already exists, it uses it 35 | collection: function () { 36 | return Mast.data.Rainbows; 37 | }, 38 | 39 | beforeRender: function (cb) { 40 | // TODO: Loading state 41 | this.collection.fetch({ 42 | silent: true, 43 | success: function () { 44 | cb(); 45 | } 46 | }); 47 | }, 48 | 49 | afterRender: function () { 50 | this.collection.trigger('reset', this.collection); 51 | }, 52 | 53 | // Standard collection bindings 54 | afterAdd: function (model, collection, options) { 55 | 56 | // Clear empty state 57 | if (this.collection.length > 0) { 58 | this.$('.emptyhtml').fadeOut(); 59 | } 60 | 61 | // Render the row in the appropriate place 62 | this.rows.insert( 63 | options.at || collection.length-1, 64 | 'Row', { 65 | model: model 66 | }); 67 | 68 | }, 69 | afterRemove: function (model, collection, options) { 70 | this.rows.remove(options.at || 0); 71 | 72 | // Empty state 73 | if (this.collection.length < 1) { 74 | this.$('.emptyhtml').fadeIn(); 75 | } 76 | }, 77 | afterReset: function (collection, options) { 78 | var self = this; 79 | 80 | // Clear empty state 81 | if (this.collection.length > 0) { 82 | this.$('.emptyhtml').fadeOut(); 83 | } 84 | 85 | // Flush region and replace w/ new contents 86 | this.rows.empty(); 87 | collection.each(function (model) { 88 | self.rows.append('Row', { model: model }); 89 | }); 90 | 91 | // Empty state 92 | if (this.collection.length < 1) { 93 | this.$('.emptyhtml').fadeIn(); 94 | } 95 | } 96 | }; 97 | }); -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /example/basic/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 | -------------------------------------------------------------------------------- /example/basic/config/policies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Policy mappings (ACL) 3 | * 4 | * Policies are simply Express middleware functions which run **before** your controllers. 5 | * You can apply one or more policies to a given controller, or protect just one of its actions. 6 | * 7 | * Any policy file (e.g. `authenticated.js`) can be dropped into the `/policies` folder, 8 | * at which point it can be accessed below by its filename, minus the extension, (e.g. `authenticated`) 9 | * 10 | * For more information on policies, check out: 11 | * http://sailsjs.org/#documentation 12 | */ 13 | 14 | 15 | module.exports.policies = { 16 | 17 | // Default policy for all controllers and actions 18 | // (`true` allows public access) 19 | '*': true 20 | 21 | /* 22 | // Here's an example of adding some policies to a controller 23 | RabbitController: { 24 | 25 | // Apply the `false` policy as the default for all of RabbitController's actions 26 | // (`false` prevents all access, which ensures that nothing bad happens to our rabbits) 27 | '*': false, 28 | 29 | // For the action `nurture`, apply the 'isRabbitMother' policy 30 | // (this overrides `false` above) 31 | nurture : 'isRabbitMother', 32 | 33 | // Apply the `isNiceToAnimals` AND `hasRabbitFood` policies 34 | // before letting any users feed our rabbits 35 | feed : ['isNiceToAnimals', 'hasRabbitFood'] 36 | } 37 | */ 38 | }; 39 | 40 | 41 | /** 42 | * Here's what the `isNiceToAnimals` policy from above might look like: 43 | * (this file would be located at `policies/isNiceToAnimals.js`) 44 | * 45 | * We'll make some educated guesses about whether our system will 46 | * consider this user someone who is nice to animals. 47 | * 48 | * Besides protecting rabbits (while a noble cause, no doubt), 49 | * here are a few other example use cases for policies: 50 | * 51 | * + cookie-based authentication 52 | * + role-based access control 53 | * + limiting file uploads based on MB quotas 54 | * + OAuth 55 | * + BasicAuth 56 | * + or any other kind of authentication scheme you can imagine 57 | * 58 | */ 59 | 60 | /* 61 | module.exports = function isNiceToAnimals (req, res, next) { 62 | 63 | // `req.session` contains a set of data specific to the user making this request. 64 | // It's kind of like our app's "memory" of the current user. 65 | 66 | // If our user has a history of animal cruelty, not only will we 67 | // prevent her from going even one step further (`return`), 68 | // we'll go ahead and redirect her to PETA (`res.redirect`). 69 | if ( req.session.user.hasHistoryOfAnimalCruelty ) { 70 | return res.redirect('http://PETA.org'); 71 | } 72 | 73 | // If the user has been seen frowning at puppies, we have to assume that 74 | // they might end up being mean to them, so we'll 75 | if ( req.session.user.frownsAtPuppies ) { 76 | return res.redirect('http://www.dailypuppy.com/'); 77 | } 78 | 79 | // Finally, if the user has a clean record, we'll call the `next()` function 80 | // to let them through to the next policy or our controller 81 | next(); 82 | }; 83 | */ 84 | -------------------------------------------------------------------------------- /example/old/views/home/index.ejs: -------------------------------------------------------------------------------- 1 |

2 | New Sails App 3 |

4 |
5 |

It works!

6 | 7 |

You've successfully installed the Sails Framework.

8 | 9 |

Sails is a modern, realtime MVC framework for the Node.js platform.
It helps you build apps fast.

10 | 11 |

12 | Documentation 13 | Changelog 14 |

15 |
16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /example/old/simple/views/home/index.ejs: -------------------------------------------------------------------------------- 1 |

2 | New Sails App 3 |

4 |
5 |

It works!

6 | 7 |

You've successfully installed the Sails Framework.

8 | 9 |

Sails is a modern, realtime MVC framework for the Node.js platform.
It helps you build apps fast.

10 | 11 |

12 | Documentation 13 | Changelog 14 |

15 |
16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /lib/touch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module that adds touch support for mobile devices. 3 | * 4 | * (c) 2012 Raymond Julin, Keyteq AS 5 | * Backbone.touch may be freely distributed under the MIT license. 6 | */ 7 | 8 | 9 | // TODO: resolve how loading will work 10 | var Framework = Mast; 11 | //////////////////////////////////////////////////////////////////////////// 12 | 13 | (function() { 14 | 15 | // The `getValue` and `delegateEventSplitter` is copied from 16 | // Backbones source, unfortunately these are not available 17 | // in any form from Backbone itself 18 | var getValue = function(object, prop) { 19 | if (!(object && object[prop])) return null; 20 | return _.isFunction(object[prop]) ? object[prop]() : object[prop]; 21 | }; 22 | 23 | var delegateEventSplitter = /^(\S+)\s*(.*)$/; 24 | 25 | _.extend(Framework.View.prototype, { 26 | 27 | _touching: false, 28 | 29 | touchPrevents: true, 30 | 31 | touchThreshold: 10, 32 | 33 | isTouch: 'ontouchstart' in document && !('callPhantom' in window), 34 | 35 | // Drop in replacement for Backbone.View#delegateEvent 36 | // Enables better touch support 37 | // 38 | // If the users device is touch enabled it replace any `click` 39 | // event with listening for touch(start|move|end) in order to 40 | // quickly trigger touch taps 41 | delegateEvents: function(events) { 42 | if (!(events || (events = getValue(this, 'events')))) return; 43 | this.undelegateEvents(); 44 | var suffix = '.delegateEvents' + this.cid; 45 | _(events).each(function(method, key) { 46 | if (!_.isFunction(method)) method = this[events[key]]; 47 | if (!method) throw new Error('Method "' + events[key] + '" does not exist'); 48 | var match = key.match(delegateEventSplitter); 49 | var eventName = match[1], 50 | selector = match[2]; 51 | var boundHandler = _.bind(this._touchHandler, this); 52 | method = _.bind(method, this); 53 | if (this._useTouchHandlers(eventName, selector)) { 54 | this.$el.on('touchstart' + suffix, selector, boundHandler); 55 | this.$el.on('touchend' + suffix, selector, { 56 | method: method 57 | }, 58 | boundHandler); 59 | } else { 60 | eventName += suffix; 61 | if (selector === '') { 62 | this.$el.bind(eventName, method); 63 | } else { 64 | this.$el.on(eventName, selector, method); 65 | } 66 | } 67 | }, this); 68 | }, 69 | 70 | // Detect if touch handlers should be used over listening for click 71 | // Allows custom detection implementations 72 | _useTouchHandlers: function(eventName, selector) { 73 | return this.isTouch && eventName === 'click'; 74 | }, 75 | 76 | // At the first touchstart we register touchevents as ongoing 77 | // and as soon as a touch move happens we set touching to false, 78 | // thus implying that a fastclick will not happen when 79 | // touchend occurs. If no touchmove happened 80 | // inbetween touchstart and touchend we trigger the event 81 | // 82 | // The `touchPrevents` toggle decides if Backbone.touch 83 | // will stop propagation and prevent default 84 | // for *button* and *a* elements 85 | _touchHandler: function(e) { 86 | if (!('changedTouches' in e.originalEvent)) return; 87 | var touch = e.originalEvent.changedTouches[0]; 88 | var x = touch.clientX; 89 | var y = touch.clientY; 90 | switch (e.type) { 91 | case 'touchstart': 92 | this._touching = [x, y]; 93 | break; 94 | case 'touchend': 95 | var oldX = this._touching[0]; 96 | var oldY = this._touching[1]; 97 | var threshold = this.touchThreshold; 98 | if (x < (oldX + threshold) && x > (oldX - threshold) && 99 | y < (oldY + threshold) && y > (oldY - threshold)) { 100 | this._touching = false; 101 | if (this.touchPrevents) { 102 | var tagName = e.currentTarget.tagName; 103 | if (tagName === 'BUTTON' || 104 | tagName === 'A') { 105 | e.preventDefault(); 106 | e.stopPropagation(); 107 | } 108 | } 109 | e.data.method(e); 110 | } 111 | break; 112 | } 113 | } 114 | }); 115 | })(); 116 | -------------------------------------------------------------------------------- /example/old/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 112 | 115 | 116 | 119 | 120 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /example/basic/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- title %> 5 | 6 | 7 | 8 | 9 | 10 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <%- body %> 37 | 38 | 39 | 40 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /example/basic/assets/linker/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 | 137 | if (result && typeof result === 'string') { 138 | try { 139 | parsedResult = window.io.JSON.parse(result); 140 | } catch (e) { 141 | if (typeof console !== 'undefined') { 142 | console.warn("Could not parse:", result, e); 143 | } 144 | throw new Error("Server response could not be parsed!\n" + result); 145 | } 146 | } 147 | 148 | // TODO: Handle errors more effectively 149 | if (parsedResult === 404) throw new Error("404: Not found"); 150 | if (parsedResult === 403) throw new Error("403: Forbidden"); 151 | if (parsedResult === 500) throw new Error("500: Server error"); 152 | 153 | cb && cb(parsedResult); 154 | 155 | }); 156 | } 157 | 158 | 159 | 160 | 161 | }) ( 162 | 163 | // In case you're wrapping socket.io to prevent pollution of the global namespace, 164 | // you can replace `window.io` with your own `io` here: 165 | window.io 166 | 167 | ); 168 | -------------------------------------------------------------------------------- /example/basic/config/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Routes 3 | * 4 | * Sails uses a number of different strategies to route requests. 5 | * Here they are top-to-bottom, in order of precedence. 6 | * 7 | * For more information on routes, check out: 8 | * http://sailsjs.org/#documentation 9 | */ 10 | 11 | 12 | 13 | /** 14 | * (1) Core middleware 15 | * 16 | * Middleware included with `app.use` is run first, before the router 17 | */ 18 | 19 | 20 | /** 21 | * (2) Static routes 22 | * 23 | * This object routes static URLs to handler functions-- 24 | * In most cases, these functions are actions inside of your controllers. 25 | * For convenience, you can also connect routes directly to views or external URLs. 26 | * 27 | */ 28 | 29 | module.exports.routes = { 30 | 31 | // By default, your root route (aka home page) points to a view 32 | // located at `views/home/index.ejs` 33 | // 34 | // (This would also work if you had a file at: `/views/home.ejs`) 35 | '/': { 36 | view: 'home/index' 37 | } 38 | 39 | /* 40 | // But what if you want your home page to display 41 | // a signup form located at `views/user/signup.ejs`? 42 | '/': { 43 | view: 'user/signup' 44 | } 45 | 46 | 47 | // Let's say you're building an email client, like Gmail 48 | // You might want your home route to serve an interface using custom logic. 49 | // In this scenario, you have a custom controller `MessageController` 50 | // with an `inbox` action. 51 | '/': 'MessageController.inbox' 52 | 53 | 54 | // Alternatively, you can use the more verbose syntax: 55 | '/': { 56 | controller: 'MessageController', 57 | action: 'inbox' 58 | } 59 | 60 | 61 | // If you decided to call your action `index` instead of `inbox`, 62 | // since the `index` action is the default, you can shortcut even further to: 63 | '/': 'MessageController' 64 | 65 | 66 | // Up until now, we haven't specified a specific HTTP method/verb 67 | // The routes above will apply to ALL verbs! 68 | // If you want to set up a route only for one in particular 69 | // (GET, POST, PUT, DELETE, etc.), just specify the verb before the path. 70 | // For example, if you have a `UserController` with a `signup` action, 71 | // and somewhere else, you're serving a signup form looks like: 72 | // 73 | //
74 | // 75 | // 76 | // 77 | //
78 | 79 | // You would want to define the following route to handle your form: 80 | 'post /signup': 'UserController.signup' 81 | 82 | 83 | // What about the ever-popular "vanity URLs" aka URL slugs? 84 | // (you remember doing this with `mod_rewrite` in PHP) 85 | // 86 | // This is where you want to set up root-relative dynamic routes like: 87 | // http://yourwebsite.com/twinkletoezz993 88 | // 89 | // You still want to allow requests through to the static assets, 90 | // So we need to set up this route to allow URLs through that have a trailing ".": 91 | // (e.g. your javascript, CSS, and image files) 92 | 'get /*(^.*)': 'UserController.profile' 93 | 94 | */ 95 | }; 96 | 97 | 98 | 99 | /** (3) Action blueprints * * These routes can be disabled by setting( in config / controllers.js): * `module.exports.controllers.blueprints.actions = false` * * All of your controllers ' actions are automatically bound to a route. For example: 100 | * + If you have a controller, `FooController`: 101 | * + its action `bar` is accessible at `/foo/bar` 102 | * + its action `index` is accessible at `/foo/index`, and also `/foo` 103 | */ 104 | 105 | 106 | /** 107 | * (4) View blueprints 108 | * 109 | * These routes can be disabled by setting (in config/controllers.js): 110 | * `module.exports.views.blueprints = false` 111 | * 112 | * If you have a view file at `/views/foo/bar.ejs`, it will be rendered and served 113 | * automatically via the route: `/foo/bar` 114 | * 115 | */ 116 | 117 | /** 118 | * (5) Shortcut CRUD blueprints 119 | * 120 | * These routes can be disabled by setting (in config/controllers.js) 121 | * `module.exports.controllers.blueprints.shortcuts = false` 122 | * 123 | * If you have a model, `Foo`, and a controller, `FooController`, 124 | * you can access CRUD operations for that model at: 125 | * /foo/find/:id? -> search lampshades using specified criteria or with id=:id 126 | * 127 | * /foo/create -> create a lampshade using specified values 128 | * 129 | * /foo/update/:id -> update the lampshade with id=:id 130 | * 131 | * /foo/destroy/:id -> delete lampshade with id=:id 132 | * 133 | */ 134 | 135 | /** 136 | * (6) REST blueprints 137 | * 138 | * These routes can be disabled by setting (in config/controllers.js) 139 | * `module.exports.controllers.blueprints.rest = false` 140 | * 141 | * If you have a model, `Foo`, and a controller, `FooController`, 142 | * you can access CRUD operations for that model at: 143 | * 144 | * get /foo/:id? -> search lampshades using specified criteria or with id=:id 145 | * 146 | * post /foo -> create a lampshade using specified values 147 | * 148 | * put /foo/:id -> update the lampshade with id=:id 149 | * 150 | * delete /foo/:id -> delete lampshade with id=:id 151 | * 152 | */ 153 | 154 | /** 155 | * (7) Static assets 156 | * 157 | * Flat files in your `assets` directory- (these are sometimes referred to as 'public') 158 | * If you have an image file at `/assets/images/foo.jpg`, it will be made available 159 | * automatically via the route: `/images/foo.jpg` 160 | * 161 | */ 162 | 163 | 164 | 165 | /** 166 | * Finally, if nothing else matched, the default 404 handler is triggered. 167 | * See `config/404.js` to adjust your app's 404 logic. 168 | */ 169 | -------------------------------------------------------------------------------- /example/old/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | 'use strict'; 4 | 5 | // Get path to core grunt dependencies from Sails 6 | var depsPath = grunt.option('gdsrc') || 'node_modules/sails/node_modules'; 7 | grunt.loadTasks(depsPath + '/grunt-contrib-clean/tasks'); 8 | grunt.loadTasks(depsPath + '/grunt-contrib-copy/tasks'); 9 | grunt.loadTasks(depsPath + '/grunt-contrib-concat/tasks'); 10 | grunt.loadTasks(depsPath + '/grunt-scriptlinker/tasks'); 11 | grunt.loadTasks(depsPath + '/grunt-contrib-jst/tasks'); 12 | grunt.loadTasks(depsPath + '/grunt-contrib-watch/tasks'); 13 | grunt.loadTasks(depsPath + '/grunt-contrib-uglify/tasks'); 14 | grunt.loadTasks(depsPath + '/grunt-contrib-cssmin/tasks'); 15 | grunt.loadTasks(depsPath + '/grunt-contrib-less/tasks'); 16 | 17 | // Project configuration. 18 | grunt.initConfig({ 19 | pkg: grunt.file.readJSON('package.json'), 20 | 21 | copy: { 22 | dev: { 23 | files: [ 24 | {expand: true, cwd: './assets', src: ['**/*'], dest: '.tmp/public'} 25 | ] 26 | }, 27 | build: { 28 | files: [ 29 | {expand: true, cwd: '.tmp/public', src: ['**/*'], dest: 'www'} 30 | ] 31 | } 32 | }, 33 | 34 | clean: { 35 | dev: ['.tmp/public/**'], 36 | build: ['www'] 37 | }, 38 | 39 | jst: { 40 | dev: { 41 | options: { 42 | templateSettings: { 43 | interpolate : /\{\{(.+?)\}\}/g 44 | } 45 | }, 46 | files: { 47 | '.tmp/public/jst.js': ['assets/templates/**/*.html'] 48 | } 49 | } 50 | }, 51 | 52 | less: { 53 | dev: { 54 | files: [ 55 | { 56 | expand: true, 57 | cwd: 'assets/styles/', 58 | src: ['*.less'], 59 | dest: '.tmp/public/styles/', 60 | ext: '.css' 61 | } 62 | ] 63 | } 64 | }, 65 | 66 | concat: { 67 | js: { 68 | src: ['.tmp/public/mixins/**/*.js', '.tmp/public/components/**/*.js'], 69 | dest: '.tmp/public/concat/production.js' 70 | }, 71 | css: { 72 | src: ['.tmp/public/mixins/**/*.css', '.tmp/public/styles/**/*.css'], 73 | dest: '.tmp/public/concat/production.css' 74 | } 75 | }, 76 | 77 | uglify: { 78 | dist: { 79 | src: ['.tmp/public/concat/production.js'], 80 | dest: '.tmp/public/min/production.js' 81 | } 82 | }, 83 | 84 | cssmin: { 85 | dist: { 86 | src: ['.tmp/public/concat/production.css'], 87 | dest: '.tmp/public/min/production.css' 88 | } 89 | }, 90 | 91 | scriptlinker: { 92 | 93 | devJs: { 94 | options: { 95 | startTag: '', 96 | endTag: '', 97 | fileTmpl: '\n\n', 98 | appRoot: '.tmp/public/' 99 | }, 100 | files: { 101 | '.tmp/public/index.html': ['.tmp/public/mixins/**/*.js', '.tmp/public/components/**/*.js'] 102 | } 103 | }, 104 | 105 | prodJs: { 106 | options: { 107 | startTag: '', 108 | endTag: '', 109 | fileTmpl: '\n\n', 110 | appRoot: '.tmp/public/' 111 | }, 112 | files: { 113 | '.tmp/public/index.html': ['.tmp/public/min/production.js'] 114 | } 115 | }, 116 | 117 | devStyles: { 118 | options: { 119 | startTag: '', 120 | endTag: '', 121 | fileTmpl: '\n\n', 122 | appRoot: '.tmp/public/' 123 | }, 124 | files: { 125 | '.tmp/public/index.html': ['.tmp/public/mixins/**/*.css', '.tmp/public/styles/**/*.css'] 126 | } 127 | }, 128 | 129 | prodStyles: { 130 | options: { 131 | startTag: '', 132 | endTag: '', 133 | fileTmpl: '\n\n', 134 | appRoot: '.tmp/public/' 135 | }, 136 | files: { 137 | '.tmp/public/index.html': ['.tmp/public/min/production.css'] 138 | } 139 | }, 140 | 141 | // Bring in JST template object 142 | devTpl: { 143 | options: { 144 | startTag: '', 145 | endTag: '', 146 | fileTmpl: '\n\n', 147 | appRoot: '.tmp/public/' 148 | }, 149 | files: { 150 | '.tmp/public/index.html': ['.tmp/public/jst.js'] 151 | } 152 | } 153 | }, 154 | 155 | watch : { 156 | api: { 157 | 158 | // API files to watch: 159 | files: ['api/**/*'] 160 | }, 161 | assets: { 162 | 163 | // Assets to watch: 164 | files: ['assets/**/*'], 165 | 166 | // When assets are changed: 167 | tasks: ['compileAssets', 'linkAssets'] 168 | } 169 | } 170 | }); 171 | 172 | // When Sails is lifted: 173 | grunt.registerTask('default', [ 174 | 'compileAssets', 175 | 'linkAssets', 176 | 'watch' 177 | ]); 178 | 179 | grunt.registerTask('compileAssets', [ 180 | 'clean:dev', 181 | 'jst:dev', 182 | 'less:dev', 183 | 'copy:dev' 184 | ]); 185 | 186 | grunt.registerTask('linkAssets', [ 187 | 188 | // Update link/script/template references in `assets` index.html 189 | 'scriptlinker:devJs', 190 | 'scriptlinker:devStyles', 191 | 'scriptlinker:devTpl' 192 | ]); 193 | 194 | 195 | // Build the assets into a web accessible folder. 196 | // (handy for phone gap apps, chrome extensions, etc.) 197 | grunt.registerTask('build', [ 198 | 'compileAssets', 199 | 'linkAssets', 200 | 'clean:build', 201 | 'copy:build' 202 | ]); 203 | 204 | // When sails is lifted in production 205 | grunt.registerTask('prod', [ 206 | 'clean:dev', 207 | 'jst:dev', 208 | 'less:dev', 209 | 'copy:dev', 210 | 'concat', 211 | 'uglify', 212 | 'cssmin', 213 | 'scriptlinker:prodJs', 214 | 'scriptlinker:prodStyles', 215 | 'scriptlinker:devTpl' 216 | ]); 217 | 218 | // When API files are changed: 219 | // grunt.event.on('watch', function(action, filepath) { 220 | // grunt.log.writeln(filepath + ' has ' + action); 221 | 222 | // // Send a request to a development-only endpoint on the server 223 | // // which will reuptake the file that was changed. 224 | // var baseurl = grunt.option('baseurl'); 225 | // var gruntSignalRoute = grunt.option('signalpath'); 226 | // var url = baseurl + gruntSignalRoute + '?action=' + action + '&filepath=' + filepath; 227 | 228 | // require('http').get(url) 229 | // .on('error', function(e) { 230 | // console.error(filepath + ' has ' + action + ', but could not signal the Sails.js server: ' + e.message); 231 | // }); 232 | // }); 233 | }; -------------------------------------------------------------------------------- /example/basic/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 | }; -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # Architecture and Design 2 | 3 | ## Components 4 | 5 | Components are attached to a particular region element, which is identified by a unique selector. Components always have a template, but sometimes you can have a template without a component, in which case a simple anonymous component is automatically generated to render it. 6 | 7 | ```javascript 8 | define([], function () { 9 | return Mast.Component.extend({ 10 | // Identity automatically links this component 11 | // to the template with name="UsersPane" 12 | name: 'UsersPane', 13 | 14 | // DOM event subscriptions 15 | scroll: function updateYPositionTicker (e) {}, 16 | 'mouseover a, button': function (e) {}, 17 | 'click a, button': function submit (e) {}, 18 | 'touchhold a, button': 'mouseover a, button', 19 | 20 | // Global event subscriptions 21 | '%dialog:open': function disableInteraction () {}, 22 | '%dialog:close': function enableInteraction () {}, 23 | 24 | // Server-sent event subscriptions 25 | '~Users:create': function createUser (user) {}, 26 | 27 | // Route subscriptions 28 | '#users/:id': function scrollToUser (id) {} 29 | }); 30 | }); 31 | 32 | ``` 33 | 34 | ## Templates 35 | 36 | Templates are loaded as script tags and held in memory. They may contain sub-regions, and the rules for rendering those regions are as specified in the following section on "Regions." 37 | 38 | ## Regions 39 | 40 | Regions must be unique within their siblings, and by convention, the `` tag is used (with the notable exception of `` as the top-level element for single-page apps being attached built from scratch) 41 | 42 | We attempt to automatically link regions with components by name (i.e. the region with name="foo" corresponds to the component named "foo"). If a region "foo" is located in the template for a region "bar", it can still be uniquely identified. If a unique identity cannot be determined, an error is thrown and rendering fails. 43 | 44 | Dependencies are infered, and subviews are rendered accordingly (i.e. if a region lies within a template, when the template is rendered, the region is magically attached, and unless this behavior is overridden in the parent component's logic, the component and/or template with the same "id" as the region is loaded and rendered) 45 | 46 | 47 | ## Remote data store communication 48 | 49 | Backbone models and collections are used for communication with asynchronous endpoints, as well as eventing from those endpoints. RPC-style server communication is a highly desirable feature IMO, but something we can worry about later. 50 | 51 | 52 | 53 | #### Current backbone collection and model methods 54 | 55 | ```javascript 56 | // Context will automatically be populated with a 'MostActiveUsers' key 57 | // which now corresponds to a list of data 58 | var MostActiveUsers = Mast.Collection.extend({ 59 | name: 'MostActiveUsers', 60 | url: '/user', 61 | 62 | // Custom overrides can be applied 63 | fetch: function (options, cb) { 64 | httpGet(this.url, options, cb); 65 | } 66 | }); 67 | 68 | var ThisUser = Mast.Model.extend({ 69 | name: 'ThisUser', 70 | 71 | // Overrides can also be specified as routes 72 | fetch: 'get /user/me', 73 | create: null, 74 | save: 'put /user/me', 75 | destroy: null 76 | }); 77 | 78 | // Control methods can be called from anywhere in the app to trigger endpoint operations 79 | Mast.ctx.MostActiveUsers.fetch(); 80 | Mast.ctx.MostActiveUsers.create(); 81 | Mast.ctx.MostActiveUsers.destroy(); 82 | // var user = ... 83 | user.save(); 84 | 85 | // Latest value of Users collection is available in templates 86 | ```ejs 87 | 88 |
  • <%=user.name%>
  • 89 |
    90 | ``` 91 | 92 | ``` 93 | 94 | #### Listen for server update 95 | 96 | ```javascript 97 | Users.on('sync', function() {}); 98 | 99 | // or.. 100 | { 101 | '~Users:sync': function () {} 102 | } 103 | ``` 104 | 105 | #### Someday (RPC) 106 | 107 | ```javascript 108 | Email.send(); // talks to email endpoint that you define in your model 109 | 110 | ``` 111 | 112 | 113 | ## Context 114 | 115 | The convention is to keep context data as **flat** as possible. The goal is not to maintain an elegant data structure here-- merely to capture the minimal amount of information needed to render the UI. If a context datum you're trying to access doesn't exist, a warning is logged and the rendering fails. 116 | 117 | This means, instead of building nested structures which represent real data relationships, it's simpler, and equally effective, to keep the data model flat. This makes for cleaner templates. See the example below, for instance: 118 | 119 | ## Referencing context data from templates 120 | 121 | Templates can reference the app context, which is automatically injected. So if the app context contains: 122 | 123 | ```javascript 124 | { 125 | category: { 126 | id: 5, 127 | type: 'Science' 128 | }, 129 | 130 | topic: { 131 | id: 2, 132 | name: 'Computer Science' 133 | }, 134 | 135 | courses: [ 136 | {id: 'CS101', title: 'Intro to CS'}, 137 | {id: 'CS435', title: 'Automata Theory'}, 138 | {id: 'CS313k', title: 'Logic, Sets, and Functions'}, 139 | {id: 'CS305', title: 'Algorithms and Data Structures'} 140 | ] 141 | } 142 | ``` 143 | 144 | The template can access this data like so: 145 | 146 | ```html 147 |

    <%=category.type%>:<%=topic.

    148 | Click here to see more details 149 | 150 | ``` 151 | 152 | 153 | ## What about lists? (Or "trees"?) 154 | 155 | 156 | Consider this template: 157 | 158 | ```html 159 |

    <%=category.type%>:<%=topic.

    160 | Click here to see more details 161 |

    Courses

    162 | 169 | ``` 170 | 171 | This is a simplistic way of doing a default rendering of the data, but more likely than not, it's possible you'll want to override certain pieces of behavior in the rendering here. This lets you do stuff like fade in individual elements, etc. Additionally, each item (or "branch") of your list needs to have the capabilities of a component. Here's how you do it: 172 | 173 | In this example, a new region called "courses" is defined: 174 | ```html 175 |

    <%=category.type%>:<%=topic.

    176 | Click here to see more details 177 |

    Courses

    178 | 181 | ``` 182 | 183 | When a region specifies an `each` attribute, it indicates that it will render its template multiple times-- exactly once for each course in the `courses` context datum. It also provides access to a `course` attribute in the `courseList` that refers to the current course. 184 | 185 | 186 | ## Implicit templates 187 | 188 | You can also identify a region's contents as an implicit template. Take a look at our running example: 189 | 190 | ```html 191 |

    <%=category.type%>:<%=topic.

    192 | 193 |

    Topic Details for <%=topic.name%>:

    194 |
    195 |

    Courses

    196 | 203 | ``` 204 | 205 | In this case, we actually omitted the `name` attribute in our regions and opted instead to include a template inline. If a region contains any non-whitespace text, it is assumed that this is to be the HTML which will be rendered as the template. We can still include a name if we want to be able to attach a component to this region, but we don't HAVE to, since the template can be rendered automatically. This is a good move during prototyping, before pulling all the templates out. 206 | 207 | > Note that `` tags are **NOT** removed from your template before it is rendered (especially important in the above example, since `