├── .gitignore
├── Procfile
├── README.md
├── app
├── controllers
│ ├── apiController.js
│ ├── homeController.js
│ ├── todoController.js
│ └── userController.js
├── index.js
├── lib
│ ├── dal.js
│ ├── logging.js
│ ├── middleware.js
│ ├── routes.js
│ ├── server.js
│ └── views.js
├── middleware
│ ├── errorMiddleware.js
│ └── passportMiddleware.js
├── models
│ ├── Todo.js
│ └── User.js
├── public
│ ├── css
│ │ ├── bootstrap-responsive.css
│ │ ├── bootstrap-responsive.min.css
│ │ ├── bootstrap.css
│ │ ├── bootstrap.min.css
│ │ └── style.css
│ ├── favicon.ico
│ ├── img
│ │ ├── glyphicons-halflings-white.png
│ │ └── glyphicons-halflings.png
│ ├── index.html
│ ├── js
│ │ ├── app.js
│ │ ├── controllers.js
│ │ ├── directives.js
│ │ ├── filters.js
│ │ ├── lib
│ │ │ ├── angular
│ │ │ │ ├── angular-cookies.js
│ │ │ │ ├── angular-cookies.min.js
│ │ │ │ ├── angular-loader.js
│ │ │ │ ├── angular-loader.min.js
│ │ │ │ ├── angular-resource.js
│ │ │ │ ├── angular-resource.min.js
│ │ │ │ ├── angular-sanitize.js
│ │ │ │ ├── angular-sanitize.min.js
│ │ │ │ ├── angular.js
│ │ │ │ ├── angular.min.js
│ │ │ │ └── version.txt
│ │ │ ├── bootstrap
│ │ │ │ ├── bootstrap.js
│ │ │ │ └── bootstrap.min.js
│ │ │ ├── jquery
│ │ │ │ └── jquery.js
│ │ │ └── underscore.js
│ │ └── services.js
│ └── partials
│ │ ├── 404.html
│ │ ├── index.html
│ │ ├── login.html
│ │ └── todos.html
└── views
│ └── partials
│ └── .git.me
├── config
├── default.json
└── production.json
├── doc
└── .gitme
├── logs
└── .git.me
├── package.json
└── test
└── .gitme
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env.js
3 | .env.json
4 | .idea
5 | npm-debug.log
6 | .ini
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: train run
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Express Train Seed
2 |
3 | This is a starting point for making AngularJS web applications with a node.js back end. Here are some of the features:
4 | - organized directories for both server side and client side
5 | - basic user authentication using 401 responses from server
6 | - data persistence using mongoose
7 |
8 | Check out the demo site here: http://angular-express-train-seed.herokuapp.com/
9 |
10 | Order you should learn this stuff, if you don't know it already:
11 |
12 | 1. AngularJS (http://angularjs.org/)
13 | 2. Node.js (http://nodejs.org/)
14 | 3. Express (http://expressjs.com/)
15 | 4. Express Train (https://npmjs.org/package/express-train)
16 |
17 | Since this app is using express train, start it by using the command 'train run'.
18 | You may need to install express train globally ('npm install -g express-train').
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/controllers/apiController.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app) {
2 | var controller = {};
3 |
4 | /*
5 | Generic CRUD functions for any model
6 | */
7 | controller.search = [
8 | /*
9 | route functions get 3 args - the request object, the response object, and next - a callback to move on
10 | to the next middleware.
11 | req.query = json object with query string arguments
12 | req.params = json object with values of routing params such as :model or :id
13 | req.body = json request body from post / put requests
14 | */
15 | function (req, res, next) {
16 | console.log('starting api.search');
17 | var query = req.query;
18 | //req.Model is a value I set in libs/params.js
19 | req.Model.find(query, function (err, docs) {
20 | if (err) return next(err);
21 | return res.json(docs);
22 | });
23 | }
24 | ]
25 | controller.create = [
26 | function (req, res, next) {
27 | console.log(req.body);
28 | var model = new req.Model(req.body);
29 | model.save(function (err, doc) {
30 | if (err) return next(err);
31 | return res.json(doc);
32 | })
33 | }
34 | ]
35 | controller.read = [
36 | function (req, res, next) {
37 | var id = req.params.id;
38 | req.Model.findById(id, function (err, doc) {
39 | if (err) return next(err);
40 | if (doc === null) return res.send(404);
41 | return res.json(doc);
42 | });
43 | }
44 | ]
45 | controller.update = [
46 | function (req, res, next) {
47 | var id = req.params.id;
48 | delete req.body._id; //removing the _id from the model to prevent mongo from thinking we are trying to change its type
49 | req.Model.findByIdAndUpdate(id, req.body, function (err, doc) {
50 | if (err) return next(err);
51 | if (doc === null) return res.send(404);
52 | return res.json(doc);
53 | })
54 | }
55 | ]
56 | controller.destroy = [
57 | function (req, res, next) {
58 | var id = req.params.id;
59 | req.Model.findByIdAndRemove(id, function (err, doc) {
60 | if (err) return next(err);
61 | if (doc === null) return res.send(404);
62 | return res.send(204);
63 | })
64 | }
65 | ]
66 |
67 | return controller;
68 | }
69 |
--------------------------------------------------------------------------------
/app/controllers/homeController.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app) {
2 | return {
3 | index:[
4 | function (req, res, next) {
5 | //we just want to return an html file. Angular will take care of the templating.
6 | res.sendfile(app.set('public') + '/index.html');
7 | }]
8 | };
9 | };
--------------------------------------------------------------------------------
/app/controllers/todoController.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app, Todo) {
2 | var controller = {};
3 |
4 | controller.preSearch = [
5 | function (req, res, next) {
6 | console.log('this it?');
7 | req.query = {userId: req.user.id};
8 | req.Model = Todo;
9 | next();
10 | }
11 | ]
12 | controller.preCreate = [
13 | function (req, res, next) {
14 | req.body.userId = req.user.id;
15 | req.Model = Todo;
16 | next();
17 | }
18 | ]
19 | controller.preUpdate = [
20 | function (req, res, next) {
21 | //try to find a todo that matches the ID in the uri and belongs to the user who is logged i
22 | Todo.find({_id: req.params.id, userId: req.user.id}, function (err, results) {
23 | if (err) return next(err);
24 | if(!results) return res.send(401); //trying to update a todo that isn't yours?!?!?!
25 | req.Model = Todo;
26 | next();
27 | });
28 | }
29 | ]
30 | controller.preDestroy = [
31 | function (req, res, next) {
32 | //try to find a todo that matches the ID in the uri and belongs to the user who is logged in
33 | Todo.find({_id: req.params.id, userId: req.user.id}, function (err, results) {
34 | if (err) return next(err);
35 | if(!results) return res.send(401); //trying to update a todo that isn't yours?!?!?!
36 | req.Model = Todo;
37 | next();
38 | });
39 | }
40 | ]
41 |
42 | return controller;
43 | }
44 |
--------------------------------------------------------------------------------
/app/controllers/userController.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app, User, passportMiddleware) {
2 | return {
3 | getCurrent: [
4 | function (req, res, next) {
5 | if (!req.user) {
6 | res.send(400);
7 | }
8 | else {
9 | User.findOne({_id: req.user.id}, function (err, results) {
10 | if (err) return next(err);
11 | if (results) {
12 | console.log({username: results.username});
13 | res.send({username: results.username});
14 | }
15 | else {
16 | res.send(400);
17 | }
18 | });
19 | }
20 | }],
21 |
22 | authenticate: [
23 | function (req, res, next) {
24 | passportMiddleware.authenticate('local', function (err, user, info) {
25 | if (err) return next(err);
26 | if (!user) {
27 | return res.send('Invalid username or password.', 400);
28 | }
29 | req.logIn(user, function (err) {
30 | if (err) return next(err);
31 | return res.send('Ok', 200);
32 | });
33 | })(req, res, next);
34 | }],
35 |
36 | create: [
37 | function (req, res, next) {
38 | var user = new User(req.body);
39 | User.findOne({username: user.username}, function (err, results) {
40 | if (err) return next(err);
41 | if (results) {
42 | res.send('A user with this username already exists.', 400);
43 | }
44 | else {
45 | user.save(function (err, results) {
46 | if (err) return next(err);
47 | req.logIn(user, function () {
48 | res.send('ok', 200)
49 | });
50 | });
51 | }
52 | })
53 | }],
54 | //logout/kill session
55 | kill: [
56 | function (req, res) {
57 | if (req.session) {
58 | req.session.destroy(function () {
59 | res.send('ok', 200)
60 | });
61 | }
62 | else {
63 | res.send('ok', 200)
64 | }
65 | }]
66 | };
67 | };
68 |
69 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | var train = require('express-train');
2 |
3 | module.exports = train(__dirname);
--------------------------------------------------------------------------------
/app/lib/dal.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 |
3 | module.exports = function (config) {
4 | //set up mongoose database connection
5 | if(!mongoose.connection.readyState){
6 | mongoose.connect(config.mongodb.uri);
7 | }
8 | }
--------------------------------------------------------------------------------
/app/lib/logging.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(app) {
3 |
4 | }
--------------------------------------------------------------------------------
/app/lib/middleware.js:
--------------------------------------------------------------------------------
1 | var express = require('express'),
2 | MongoStore = require('connect-mongo')(express),
3 | connect_timeout = require('connect-timeout');
4 |
5 |
6 | // Middleware
7 |
8 | module.exports = function (app, config, passportMiddleware ) {
9 |
10 | // Sessions
11 | var mongoStore = new MongoStore({
12 | url: config.mongodb.uri
13 | });
14 |
15 | var session_middleware = express.session({
16 | key:config.session.key,
17 | secret: config.session.secret,
18 | store:mongoStore
19 | });
20 |
21 | // Error handler
22 | var error_middleware = express.errorHandler({
23 | dumpExceptions:true,
24 | showStack:true
25 | });
26 |
27 | // Middleware stack for all requests
28 | app.use(express['static'](app.set('public'))); // static files in /public
29 | app.use(connect_timeout({ time:config.request_timeout })); // request timeouts
30 | app.use(express.cookieParser()); // req.cookies
31 | app.use(session_middleware); // req.session
32 | app.use(express.bodyParser()); // req.body & req.files
33 | app.use(express.methodOverride()); // '_method' property in body (POST -> DELETE / PUT)
34 | app.use(passportMiddleware.initialize());
35 | app.use(passportMiddleware.session());
36 | app.use(passportMiddleware.setLocals);
37 | app.use(app.router); // routes in lib/routes.js
38 |
39 | // Handle errors thrown from middleware/routes
40 | app.use(error_middleware);
41 |
42 | app.configure('development', function(){
43 | require('express-trace')(app);
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/app/lib/routes.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app, homeController, userController, todoController, apiController) {
2 |
3 | // Home
4 | app.get('/', homeController.index);
5 |
6 | //User
7 | app.get('/user', userController.getCurrent);
8 | app.post('/user/login', userController.authenticate);
9 | app.post('/user/register', userController.create);
10 | app.post('/user/logout', userController.kill);
11 |
12 | /*
13 | Rather than go right to the API routes below, we need to do a few things first, like say what Users Todos
14 | to return and stamp the Todos they create with their ID. Once we've dne these things (in the "pre" functions),
15 | we end up invoking the generic api methods. Cool, right?
16 | */
17 | app.get('/api/Todo', ensureAuthenticated, todoController.preSearch, apiController.search);
18 | app.post('/api/Todo', ensureAuthenticated, todoController.preCreate, apiController.create);
19 | app.post('/api/Todo/:id', ensureAuthenticated, todoController.preUpdate, apiController.update);
20 | app.del('/api/Todo/:id', ensureAuthenticated, todoController.preDestroy, apiController.destroy);
21 |
22 | //Generic restful api for all models - if previous routes are not matched, will fall back to these
23 | //See libs/params.js, which adds param middleware to load & set req.Model based on :model argument
24 | app.get('/api/:model', ensureAuthenticated, apiController.search);
25 | app.post('/api/:model', ensureAuthenticated, apiController.create);
26 | app.get('/api/:model/:id', ensureAuthenticated, apiController.read);
27 | app.post('/api/:model/:id', ensureAuthenticated, apiController.update);
28 | app.del('/api/:model/:id', ensureAuthenticated, apiController.destroy);
29 |
30 | /*
31 | default route if we haven't hit anything yet
32 | This will just return the index file and pass the url to our angular app.
33 | Angular will decide if it should display a 404 page.
34 | */
35 | app.get('*', homeController.index);
36 |
37 | /*
38 | Route Helpers
39 | */
40 |
41 | //whenever a router parameter :model is matched, this is run
42 | app.param('model', function (req, res, next, model) {
43 | console.log(app);
44 | var Model = app.models[model];
45 | if (Model === undefined) {
46 | //if the request is for a model that does not exist, 404
47 | return res.send(404);
48 | }
49 | req.Model = Model;
50 | return next();
51 | });
52 |
53 | /*
54 | Make sure this person is logged in.
55 | If they aren't, return a 401. This let's angular know to go to a login page.
56 | */
57 | function ensureAuthenticated(req, res, next) {
58 | if (req.isAuthenticated()) {
59 | return next();
60 | }
61 | res.send(401)
62 | }
63 | };
--------------------------------------------------------------------------------
/app/lib/server.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app, config) {
2 |
3 | console.log('[express train application listening on %s]', config.port);
4 | return app.listen(config.port);
5 | }
--------------------------------------------------------------------------------
/app/lib/views.js:
--------------------------------------------------------------------------------
1 | var hbs = require('express-hbs'),
2 | path = require('path');
3 |
4 | module.exports = function (app) {
5 |
6 | //set up view engine
7 | app.set('view engine', 'hbs');
8 |
9 | app.engine('hbs', hbs.express3({
10 | partialsDir:path.join(__dirname, "../views/partials")
11 | }));
12 |
13 | // Static locals
14 | app.locals({
15 | });
16 |
17 | };
--------------------------------------------------------------------------------
/app/middleware/errorMiddleware.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | return {
3 |
4 | log: function(msg) {
5 | return function(req, res, next) {
6 | console.log(msg);
7 | return next();
8 | };
9 | },
10 |
11 | apologize: function(err, req, res, next) {
12 | req.flash('Sorry, something went wrong');
13 | res.redirect('/');
14 | console.log('ERROR:', err);
15 | }
16 | };
17 | };
--------------------------------------------------------------------------------
/app/middleware/passportMiddleware.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport'),
2 | LocalStrategy = require('passport-local').Strategy;
3 |
4 | var BAD_LOGIN_STRING = 'Invalid username or password'
5 |
6 | module.exports = function(app, User) {
7 | var strategy = new LocalStrategy(
8 | {
9 | usernameField: 'username',
10 | passwordField: 'password'
11 | },
12 | function (username, password, done) {
13 | console.log('starting local strategy');
14 | User.findOne({username: username}, function (err, user) {
15 | console.log('user = ' + user);
16 | if (err) return done(err);
17 | if (!user) return done(null, false, { message:BAD_LOGIN_STRING });
18 | if (user.authenticate(password)) return done(null, user);
19 | else return done(null, false, { message:BAD_LOGIN_STRING });
20 | });
21 | });
22 |
23 | passport.serializeUser(function (user, done) {
24 | done(null, user.id);
25 | });
26 |
27 | passport.deserializeUser(function (id, done) {
28 | User.findById(id, function(err, user){
29 | done(err, user);
30 | });
31 | });
32 |
33 | passport.use(strategy);
34 |
35 | passport.setLocals = function(req, res, next) {
36 | if(req.isAuthenticated()) {
37 | res.locals.user = req.user;
38 | }
39 | return next();
40 | }
41 |
42 | return passport;
43 | }
44 |
--------------------------------------------------------------------------------
/app/models/Todo.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 |
3 | module.exports = function (app) {
4 |
5 | var TodoSchema = new mongoose.Schema({
6 | text: {type: String}, //text of the todo
7 | complete: {type: Boolean}, //whether the todo is complete or not
8 | userId: {type: mongoose.Schema.Types.ObjectId} //the user this todo belongs to
9 | });
10 |
11 | return mongoose.model('Todo', TodoSchema);
12 | }
13 |
--------------------------------------------------------------------------------
/app/models/User.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose'),
2 | crypto = require('crypto');
3 |
4 |
5 | module.exports = function (app) {
6 |
7 | var UserSchema = new mongoose.Schema({
8 | username: { type:String, required:true , unique:true},
9 | hashed_password: {type:String, required:true},
10 | salt: {type:String, required:true}
11 | });
12 |
13 | UserSchema.virtual('password')
14 | .set(function(password) {
15 | this._password = password;
16 | this.salt = this.makeSalt();
17 | this.hashed_password = this.encryptPassword(password);
18 | })
19 | .get(function() { return this._password; });
20 |
21 | UserSchema.method('authenticate', function(plainText) {
22 | console.log('authenticate called:')
23 | console.log('plain text = ' + plainText)
24 | console.log('hashed = ' + this.encryptPassword(plainText))
25 | console.log('db password= ' + this.hashed_password)
26 | return this.encryptPassword(plainText) === this.hashed_password;
27 | });
28 |
29 | UserSchema.method('makeSalt', function() {
30 | return Math.round((new Date().valueOf() * Math.random())) + '';
31 | });
32 |
33 | UserSchema.method('encryptPassword', function(password) {
34 | return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
35 | });
36 |
37 | UserSchema.method('generateToken', function() {
38 | return crypto.createHash('md5').update(this.username + Date().toString()).digest("hex");
39 | });
40 |
41 | UserSchema.pre('save', function(next) {
42 |
43 | this.token = this.generateToken();
44 |
45 | if (!validatePresenceOf(this.password || this.hashed_password)) {
46 | next(new Error('Invalid password'));
47 | } else {
48 | next();
49 | }
50 | });
51 |
52 | return mongoose.model('User', UserSchema);
53 |
54 | }
55 |
56 | function validatePresenceOf(value) {
57 | return value && value.length;
58 | }
--------------------------------------------------------------------------------
/app/public/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | .clearfix {
12 | *zoom: 1;
13 | }
14 |
15 | .clearfix:before,
16 | .clearfix:after {
17 | display: table;
18 | line-height: 0;
19 | content: "";
20 | }
21 |
22 | .clearfix:after {
23 | clear: both;
24 | }
25 |
26 | .hide-text {
27 | font: 0/0 a;
28 | color: transparent;
29 | text-shadow: none;
30 | background-color: transparent;
31 | border: 0;
32 | }
33 |
34 | .input-block-level {
35 | display: block;
36 | width: 100%;
37 | min-height: 30px;
38 | -webkit-box-sizing: border-box;
39 | -moz-box-sizing: border-box;
40 | box-sizing: border-box;
41 | }
42 |
43 | @-ms-viewport {
44 | width: device-width;
45 | }
46 |
47 | .hidden {
48 | display: none;
49 | visibility: hidden;
50 | }
51 |
52 | .visible-phone {
53 | display: none !important;
54 | }
55 |
56 | .visible-tablet {
57 | display: none !important;
58 | }
59 |
60 | .hidden-desktop {
61 | display: none !important;
62 | }
63 |
64 | .visible-desktop {
65 | display: inherit !important;
66 | }
67 |
68 | @media (min-width: 768px) and (max-width: 979px) {
69 | .hidden-desktop {
70 | display: inherit !important;
71 | }
72 | .visible-desktop {
73 | display: none !important ;
74 | }
75 | .visible-tablet {
76 | display: inherit !important;
77 | }
78 | .hidden-tablet {
79 | display: none !important;
80 | }
81 | }
82 |
83 | @media (max-width: 767px) {
84 | .hidden-desktop {
85 | display: inherit !important;
86 | }
87 | .visible-desktop {
88 | display: none !important;
89 | }
90 | .visible-phone {
91 | display: inherit !important;
92 | }
93 | .hidden-phone {
94 | display: none !important;
95 | }
96 | }
97 |
98 | .visible-print {
99 | display: none !important;
100 | }
101 |
102 | @media print {
103 | .visible-print {
104 | display: inherit !important;
105 | }
106 | .hidden-print {
107 | display: none !important;
108 | }
109 | }
110 |
111 | @media (min-width: 1200px) {
112 | .row {
113 | margin-left: -30px;
114 | *zoom: 1;
115 | }
116 | .row:before,
117 | .row:after {
118 | display: table;
119 | line-height: 0;
120 | content: "";
121 | }
122 | .row:after {
123 | clear: both;
124 | }
125 | [class*="span"] {
126 | float: left;
127 | min-height: 1px;
128 | margin-left: 30px;
129 | }
130 | .container,
131 | .navbar-static-top .container,
132 | .navbar-fixed-top .container,
133 | .navbar-fixed-bottom .container {
134 | width: 1170px;
135 | }
136 | .span12 {
137 | width: 1170px;
138 | }
139 | .span11 {
140 | width: 1070px;
141 | }
142 | .span10 {
143 | width: 970px;
144 | }
145 | .span9 {
146 | width: 870px;
147 | }
148 | .span8 {
149 | width: 770px;
150 | }
151 | .span7 {
152 | width: 670px;
153 | }
154 | .span6 {
155 | width: 570px;
156 | }
157 | .span5 {
158 | width: 470px;
159 | }
160 | .span4 {
161 | width: 370px;
162 | }
163 | .span3 {
164 | width: 270px;
165 | }
166 | .span2 {
167 | width: 170px;
168 | }
169 | .span1 {
170 | width: 70px;
171 | }
172 | .offset12 {
173 | margin-left: 1230px;
174 | }
175 | .offset11 {
176 | margin-left: 1130px;
177 | }
178 | .offset10 {
179 | margin-left: 1030px;
180 | }
181 | .offset9 {
182 | margin-left: 930px;
183 | }
184 | .offset8 {
185 | margin-left: 830px;
186 | }
187 | .offset7 {
188 | margin-left: 730px;
189 | }
190 | .offset6 {
191 | margin-left: 630px;
192 | }
193 | .offset5 {
194 | margin-left: 530px;
195 | }
196 | .offset4 {
197 | margin-left: 430px;
198 | }
199 | .offset3 {
200 | margin-left: 330px;
201 | }
202 | .offset2 {
203 | margin-left: 230px;
204 | }
205 | .offset1 {
206 | margin-left: 130px;
207 | }
208 | .row-fluid {
209 | width: 100%;
210 | *zoom: 1;
211 | }
212 | .row-fluid:before,
213 | .row-fluid:after {
214 | display: table;
215 | line-height: 0;
216 | content: "";
217 | }
218 | .row-fluid:after {
219 | clear: both;
220 | }
221 | .row-fluid [class*="span"] {
222 | display: block;
223 | float: left;
224 | width: 100%;
225 | min-height: 30px;
226 | margin-left: 2.564102564102564%;
227 | *margin-left: 2.5109110747408616%;
228 | -webkit-box-sizing: border-box;
229 | -moz-box-sizing: border-box;
230 | box-sizing: border-box;
231 | }
232 | .row-fluid [class*="span"]:first-child {
233 | margin-left: 0;
234 | }
235 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
236 | margin-left: 2.564102564102564%;
237 | }
238 | .row-fluid .span12 {
239 | width: 100%;
240 | *width: 99.94680851063829%;
241 | }
242 | .row-fluid .span11 {
243 | width: 91.45299145299145%;
244 | *width: 91.39979996362975%;
245 | }
246 | .row-fluid .span10 {
247 | width: 82.90598290598291%;
248 | *width: 82.8527914166212%;
249 | }
250 | .row-fluid .span9 {
251 | width: 74.35897435897436%;
252 | *width: 74.30578286961266%;
253 | }
254 | .row-fluid .span8 {
255 | width: 65.81196581196582%;
256 | *width: 65.75877432260411%;
257 | }
258 | .row-fluid .span7 {
259 | width: 57.26495726495726%;
260 | *width: 57.21176577559556%;
261 | }
262 | .row-fluid .span6 {
263 | width: 48.717948717948715%;
264 | *width: 48.664757228587014%;
265 | }
266 | .row-fluid .span5 {
267 | width: 40.17094017094017%;
268 | *width: 40.11774868157847%;
269 | }
270 | .row-fluid .span4 {
271 | width: 31.623931623931625%;
272 | *width: 31.570740134569924%;
273 | }
274 | .row-fluid .span3 {
275 | width: 23.076923076923077%;
276 | *width: 23.023731587561375%;
277 | }
278 | .row-fluid .span2 {
279 | width: 14.52991452991453%;
280 | *width: 14.476723040552828%;
281 | }
282 | .row-fluid .span1 {
283 | width: 5.982905982905983%;
284 | *width: 5.929714493544281%;
285 | }
286 | .row-fluid .offset12 {
287 | margin-left: 105.12820512820512%;
288 | *margin-left: 105.02182214948171%;
289 | }
290 | .row-fluid .offset12:first-child {
291 | margin-left: 102.56410256410257%;
292 | *margin-left: 102.45771958537915%;
293 | }
294 | .row-fluid .offset11 {
295 | margin-left: 96.58119658119658%;
296 | *margin-left: 96.47481360247316%;
297 | }
298 | .row-fluid .offset11:first-child {
299 | margin-left: 94.01709401709402%;
300 | *margin-left: 93.91071103837061%;
301 | }
302 | .row-fluid .offset10 {
303 | margin-left: 88.03418803418803%;
304 | *margin-left: 87.92780505546462%;
305 | }
306 | .row-fluid .offset10:first-child {
307 | margin-left: 85.47008547008548%;
308 | *margin-left: 85.36370249136206%;
309 | }
310 | .row-fluid .offset9 {
311 | margin-left: 79.48717948717949%;
312 | *margin-left: 79.38079650845607%;
313 | }
314 | .row-fluid .offset9:first-child {
315 | margin-left: 76.92307692307693%;
316 | *margin-left: 76.81669394435352%;
317 | }
318 | .row-fluid .offset8 {
319 | margin-left: 70.94017094017094%;
320 | *margin-left: 70.83378796144753%;
321 | }
322 | .row-fluid .offset8:first-child {
323 | margin-left: 68.37606837606839%;
324 | *margin-left: 68.26968539734497%;
325 | }
326 | .row-fluid .offset7 {
327 | margin-left: 62.393162393162385%;
328 | *margin-left: 62.28677941443899%;
329 | }
330 | .row-fluid .offset7:first-child {
331 | margin-left: 59.82905982905982%;
332 | *margin-left: 59.72267685033642%;
333 | }
334 | .row-fluid .offset6 {
335 | margin-left: 53.84615384615384%;
336 | *margin-left: 53.739770867430444%;
337 | }
338 | .row-fluid .offset6:first-child {
339 | margin-left: 51.28205128205128%;
340 | *margin-left: 51.175668303327875%;
341 | }
342 | .row-fluid .offset5 {
343 | margin-left: 45.299145299145295%;
344 | *margin-left: 45.1927623204219%;
345 | }
346 | .row-fluid .offset5:first-child {
347 | margin-left: 42.73504273504273%;
348 | *margin-left: 42.62865975631933%;
349 | }
350 | .row-fluid .offset4 {
351 | margin-left: 36.75213675213675%;
352 | *margin-left: 36.645753773413354%;
353 | }
354 | .row-fluid .offset4:first-child {
355 | margin-left: 34.18803418803419%;
356 | *margin-left: 34.081651209310785%;
357 | }
358 | .row-fluid .offset3 {
359 | margin-left: 28.205128205128204%;
360 | *margin-left: 28.0987452264048%;
361 | }
362 | .row-fluid .offset3:first-child {
363 | margin-left: 25.641025641025642%;
364 | *margin-left: 25.53464266230224%;
365 | }
366 | .row-fluid .offset2 {
367 | margin-left: 19.65811965811966%;
368 | *margin-left: 19.551736679396257%;
369 | }
370 | .row-fluid .offset2:first-child {
371 | margin-left: 17.094017094017094%;
372 | *margin-left: 16.98763411529369%;
373 | }
374 | .row-fluid .offset1 {
375 | margin-left: 11.11111111111111%;
376 | *margin-left: 11.004728132387708%;
377 | }
378 | .row-fluid .offset1:first-child {
379 | margin-left: 8.547008547008547%;
380 | *margin-left: 8.440625568285142%;
381 | }
382 | input,
383 | textarea,
384 | .uneditable-input {
385 | margin-left: 0;
386 | }
387 | .controls-row [class*="span"] + [class*="span"] {
388 | margin-left: 30px;
389 | }
390 | input.span12,
391 | textarea.span12,
392 | .uneditable-input.span12 {
393 | width: 1156px;
394 | }
395 | input.span11,
396 | textarea.span11,
397 | .uneditable-input.span11 {
398 | width: 1056px;
399 | }
400 | input.span10,
401 | textarea.span10,
402 | .uneditable-input.span10 {
403 | width: 956px;
404 | }
405 | input.span9,
406 | textarea.span9,
407 | .uneditable-input.span9 {
408 | width: 856px;
409 | }
410 | input.span8,
411 | textarea.span8,
412 | .uneditable-input.span8 {
413 | width: 756px;
414 | }
415 | input.span7,
416 | textarea.span7,
417 | .uneditable-input.span7 {
418 | width: 656px;
419 | }
420 | input.span6,
421 | textarea.span6,
422 | .uneditable-input.span6 {
423 | width: 556px;
424 | }
425 | input.span5,
426 | textarea.span5,
427 | .uneditable-input.span5 {
428 | width: 456px;
429 | }
430 | input.span4,
431 | textarea.span4,
432 | .uneditable-input.span4 {
433 | width: 356px;
434 | }
435 | input.span3,
436 | textarea.span3,
437 | .uneditable-input.span3 {
438 | width: 256px;
439 | }
440 | input.span2,
441 | textarea.span2,
442 | .uneditable-input.span2 {
443 | width: 156px;
444 | }
445 | input.span1,
446 | textarea.span1,
447 | .uneditable-input.span1 {
448 | width: 56px;
449 | }
450 | .thumbnails {
451 | margin-left: -30px;
452 | }
453 | .thumbnails > li {
454 | margin-left: 30px;
455 | }
456 | .row-fluid .thumbnails {
457 | margin-left: 0;
458 | }
459 | }
460 |
461 | @media (min-width: 768px) and (max-width: 979px) {
462 | .row {
463 | margin-left: -20px;
464 | *zoom: 1;
465 | }
466 | .row:before,
467 | .row:after {
468 | display: table;
469 | line-height: 0;
470 | content: "";
471 | }
472 | .row:after {
473 | clear: both;
474 | }
475 | [class*="span"] {
476 | float: left;
477 | min-height: 1px;
478 | margin-left: 20px;
479 | }
480 | .container,
481 | .navbar-static-top .container,
482 | .navbar-fixed-top .container,
483 | .navbar-fixed-bottom .container {
484 | width: 724px;
485 | }
486 | .span12 {
487 | width: 724px;
488 | }
489 | .span11 {
490 | width: 662px;
491 | }
492 | .span10 {
493 | width: 600px;
494 | }
495 | .span9 {
496 | width: 538px;
497 | }
498 | .span8 {
499 | width: 476px;
500 | }
501 | .span7 {
502 | width: 414px;
503 | }
504 | .span6 {
505 | width: 352px;
506 | }
507 | .span5 {
508 | width: 290px;
509 | }
510 | .span4 {
511 | width: 228px;
512 | }
513 | .span3 {
514 | width: 166px;
515 | }
516 | .span2 {
517 | width: 104px;
518 | }
519 | .span1 {
520 | width: 42px;
521 | }
522 | .offset12 {
523 | margin-left: 764px;
524 | }
525 | .offset11 {
526 | margin-left: 702px;
527 | }
528 | .offset10 {
529 | margin-left: 640px;
530 | }
531 | .offset9 {
532 | margin-left: 578px;
533 | }
534 | .offset8 {
535 | margin-left: 516px;
536 | }
537 | .offset7 {
538 | margin-left: 454px;
539 | }
540 | .offset6 {
541 | margin-left: 392px;
542 | }
543 | .offset5 {
544 | margin-left: 330px;
545 | }
546 | .offset4 {
547 | margin-left: 268px;
548 | }
549 | .offset3 {
550 | margin-left: 206px;
551 | }
552 | .offset2 {
553 | margin-left: 144px;
554 | }
555 | .offset1 {
556 | margin-left: 82px;
557 | }
558 | .row-fluid {
559 | width: 100%;
560 | *zoom: 1;
561 | }
562 | .row-fluid:before,
563 | .row-fluid:after {
564 | display: table;
565 | line-height: 0;
566 | content: "";
567 | }
568 | .row-fluid:after {
569 | clear: both;
570 | }
571 | .row-fluid [class*="span"] {
572 | display: block;
573 | float: left;
574 | width: 100%;
575 | min-height: 30px;
576 | margin-left: 2.7624309392265194%;
577 | *margin-left: 2.709239449864817%;
578 | -webkit-box-sizing: border-box;
579 | -moz-box-sizing: border-box;
580 | box-sizing: border-box;
581 | }
582 | .row-fluid [class*="span"]:first-child {
583 | margin-left: 0;
584 | }
585 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
586 | margin-left: 2.7624309392265194%;
587 | }
588 | .row-fluid .span12 {
589 | width: 100%;
590 | *width: 99.94680851063829%;
591 | }
592 | .row-fluid .span11 {
593 | width: 91.43646408839778%;
594 | *width: 91.38327259903608%;
595 | }
596 | .row-fluid .span10 {
597 | width: 82.87292817679558%;
598 | *width: 82.81973668743387%;
599 | }
600 | .row-fluid .span9 {
601 | width: 74.30939226519337%;
602 | *width: 74.25620077583166%;
603 | }
604 | .row-fluid .span8 {
605 | width: 65.74585635359117%;
606 | *width: 65.69266486422946%;
607 | }
608 | .row-fluid .span7 {
609 | width: 57.18232044198895%;
610 | *width: 57.12912895262725%;
611 | }
612 | .row-fluid .span6 {
613 | width: 48.61878453038674%;
614 | *width: 48.56559304102504%;
615 | }
616 | .row-fluid .span5 {
617 | width: 40.05524861878453%;
618 | *width: 40.00205712942283%;
619 | }
620 | .row-fluid .span4 {
621 | width: 31.491712707182323%;
622 | *width: 31.43852121782062%;
623 | }
624 | .row-fluid .span3 {
625 | width: 22.92817679558011%;
626 | *width: 22.87498530621841%;
627 | }
628 | .row-fluid .span2 {
629 | width: 14.3646408839779%;
630 | *width: 14.311449394616199%;
631 | }
632 | .row-fluid .span1 {
633 | width: 5.801104972375691%;
634 | *width: 5.747913483013988%;
635 | }
636 | .row-fluid .offset12 {
637 | margin-left: 105.52486187845304%;
638 | *margin-left: 105.41847889972962%;
639 | }
640 | .row-fluid .offset12:first-child {
641 | margin-left: 102.76243093922652%;
642 | *margin-left: 102.6560479605031%;
643 | }
644 | .row-fluid .offset11 {
645 | margin-left: 96.96132596685082%;
646 | *margin-left: 96.8549429881274%;
647 | }
648 | .row-fluid .offset11:first-child {
649 | margin-left: 94.1988950276243%;
650 | *margin-left: 94.09251204890089%;
651 | }
652 | .row-fluid .offset10 {
653 | margin-left: 88.39779005524862%;
654 | *margin-left: 88.2914070765252%;
655 | }
656 | .row-fluid .offset10:first-child {
657 | margin-left: 85.6353591160221%;
658 | *margin-left: 85.52897613729868%;
659 | }
660 | .row-fluid .offset9 {
661 | margin-left: 79.8342541436464%;
662 | *margin-left: 79.72787116492299%;
663 | }
664 | .row-fluid .offset9:first-child {
665 | margin-left: 77.07182320441989%;
666 | *margin-left: 76.96544022569647%;
667 | }
668 | .row-fluid .offset8 {
669 | margin-left: 71.2707182320442%;
670 | *margin-left: 71.16433525332079%;
671 | }
672 | .row-fluid .offset8:first-child {
673 | margin-left: 68.50828729281768%;
674 | *margin-left: 68.40190431409427%;
675 | }
676 | .row-fluid .offset7 {
677 | margin-left: 62.70718232044199%;
678 | *margin-left: 62.600799341718584%;
679 | }
680 | .row-fluid .offset7:first-child {
681 | margin-left: 59.94475138121547%;
682 | *margin-left: 59.838368402492065%;
683 | }
684 | .row-fluid .offset6 {
685 | margin-left: 54.14364640883978%;
686 | *margin-left: 54.037263430116376%;
687 | }
688 | .row-fluid .offset6:first-child {
689 | margin-left: 51.38121546961326%;
690 | *margin-left: 51.27483249088986%;
691 | }
692 | .row-fluid .offset5 {
693 | margin-left: 45.58011049723757%;
694 | *margin-left: 45.47372751851417%;
695 | }
696 | .row-fluid .offset5:first-child {
697 | margin-left: 42.81767955801105%;
698 | *margin-left: 42.71129657928765%;
699 | }
700 | .row-fluid .offset4 {
701 | margin-left: 37.01657458563536%;
702 | *margin-left: 36.91019160691196%;
703 | }
704 | .row-fluid .offset4:first-child {
705 | margin-left: 34.25414364640884%;
706 | *margin-left: 34.14776066768544%;
707 | }
708 | .row-fluid .offset3 {
709 | margin-left: 28.45303867403315%;
710 | *margin-left: 28.346655695309746%;
711 | }
712 | .row-fluid .offset3:first-child {
713 | margin-left: 25.69060773480663%;
714 | *margin-left: 25.584224756083227%;
715 | }
716 | .row-fluid .offset2 {
717 | margin-left: 19.88950276243094%;
718 | *margin-left: 19.783119783707537%;
719 | }
720 | .row-fluid .offset2:first-child {
721 | margin-left: 17.12707182320442%;
722 | *margin-left: 17.02068884448102%;
723 | }
724 | .row-fluid .offset1 {
725 | margin-left: 11.32596685082873%;
726 | *margin-left: 11.219583872105325%;
727 | }
728 | .row-fluid .offset1:first-child {
729 | margin-left: 8.56353591160221%;
730 | *margin-left: 8.457152932878806%;
731 | }
732 | input,
733 | textarea,
734 | .uneditable-input {
735 | margin-left: 0;
736 | }
737 | .controls-row [class*="span"] + [class*="span"] {
738 | margin-left: 20px;
739 | }
740 | input.span12,
741 | textarea.span12,
742 | .uneditable-input.span12 {
743 | width: 710px;
744 | }
745 | input.span11,
746 | textarea.span11,
747 | .uneditable-input.span11 {
748 | width: 648px;
749 | }
750 | input.span10,
751 | textarea.span10,
752 | .uneditable-input.span10 {
753 | width: 586px;
754 | }
755 | input.span9,
756 | textarea.span9,
757 | .uneditable-input.span9 {
758 | width: 524px;
759 | }
760 | input.span8,
761 | textarea.span8,
762 | .uneditable-input.span8 {
763 | width: 462px;
764 | }
765 | input.span7,
766 | textarea.span7,
767 | .uneditable-input.span7 {
768 | width: 400px;
769 | }
770 | input.span6,
771 | textarea.span6,
772 | .uneditable-input.span6 {
773 | width: 338px;
774 | }
775 | input.span5,
776 | textarea.span5,
777 | .uneditable-input.span5 {
778 | width: 276px;
779 | }
780 | input.span4,
781 | textarea.span4,
782 | .uneditable-input.span4 {
783 | width: 214px;
784 | }
785 | input.span3,
786 | textarea.span3,
787 | .uneditable-input.span3 {
788 | width: 152px;
789 | }
790 | input.span2,
791 | textarea.span2,
792 | .uneditable-input.span2 {
793 | width: 90px;
794 | }
795 | input.span1,
796 | textarea.span1,
797 | .uneditable-input.span1 {
798 | width: 28px;
799 | }
800 | }
801 |
802 | @media (max-width: 767px) {
803 | body {
804 | padding-right: 20px;
805 | padding-left: 20px;
806 | }
807 | .navbar-fixed-top,
808 | .navbar-fixed-bottom,
809 | .navbar-static-top {
810 | margin-right: -20px;
811 | margin-left: -20px;
812 | }
813 | .container-fluid {
814 | padding: 0;
815 | }
816 | .dl-horizontal dt {
817 | float: none;
818 | width: auto;
819 | clear: none;
820 | text-align: left;
821 | }
822 | .dl-horizontal dd {
823 | margin-left: 0;
824 | }
825 | .container {
826 | width: auto;
827 | }
828 | .row-fluid {
829 | width: 100%;
830 | }
831 | .row,
832 | .thumbnails {
833 | margin-left: 0;
834 | }
835 | .thumbnails > li {
836 | float: none;
837 | margin-left: 0;
838 | }
839 | [class*="span"],
840 | .uneditable-input[class*="span"],
841 | .row-fluid [class*="span"] {
842 | display: block;
843 | float: none;
844 | width: 100%;
845 | margin-left: 0;
846 | -webkit-box-sizing: border-box;
847 | -moz-box-sizing: border-box;
848 | box-sizing: border-box;
849 | }
850 | .span12,
851 | .row-fluid .span12 {
852 | width: 100%;
853 | -webkit-box-sizing: border-box;
854 | -moz-box-sizing: border-box;
855 | box-sizing: border-box;
856 | }
857 | .row-fluid [class*="offset"]:first-child {
858 | margin-left: 0;
859 | }
860 | .input-large,
861 | .input-xlarge,
862 | .input-xxlarge,
863 | input[class*="span"],
864 | select[class*="span"],
865 | textarea[class*="span"],
866 | .uneditable-input {
867 | display: block;
868 | width: 100%;
869 | min-height: 30px;
870 | -webkit-box-sizing: border-box;
871 | -moz-box-sizing: border-box;
872 | box-sizing: border-box;
873 | }
874 | .input-prepend input,
875 | .input-append input,
876 | .input-prepend input[class*="span"],
877 | .input-append input[class*="span"] {
878 | display: inline-block;
879 | width: auto;
880 | }
881 | .controls-row [class*="span"] + [class*="span"] {
882 | margin-left: 0;
883 | }
884 | .modal {
885 | position: fixed;
886 | top: 20px;
887 | right: 20px;
888 | left: 20px;
889 | width: auto;
890 | margin: 0;
891 | }
892 | .modal.fade {
893 | top: -100px;
894 | }
895 | .modal.fade.in {
896 | top: 20px;
897 | }
898 | }
899 |
900 | @media (max-width: 480px) {
901 | .nav-collapse {
902 | -webkit-transform: translate3d(0, 0, 0);
903 | }
904 | .page-header h1 small {
905 | display: block;
906 | line-height: 20px;
907 | }
908 | input[type="checkbox"],
909 | input[type="radio"] {
910 | border: 1px solid #ccc;
911 | }
912 | .form-horizontal .control-label {
913 | float: none;
914 | width: auto;
915 | padding-top: 0;
916 | text-align: left;
917 | }
918 | .form-horizontal .controls {
919 | margin-left: 0;
920 | }
921 | .form-horizontal .control-list {
922 | padding-top: 0;
923 | }
924 | .form-horizontal .form-actions {
925 | padding-right: 10px;
926 | padding-left: 10px;
927 | }
928 | .media .pull-left,
929 | .media .pull-right {
930 | display: block;
931 | float: none;
932 | margin-bottom: 10px;
933 | }
934 | .media-object {
935 | margin-right: 0;
936 | margin-left: 0;
937 | }
938 | .modal {
939 | top: 10px;
940 | right: 10px;
941 | left: 10px;
942 | }
943 | .modal-header .close {
944 | padding: 10px;
945 | margin: -10px;
946 | }
947 | .carousel-caption {
948 | position: static;
949 | }
950 | }
951 |
952 | @media (max-width: 979px) {
953 | body {
954 | padding-top: 0;
955 | }
956 | .navbar-fixed-top,
957 | .navbar-fixed-bottom {
958 | position: static;
959 | }
960 | .navbar-fixed-top {
961 | margin-bottom: 20px;
962 | }
963 | .navbar-fixed-bottom {
964 | margin-top: 20px;
965 | }
966 | .navbar-fixed-top .navbar-inner,
967 | .navbar-fixed-bottom .navbar-inner {
968 | padding: 5px;
969 | }
970 | .navbar .container {
971 | width: auto;
972 | padding: 0;
973 | }
974 | .navbar .brand {
975 | padding-right: 10px;
976 | padding-left: 10px;
977 | margin: 0 0 0 -5px;
978 | }
979 | .nav-collapse {
980 | clear: both;
981 | }
982 | .nav-collapse .nav {
983 | float: none;
984 | margin: 0 0 10px;
985 | }
986 | .nav-collapse .nav > li {
987 | float: none;
988 | }
989 | .nav-collapse .nav > li > a {
990 | margin-bottom: 2px;
991 | }
992 | .nav-collapse .nav > .divider-vertical {
993 | display: none;
994 | }
995 | .nav-collapse .nav .nav-header {
996 | color: #777777;
997 | text-shadow: none;
998 | }
999 | .nav-collapse .nav > li > a,
1000 | .nav-collapse .dropdown-menu a {
1001 | padding: 9px 15px;
1002 | font-weight: bold;
1003 | color: #777777;
1004 | -webkit-border-radius: 3px;
1005 | -moz-border-radius: 3px;
1006 | border-radius: 3px;
1007 | }
1008 | .nav-collapse .btn {
1009 | padding: 4px 10px 4px;
1010 | font-weight: normal;
1011 | -webkit-border-radius: 4px;
1012 | -moz-border-radius: 4px;
1013 | border-radius: 4px;
1014 | }
1015 | .nav-collapse .dropdown-menu li + li a {
1016 | margin-bottom: 2px;
1017 | }
1018 | .nav-collapse .nav > li > a:hover,
1019 | .nav-collapse .nav > li > a:focus,
1020 | .nav-collapse .dropdown-menu a:hover,
1021 | .nav-collapse .dropdown-menu a:focus {
1022 | background-color: #f2f2f2;
1023 | }
1024 | .navbar-inverse .nav-collapse .nav > li > a,
1025 | .navbar-inverse .nav-collapse .dropdown-menu a {
1026 | color: #999999;
1027 | }
1028 | .navbar-inverse .nav-collapse .nav > li > a:hover,
1029 | .navbar-inverse .nav-collapse .nav > li > a:focus,
1030 | .navbar-inverse .nav-collapse .dropdown-menu a:hover,
1031 | .navbar-inverse .nav-collapse .dropdown-menu a:focus {
1032 | background-color: #111111;
1033 | }
1034 | .nav-collapse.in .btn-group {
1035 | padding: 0;
1036 | margin-top: 5px;
1037 | }
1038 | .nav-collapse .dropdown-menu {
1039 | position: static;
1040 | top: auto;
1041 | left: auto;
1042 | display: none;
1043 | float: none;
1044 | max-width: none;
1045 | padding: 0;
1046 | margin: 0 15px;
1047 | background-color: transparent;
1048 | border: none;
1049 | -webkit-border-radius: 0;
1050 | -moz-border-radius: 0;
1051 | border-radius: 0;
1052 | -webkit-box-shadow: none;
1053 | -moz-box-shadow: none;
1054 | box-shadow: none;
1055 | }
1056 | .nav-collapse .open > .dropdown-menu {
1057 | display: block;
1058 | }
1059 | .nav-collapse .dropdown-menu:before,
1060 | .nav-collapse .dropdown-menu:after {
1061 | display: none;
1062 | }
1063 | .nav-collapse .dropdown-menu .divider {
1064 | display: none;
1065 | }
1066 | .nav-collapse .nav > li > .dropdown-menu:before,
1067 | .nav-collapse .nav > li > .dropdown-menu:after {
1068 | display: none;
1069 | }
1070 | .nav-collapse .navbar-form,
1071 | .nav-collapse .navbar-search {
1072 | float: none;
1073 | padding: 10px 15px;
1074 | margin: 10px 0;
1075 | border-top: 1px solid #f2f2f2;
1076 | border-bottom: 1px solid #f2f2f2;
1077 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1078 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1079 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1080 | }
1081 | .navbar-inverse .nav-collapse .navbar-form,
1082 | .navbar-inverse .nav-collapse .navbar-search {
1083 | border-top-color: #111111;
1084 | border-bottom-color: #111111;
1085 | }
1086 | .navbar .nav-collapse .nav.pull-right {
1087 | float: none;
1088 | margin-left: 0;
1089 | }
1090 | .nav-collapse,
1091 | .nav-collapse.collapse {
1092 | height: 0;
1093 | overflow: hidden;
1094 | }
1095 | .navbar .btn-navbar {
1096 | display: block;
1097 | }
1098 | .navbar-static .navbar-inner {
1099 | padding-right: 10px;
1100 | padding-left: 10px;
1101 | }
1102 | }
1103 |
1104 | @media (min-width: 980px) {
1105 | .nav-collapse.collapse {
1106 | height: auto !important;
1107 | overflow: visible !important;
1108 | }
1109 | }
1110 |
--------------------------------------------------------------------------------
/app/public/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------
/app/public/css/style.css:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chris-langager/Angular-Express-Train-Seed/d67b9e8e63e95ad14be6eb31d7ea9a7d7a82714d/app/public/favicon.ico
--------------------------------------------------------------------------------
/app/public/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chris-langager/Angular-Express-Train-Seed/d67b9e8e63e95ad14be6eb31d7ea9a7d7a82714d/app/public/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/app/public/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chris-langager/Angular-Express-Train-Seed/d67b9e8e63e95ad14be6eb31d7ea9a7d7a82714d/app/public/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Angular Express Train
5 |
6 |
7 |
8 |
9 |
30 |
31 |
32 |
33 |
34 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/public/js/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Declare app level module which depends on filters, and services
4 | angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives'])
5 | .config(['$routeProvider', '$locationProvider', '$httpProvider', function ($routeProvider, $locationProvider, $httpProvider) {
6 |
7 | //The routes that our angular app will handle
8 | $routeProvider
9 | .when('/', { templateUrl: '/partials/index.html', controller: IndexCtrl })
10 | .when('/login', { templateUrl: '/partials/login.html'})
11 | .when('/todos', { templateUrl: '/partials/todos.html', controller: TodosCtrl })
12 | .otherwise({ templateUrl: '/partials/404.html' });
13 |
14 | //gets rid of the # in urls
15 | $locationProvider.html5Mode(true);
16 |
17 | /*
18 | Set up an interceptor to watch for 401 errors.
19 | The server, rather than redirect to a login page (or whatever), just returns a 401 error
20 | if it receives a request that should have a user session going. Angular catches the error below
21 | and says what happens - in this case, we just redirect to a login page. You can get a little more
22 | complex with this strategy, such as queueing up failed requests and re-trying them once the user logs in.
23 | Read all about it here: http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application
24 | */
25 | var interceptor = ['$q', '$location', '$rootScope', function ($q, $location, $rootScope) {
26 | function success(response) {
27 | return response;
28 | }
29 |
30 | function error(response) {
31 | var status = response.status;
32 | if (status == 401) {
33 | $rootScope.redirect = $location.url(); // save the current url so we can redirect the user back
34 | $rootScope.user = {}
35 | $location.path('/login');
36 | }
37 | return $q.reject(response);
38 | }
39 |
40 | return function (promise) {
41 | return promise.then(success, error);
42 | }
43 | }];
44 | $httpProvider.responseInterceptors.push(interceptor);
45 |
46 | }])
47 | .run(function ($rootScope, $http, $location) {
48 |
49 | //global object representing the user who is logged in
50 | $rootScope.user = {};
51 |
52 | //as the app spins up, let's check to see if we have an active session with the server
53 | $http.get('/user')
54 | .success(function (data) {
55 | $rootScope.user.username = data.username;
56 | })
57 | .error(function (data) {
58 | });
59 |
60 | //global function for logging out a user
61 | $rootScope.logout = function () {
62 | $rootScope.user = {}
63 | $http.post('user/logout', {});
64 | $location.path('/');
65 | }
66 |
67 | });
--------------------------------------------------------------------------------
/app/public/js/controllers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | function IndexCtrl($scope, $http) {
5 | }
6 |
7 | function LoginCtrl($scope, $http, $rootScope, $location) {
8 | $scope.user = {};
9 | $scope.statusMessage = '';
10 |
11 | //figure out where we should redirect to once the user has logged in.
12 | if (!$rootScope.redirect || $rootScope.redirect == '/login') {
13 | $rootScope.redirect = '/todos';
14 | }
15 |
16 | $scope.submit = function (user) {
17 | $http.post('/user/login', $scope.user)
18 | .success(function (data) {
19 | $rootScope.user.username = $scope.user.username;
20 | $location.path($rootScope.redirect);
21 | })
22 | .error(function (data, status, headers, config) {
23 | $scope.statusMessage = data;
24 | });
25 | }
26 | }
27 |
28 | function RegisterCtrl($scope, $http, $rootScope, $location) {
29 | $scope.user = {};
30 | $scope.statusMessage = '';
31 |
32 | $scope.submit = function (user) {
33 | $http.post('/user/register', $scope.user)
34 | .success(function (data) {
35 | $rootScope.user.username = $scope.user.username;
36 | $location.path('/todos');
37 | })
38 | .error(function (data, status, headers, config) {
39 | $scope.statusMessage = data;
40 | });
41 | }
42 | }
43 |
44 | function TodosCtrl($scope, $http, Todo) {
45 |
46 | //get the todos from server
47 | getTodosFromServer()
48 |
49 | $scope.newTodo = {};
50 |
51 | //function to create a new Todo object
52 | $scope.createTodo = function (todo) {
53 | if ($scope.newTodoForm.$invalid) {
54 | return;
55 | }
56 | Todo.save({}, $scope.newTodo,
57 | function (data) {
58 | $scope.todos.push(data);
59 | $scope.statusMessage = '';
60 | $scope.newTodo = {};
61 |
62 | },
63 | function (data, status, headers, config) {
64 | $scope.statusMessage = data;
65 | });
66 | }
67 |
68 | //we'll call this function when the checkbox of a todo is checked
69 | $scope.markComplete = function (todo) {
70 | todo.$save({id: todo._id});
71 | }
72 |
73 | //remove complete todos
74 | $scope.removeComplete = function () {
75 | $scope.todos.forEach(function (todo) {
76 | if (todo.complete) {
77 | todo.$delete({id: todo._id}, function(){ //delete on server
78 | $scope.todos.splice( $scope.todos.indexOf(todo), 1 ); //remove from client
79 | });
80 | }
81 | })
82 | }
83 |
84 | function getTodosFromServer() {
85 | Todo.query(function (data) {
86 | $scope.todos = data;
87 | });
88 | }
89 |
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/app/public/js/directives.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* Directives */
4 |
5 |
6 | angular.module('myApp.directives', []).
7 | directive('appVersion', ['version', function(version) {
8 | return function(scope, elm, attrs) {
9 | elm.text(version);
10 | };
11 | }])
12 | .directive('dropdown', function () {
13 | return function (scope, elm, attrs) {
14 | $(elm).dropdown();
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/app/public/js/filters.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* Filters */
4 |
5 | angular.module('myApp.filters', []).
6 | filter('interpolate', ['version', function(version) {
7 | return function(text) {
8 | return String(text).replace(/\%VERSION\%/mg, version);
9 | }
10 | }]);
11 |
--------------------------------------------------------------------------------
/app/public/js/lib/angular/angular-cookies.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.0
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {
7 | 'use strict';
8 |
9 | /**
10 | * @ngdoc overview
11 | * @name ngCookies
12 | */
13 |
14 |
15 | angular.module('ngCookies', ['ng']).
16 | /**
17 | * @ngdoc object
18 | * @name ngCookies.$cookies
19 | * @requires $browser
20 | *
21 | * @description
22 | * Provides read/write access to browser's cookies.
23 | *
24 | * Only a simple Object is exposed and by adding or removing properties to/from
25 | * this object, new cookies are created/deleted at the end of current $eval.
26 | *
27 | * @example
28 | */
29 | factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
30 | var cookies = {},
31 | lastCookies = {},
32 | lastBrowserCookies,
33 | runEval = false,
34 | copy = angular.copy,
35 | isUndefined = angular.isUndefined;
36 |
37 | //creates a poller fn that copies all cookies from the $browser to service & inits the service
38 | $browser.addPollFn(function() {
39 | var currentCookies = $browser.cookies();
40 | if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
41 | lastBrowserCookies = currentCookies;
42 | copy(currentCookies, lastCookies);
43 | copy(currentCookies, cookies);
44 | if (runEval) $rootScope.$apply();
45 | }
46 | })();
47 |
48 | runEval = true;
49 |
50 | //at the end of each eval, push cookies
51 | //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
52 | // strings or browser refuses to store some cookies, we update the model in the push fn.
53 | $rootScope.$watch(push);
54 |
55 | return cookies;
56 |
57 |
58 | /**
59 | * Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
60 | */
61 | function push() {
62 | var name,
63 | value,
64 | browserCookies,
65 | updated;
66 |
67 | //delete any cookies deleted in $cookies
68 | for (name in lastCookies) {
69 | if (isUndefined(cookies[name])) {
70 | $browser.cookies(name, undefined);
71 | }
72 | }
73 |
74 | //update all cookies updated in $cookies
75 | for(name in cookies) {
76 | value = cookies[name];
77 | if (!angular.isString(value)) {
78 | if (angular.isDefined(lastCookies[name])) {
79 | cookies[name] = lastCookies[name];
80 | } else {
81 | delete cookies[name];
82 | }
83 | } else if (value !== lastCookies[name]) {
84 | $browser.cookies(name, value);
85 | updated = true;
86 | }
87 | }
88 |
89 | //verify what was actually stored
90 | if (updated){
91 | updated = false;
92 | browserCookies = $browser.cookies();
93 |
94 | for (name in cookies) {
95 | if (cookies[name] !== browserCookies[name]) {
96 | //delete or reset all cookies that the browser dropped from $cookies
97 | if (isUndefined(browserCookies[name])) {
98 | delete cookies[name];
99 | } else {
100 | cookies[name] = browserCookies[name];
101 | }
102 | updated = true;
103 | }
104 | }
105 | }
106 | }
107 | }]).
108 |
109 |
110 | /**
111 | * @ngdoc object
112 | * @name ngCookies.$cookieStore
113 | * @requires $cookies
114 | *
115 | * @description
116 | * Provides a key-value (string-object) storage, that is backed by session cookies.
117 | * Objects put or retrieved from this storage are automatically serialized or
118 | * deserialized by angular's toJson/fromJson.
119 | * @example
120 | */
121 | factory('$cookieStore', ['$cookies', function($cookies) {
122 |
123 | return {
124 | /**
125 | * @ngdoc method
126 | * @name ngCookies.$cookieStore#get
127 | * @methodOf ngCookies.$cookieStore
128 | *
129 | * @description
130 | * Returns the value of given cookie key
131 | *
132 | * @param {string} key Id to use for lookup.
133 | * @returns {Object} Deserialized cookie value.
134 | */
135 | get: function(key) {
136 | return angular.fromJson($cookies[key]);
137 | },
138 |
139 | /**
140 | * @ngdoc method
141 | * @name ngCookies.$cookieStore#put
142 | * @methodOf ngCookies.$cookieStore
143 | *
144 | * @description
145 | * Sets a value for given cookie key
146 | *
147 | * @param {string} key Id for the `value`.
148 | * @param {Object} value Value to be stored.
149 | */
150 | put: function(key, value) {
151 | $cookies[key] = angular.toJson(value);
152 | },
153 |
154 | /**
155 | * @ngdoc method
156 | * @name ngCookies.$cookieStore#remove
157 | * @methodOf ngCookies.$cookieStore
158 | *
159 | * @description
160 | * Remove given cookie
161 | *
162 | * @param {string} key Id of the key-value pair to delete.
163 | */
164 | remove: function(key) {
165 | delete $cookies[key];
166 | }
167 | };
168 |
169 | }]);
170 |
171 | })(window, window.angular);
172 |
--------------------------------------------------------------------------------
/app/public/js/lib/angular/angular-cookies.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.0
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,c){var b={},g={},h,i=!1,j=f.copy,k=f.isUndefined;c.addPollFn(function(){var a=c.cookies();h!=a&&(h=a,j(a,g),j(a,b),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(b[a])&&c.cookies(a,l);for(a in b)e=b[a],f.isString(e)?e!==g[a]&&(c.cookies(a,e),d=!0):f.isDefined(g[a])?b[a]=g[a]:delete b[a];if(d)for(a in e=c.cookies(),b)b[a]!==e[a]&&(k(e[a])?delete b[a]:b[a]=e[a])});return b}]).factory("$cookieStore",
7 | ["$cookies",function(d){return{get:function(c){return f.fromJson(d[c])},put:function(c,b){d[c]=f.toJson(b)},remove:function(c){delete d[c]}}}])})(window,window.angular);
8 |
--------------------------------------------------------------------------------
/app/public/js/lib/angular/angular-loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.0
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 |
7 | (
8 |
9 | /**
10 | * @ngdoc interface
11 | * @name angular.Module
12 | * @description
13 | *
14 | * Interface for configuring angular {@link angular.module modules}.
15 | */
16 |
17 | function setupModuleLoader(window) {
18 |
19 | function ensure(obj, name, factory) {
20 | return obj[name] || (obj[name] = factory());
21 | }
22 |
23 | return ensure(ensure(window, 'angular', Object), 'module', function() {
24 | /** @type {Object.} */
25 | var modules = {};
26 |
27 | /**
28 | * @ngdoc function
29 | * @name angular.module
30 | * @description
31 | *
32 | * The `angular.module` is a global place for creating and registering Angular modules. All
33 | * modules (angular core or 3rd party) that should be available to an application must be
34 | * registered using this mechanism.
35 | *
36 | *
37 | * # Module
38 | *
39 | * A module is a collocation of services, directives, filters, and configure information. Module
40 | * is used to configure the {@link AUTO.$injector $injector}.
41 | *
42 | *
43 | * // Create a new module
44 | * var myModule = angular.module('myModule', []);
45 | *
46 | * // register a new service
47 | * myModule.value('appName', 'MyCoolApp');
48 | *
49 | * // configure existing services inside initialization blocks.
50 | * myModule.config(function($locationProvider) {
51 | 'use strict';
52 | * // Configure existing providers
53 | * $locationProvider.hashPrefix('!');
54 | * });
55 | *
56 | *
57 | * Then you can create an injector and load your modules like this:
58 | *
59 | *
60 | * var injector = angular.injector(['ng', 'MyModule'])
61 | *
62 | *
63 | * However it's more likely that you'll just use
64 | * {@link ng.directive:ngApp ngApp} or
65 | * {@link angular.bootstrap} to simplify this process for you.
66 | *
67 | * @param {!string} name The name of the module to create or retrieve.
68 | * @param {Array.=} requires If specified then new module is being created. If unspecified then the
69 | * the module is being retrieved for further configuration.
70 | * @param {Function} configFn Option configuration function for the module. Same as
71 | * {@link angular.Module#config Module#config()}.
72 | * @returns {module} new module with the {@link angular.Module} api.
73 | */
74 | return function module(name, requires, configFn) {
75 | if (requires && modules.hasOwnProperty(name)) {
76 | modules[name] = null;
77 | }
78 | return ensure(modules, name, function() {
79 | if (!requires) {
80 | throw Error('No module: ' + name);
81 | }
82 |
83 | /** @type {!Array.>} */
84 | var invokeQueue = [];
85 |
86 | /** @type {!Array.} */
87 | var runBlocks = [];
88 |
89 | var config = invokeLater('$injector', 'invoke');
90 |
91 | /** @type {angular.Module} */
92 | var moduleInstance = {
93 | // Private state
94 | _invokeQueue: invokeQueue,
95 | _runBlocks: runBlocks,
96 |
97 | /**
98 | * @ngdoc property
99 | * @name angular.Module#requires
100 | * @propertyOf angular.Module
101 | * @returns {Array.} List of module names which must be loaded before this module.
102 | * @description
103 | * Holds the list of modules which the injector will load before the current module is loaded.
104 | */
105 | requires: requires,
106 |
107 | /**
108 | * @ngdoc property
109 | * @name angular.Module#name
110 | * @propertyOf angular.Module
111 | * @returns {string} Name of the module.
112 | * @description
113 | */
114 | name: name,
115 |
116 |
117 | /**
118 | * @ngdoc method
119 | * @name angular.Module#provider
120 | * @methodOf angular.Module
121 | * @param {string} name service name
122 | * @param {Function} providerType Construction function for creating new instance of the service.
123 | * @description
124 | * See {@link AUTO.$provide#provider $provide.provider()}.
125 | */
126 | provider: invokeLater('$provide', 'provider'),
127 |
128 | /**
129 | * @ngdoc method
130 | * @name angular.Module#factory
131 | * @methodOf angular.Module
132 | * @param {string} name service name
133 | * @param {Function} providerFunction Function for creating new instance of the service.
134 | * @description
135 | * See {@link AUTO.$provide#factory $provide.factory()}.
136 | */
137 | factory: invokeLater('$provide', 'factory'),
138 |
139 | /**
140 | * @ngdoc method
141 | * @name angular.Module#service
142 | * @methodOf angular.Module
143 | * @param {string} name service name
144 | * @param {Function} constructor A constructor function that will be instantiated.
145 | * @description
146 | * See {@link AUTO.$provide#service $provide.service()}.
147 | */
148 | service: invokeLater('$provide', 'service'),
149 |
150 | /**
151 | * @ngdoc method
152 | * @name angular.Module#value
153 | * @methodOf angular.Module
154 | * @param {string} name service name
155 | * @param {*} object Service instance object.
156 | * @description
157 | * See {@link AUTO.$provide#value $provide.value()}.
158 | */
159 | value: invokeLater('$provide', 'value'),
160 |
161 | /**
162 | * @ngdoc method
163 | * @name angular.Module#constant
164 | * @methodOf angular.Module
165 | * @param {string} name constant name
166 | * @param {*} object Constant value.
167 | * @description
168 | * Because the constant are fixed, they get applied before other provide methods.
169 | * See {@link AUTO.$provide#constant $provide.constant()}.
170 | */
171 | constant: invokeLater('$provide', 'constant', 'unshift'),
172 |
173 | /**
174 | * @ngdoc method
175 | * @name angular.Module#filter
176 | * @methodOf angular.Module
177 | * @param {string} name Filter name.
178 | * @param {Function} filterFactory Factory function for creating new instance of filter.
179 | * @description
180 | * See {@link ng.$filterProvider#register $filterProvider.register()}.
181 | */
182 | filter: invokeLater('$filterProvider', 'register'),
183 |
184 | /**
185 | * @ngdoc method
186 | * @name angular.Module#controller
187 | * @methodOf angular.Module
188 | * @param {string} name Controller name.
189 | * @param {Function} constructor Controller constructor function.
190 | * @description
191 | * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
192 | */
193 | controller: invokeLater('$controllerProvider', 'register'),
194 |
195 | /**
196 | * @ngdoc method
197 | * @name angular.Module#directive
198 | * @methodOf angular.Module
199 | * @param {string} name directive name
200 | * @param {Function} directiveFactory Factory function for creating new instance of
201 | * directives.
202 | * @description
203 | * See {@link ng.$compileProvider.directive $compileProvider.directive()}.
204 | */
205 | directive: invokeLater('$compileProvider', 'directive'),
206 |
207 | /**
208 | * @ngdoc method
209 | * @name angular.Module#config
210 | * @methodOf angular.Module
211 | * @param {Function} configFn Execute this function on module load. Useful for service
212 | * configuration.
213 | * @description
214 | * Use this method to register work which needs to be performed on module loading.
215 | */
216 | config: config,
217 |
218 | /**
219 | * @ngdoc method
220 | * @name angular.Module#run
221 | * @methodOf angular.Module
222 | * @param {Function} initializationFn Execute this function after injector creation.
223 | * Useful for application initialization.
224 | * @description
225 | * Use this method to register work which needs to be performed when the injector with
226 | * with the current module is finished loading.
227 | */
228 | run: function(block) {
229 | runBlocks.push(block);
230 | return this;
231 | }
232 | };
233 |
234 | if (configFn) {
235 | config(configFn);
236 | }
237 |
238 | return moduleInstance;
239 |
240 | /**
241 | * @param {string} provider
242 | * @param {string} method
243 | * @param {String=} insertMethod
244 | * @returns {angular.Module}
245 | */
246 | function invokeLater(provider, method, insertMethod) {
247 | return function() {
248 | invokeQueue[insertMethod || 'push']([provider, method, arguments]);
249 | return moduleInstance;
250 | }
251 | }
252 | });
253 | };
254 | });
255 |
256 | }
257 | )(window);
258 |
259 | /**
260 | * Closure compiler type information
261 | *
262 | * @typedef { {
263 | * requires: !Array.,
264 | * invokeQueue: !Array.>,
265 | *
266 | * service: function(string, Function):angular.Module,
267 | * factory: function(string, Function):angular.Module,
268 | * value: function(string, *):angular.Module,
269 | *
270 | * filter: function(string, Function):angular.Module,
271 | *
272 | * init: function(Function):angular.Module
273 | * } }
274 | */
275 | angular.Module;
276 |
277 |
--------------------------------------------------------------------------------
/app/public/js/lib/angular/angular-loader.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.0
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(i){'use strict';function d(c,b,e){return c[b]||(c[b]=e())}return d(d(i,"angular",Object),"module",function(){var c={};return function(b,e,f){e&&c.hasOwnProperty(b)&&(c[b]=null);return d(c,b,function(){function a(a,b,d){return function(){c[d||"push"]([a,b,arguments]);return g}}if(!e)throw Error("No module: "+b);var c=[],d=[],h=a("$injector","invoke"),g={_invokeQueue:c,_runBlocks:d,requires:e,name:b,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),
7 | value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:h,run:function(a){d.push(a);return this}};f&&h(f);return g})}})})(window);
8 |
--------------------------------------------------------------------------------
/app/public/js/lib/angular/angular-resource.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.0
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {
7 | 'use strict';
8 |
9 | /**
10 | * @ngdoc overview
11 | * @name ngResource
12 | * @description
13 | */
14 |
15 | /**
16 | * @ngdoc object
17 | * @name ngResource.$resource
18 | * @requires $http
19 | *
20 | * @description
21 | * A factory which creates a resource object that lets you interact with
22 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
23 | *
24 | * The returned resource object has action methods which provide high-level behaviors without
25 | * the need to interact with the low level {@link ng.$http $http} service.
26 | *
27 | * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
28 | * `/user/:username`.
29 | *
30 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
31 | * `actions` methods.
32 | *
33 | * Each key value in the parameter object is first bound to url template if present and then any
34 | * excess keys are appended to the url search query after the `?`.
35 | *
36 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
37 | * URL `/path/greet?salutation=Hello`.
38 | *
39 | * If the parameter value is prefixed with `@` then the value of that parameter is extracted from
40 | * the data object (useful for non-GET operations).
41 | *
42 | * @param {Object.