",
16 | "license": "MIT",
17 | "dependencies": {
18 | "body-parser": "^1.13.2",
19 | "browserify": "^10.2.4",
20 | "dominus": "^5.0.1",
21 | "express": "^4.13.1",
22 | "hoganator": "^1.1.1",
23 | "serve-static": "^1.10.0",
24 | "skyrocket": "^1.2.1",
25 | "socket.io": "^1.3.5",
26 | "socket.io-client": "^1.3.5",
27 | "superagent": "^1.2.0",
28 | "taunus": "^7.1.0",
29 | "taunus-actions": "^1.0.0",
30 | "taunus-express": "^3.0.0",
31 | "todomvc-app-css": "^2.0.1",
32 | "todomvc-common": "^1.0.2"
33 | },
34 | "devDependencies": {
35 | "chokidar-cli": "^0.3.0",
36 | "errorify": "^0.2.4",
37 | "nodemon": "^1.3.7",
38 | "watchify": "^3.2.3"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/readme.markdown:
--------------------------------------------------------------------------------
1 | # Taunus TodoMVC
2 |
3 | > Micro Isomorphic MVC Engine for Node.js
4 |
5 | ## Install dependencies
6 | `npm install`
7 |
8 | ## Run in production mode
9 | `npm start`
10 |
11 | ## Run in development mode
12 | `npm run dev`
13 |
--------------------------------------------------------------------------------
/realtime.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var socket = require('socket.io');
4 | var io;
5 |
6 | function realtime (server) {
7 | if (io) { return io; }
8 | io = realtime.io = addExceptMethod(socket(server));
9 | io.on('connection', connected);
10 | return io;
11 |
12 | function connected (socket) {
13 | socket.on('error', console.error.bind(console));
14 | socket.on('/skyrocket/join', join);
15 | socket.on('/skyrocket/leave', leave);
16 |
17 | function join (data) {
18 | data.rooms.forEach(socket.join, socket);
19 | }
20 | function leave (data) {
21 | data.rooms.forEach(socket.leave, socket);
22 | }
23 | }
24 | }
25 |
26 | function addExceptMethod (io) {
27 | var sockets = io.sockets;
28 | var _broadcast = sockets.adapter.broadcast;
29 |
30 | sockets.constructor.prototype.except = except;
31 | sockets.adapter.broadcast = broadcast;
32 | return io;
33 |
34 | function except (id) {
35 | this.excepts = this.excepts || [];
36 | this.excepts.push(id);
37 | return this;
38 | }
39 | function broadcast (packet, options) {
40 | if (sockets.excepts) {
41 | options.except = sockets.excepts;
42 | }
43 | _broadcast.apply(this, arguments);
44 | delete sockets.excepts;
45 | }
46 | }
47 |
48 | module.exports = realtime;
49 |
--------------------------------------------------------------------------------
/routes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var url = require('url');
3 | var taunus = require('taunus');
4 | var todosService = require('./services/todos');
5 |
6 | function setup(app) {
7 | var socket = require('./realtime');
8 | app.post('/api/todo', require('./controllers/forms/todos/create'));
9 | app.post('/api/todo/:id/edit', require('./controllers/forms/todos/edit'));
10 | app.post('/api/todo/:id/remove', require('./controllers/forms/todos/remove'));
11 | app.post('/api/todo/:id/complete', require('./controllers/forms/todos/complete'));
12 | app.post('/api/todo/clear-completed', require('./controllers/forms/todos/clear-completed'));
13 | app.post('/api/todo/mark-all-completed', require('./controllers/forms/todos/mark-all-completed'));
14 | }
15 |
16 | module.exports = setup;
17 |
--------------------------------------------------------------------------------
/services/todos.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var _todos = {
3 | '1': {
4 | id: 1,
5 | title: 'Taste JavaScript',
6 | completed: true
7 | },
8 | '2': {
9 | id: 2,
10 | title: 'Buy a Unicorn',
11 | completed: false
12 | }
13 | };
14 |
15 | function getAll (callback) {
16 | callback(null, toArray(_todos));
17 | }
18 |
19 | function getOne (id, callback) {
20 | if (!_todos[id]) { return callback(new Error('not_found')); }
21 | callback(null, _todos[id]);
22 | }
23 |
24 | function add (todo, callback) {
25 | todo.id = _todos.length === 0 ? 1 : Object.keys(_todos).reduce(function(a, b) { return Math.max(a, b); }, 0) + 1;
26 | todo.completed = !!todo.completed;
27 | _todos['' + todo.id] = todo;
28 | callback(null, todo);
29 | }
30 |
31 | function edit (todo, callback) {
32 | if (!_todos[todo.id]) { return callback(new Error('not_found')); }
33 | _todos[todo.id].title = todo.title;
34 | callback(null, _todos[todo.id]);
35 | }
36 |
37 | function complete (todo, callback) {
38 | if (!_todos[todo.id]) { return callback(new Error('not_found')); }
39 | _todos[todo.id].completed = todo.completed;
40 | callback(null, _todos[todo.id]);
41 | }
42 |
43 | function remove (id, callback) {
44 | if (!_todos[id]) { return callback(new Error('not_found')); }
45 | var removedTodo = _todos[id];
46 | delete _todos[id];
47 | callback(null, removedTodo);
48 | }
49 |
50 | function clearCompleted (callback) {
51 | var todos = {};
52 | toArray(_todos).filter(function (todo) {
53 | return !todo.completed;
54 | }).forEach(function (todo) {
55 | todos[todo.id] = todo;
56 | });
57 | _todos = todos;
58 | callback(null, toArray(_todos));
59 | }
60 |
61 | function markAllCompleted (callback) {
62 | Object.keys(_todos).forEach(function (key) {
63 | _todos[key].completed = true;
64 | });
65 | callback(null, toArray(_todos));
66 | }
67 |
68 | function toArray (todos) {
69 | return Object.keys(todos).map(function (key) {
70 | return todos[key];
71 | });
72 | }
73 |
74 | module.exports = {
75 | add: add,
76 | edit: edit,
77 | getAll: getAll,
78 | getOne: getOne,
79 | remove: remove,
80 | complete: complete,
81 | clearCompleted: clearCompleted,
82 | markAllCompleted: markAllCompleted
83 | };
84 |
--------------------------------------------------------------------------------
/shared/filter-todos.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function filterTodos (model, currentPath, options) {
4 | options = options || {};
5 |
6 | var completedTodos = [];
7 | var activeTodos = [];
8 |
9 | model.todos.forEach(function (todo) {
10 | if (todo.completed) {
11 | completedTodos.push(todo);
12 | } else {
13 | activeTodos.push(todo);
14 | }
15 | });
16 |
17 | if (options.counts) {
18 | model.todosCount = model.todos.length;
19 | model.activeTodosCount = activeTodos.length;
20 | model.completedTodosCount = completedTodos.length;
21 | }
22 |
23 | if (currentPath === 'completed') {
24 | model.todos = completedTodos;
25 | } else if (currentPath === 'active') {
26 | model.todos = activeTodos;
27 | }
28 | }
29 |
30 | module.exports = filterTodos;
31 |
--------------------------------------------------------------------------------
/views/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Taunus • TodoMVC
7 |
8 |
9 |
10 |
11 |
12 | {{{ partial }}}
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/views/todos/footer.html:
--------------------------------------------------------------------------------
1 | {{ activeTodosCount }} item left
2 |
13 | {{#completedTodosCount}}
14 |
17 | {{/completedTodosCount}}
18 |
--------------------------------------------------------------------------------
/views/todos/index.html:
--------------------------------------------------------------------------------
1 |
2 |
8 | {{#todosCount}}
9 |
10 | {{^completed}}
11 |
15 | {{/completed}}
16 |
19 |
20 |
23 | {{/todosCount}}
24 |
25 |
30 |
--------------------------------------------------------------------------------
/views/todos/list.html:
--------------------------------------------------------------------------------
1 | {{#todos}}
2 |
3 |
4 |
8 |
9 |
12 |
13 |
16 |
17 | {{/todos}}
18 |
--------------------------------------------------------------------------------