├── .gitignore ├── LICENSE.txt ├── Procfile ├── README.md ├── app.js ├── models └── todos.js ├── package.json ├── public ├── images │ └── bg.png ├── javascripts │ ├── backbone │ │ ├── lib │ │ │ ├── backbone-min.js │ │ │ └── underscore-min.js │ │ └── models │ │ │ └── model.js │ ├── do.js │ └── jquery-1.8.3.min.js └── stylesheets │ └── style.css ├── routes ├── index.js └── user.js └── views ├── index.jade └── layout.jade /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | *.sublime-* 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log 17 | node_modules 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ishuah Kariuki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## [DEPRECATED] TodoMVC built with Node.js (Express.js Framework) + MongoDb + socket.io 2 | 3 | Simple Todo Application based on https://github.com/addyosmani/todomvc 4 | 5 | I've deprecated this project because a lot of dependencies are outdated. 6 | Thank you to everyone who contributed/starred/posted issues! 7 | 8 | ### Installation 9 | 1. Clone this repo 10 | 2. run npm install 11 | 3. Ensure MongoDb is running (command: mongodb ) 12 | 4. Run command: node app.js. Check http://localhost:8080 13 | 14 | ### Additional features 15 | 1. Realtime support! Add, edit, delete and changing status all supported. 16 | 17 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | const express = require('express') 6 | , routes = require('./routes') 7 | , user = require('./routes/user') 8 | , http = require('http') 9 | , path = require('path') 10 | , mongoose = require('mongoose') 11 | , io = require('socket.io') 12 | , mongoURI = process.env.MONGOLAB_URI || 'mongodb://localhost/todos' 13 | , Schema = mongoose.Schema 14 | , ObjectID = Schema.ObjectId 15 | , Todo = require('./models/todos.js').init(Schema, mongoose) 16 | ; 17 | 18 | var connectWithRetry = function() { 19 | return mongoose.connect(mongoURI, function(err) { 20 | if (err) { 21 | console.error('Failed to connect to mongo on startup - retrying in 5 sec', err); 22 | setTimeout(connectWithRetry, 5000); 23 | } 24 | }); 25 | }; 26 | 27 | connectWithRetry(); 28 | 29 | mongoose.connection.on('open', function() { 30 | console.log("connected to mongodb"); 31 | }); 32 | 33 | var app = express(); 34 | 35 | app.configure(function() { 36 | app.set('port', process.env.PORT || 8080); 37 | app.set('views', __dirname + '/views'); 38 | app.set('view engine', 'jade'); 39 | app.use(express.favicon()); 40 | app.use(express.logger('dev')); 41 | app.use(express.bodyParser()); 42 | app.use(express.methodOverride()); 43 | app.use(app.router); 44 | app.use(express.static(path.join(__dirname, 'public'))); 45 | }); 46 | 47 | app.configure('development', function() { 48 | app.use(express.errorHandler()); 49 | }); 50 | 51 | var server = http.createServer(app).listen(app.get('port'), function() { 52 | console.log("Express server listening on port " + app.get('port')); 53 | }); 54 | 55 | 56 | var sio = io.listen(server); 57 | //User online user count variable 58 | var users = 0; 59 | 60 | var address_list = new Array(); 61 | 62 | sio.sockets.on('connection', function (socket) { 63 | var address = socket.handshake.address; 64 | 65 | if (address_list[address]) { 66 | var socketid = address_list[address].list; 67 | socketid.push(socket.id); 68 | address_list[address].list = socketid; 69 | } else { 70 | var socketid = new Array(); 71 | socketid.push(socket.id); 72 | address_list[address] = new Array(); 73 | address_list[address].list = socketid; 74 | } 75 | 76 | users = Object.keys(address_list).length; 77 | 78 | socket.emit('count', { count: users }); 79 | socket.broadcast.emit('count', { count: users }); 80 | 81 | /* 82 | handles 'all' namespace 83 | function: list all todos 84 | response: all todos, json format 85 | */ 86 | Todo.find({}, function(err, todos) { 87 | socket.emit('all',todos); 88 | }); 89 | 90 | /* 91 | handles 'add' namespace 92 | function: add a todo 93 | Response: Todo object 94 | */ 95 | socket.on('add', function(data) { 96 | var todo = new Todo({ 97 | title: data.title, 98 | complete: false 99 | }); 100 | 101 | todo.save(function(err) { 102 | if (err) throw err; 103 | socket.emit('added', todo ); 104 | socket.broadcast.emit('added', todo); 105 | }); 106 | }); 107 | 108 | /* 109 | Handles 'delete' namespace 110 | function: delete a todo 111 | response: the delete todo id, json object 112 | */ 113 | socket.on('delete', function(data) { 114 | Todo.findById(data.id, function(err, todo) { 115 | todo.remove(function(err) { 116 | if (err) throw err; 117 | socket.emit('deleted', data ); 118 | socket.broadcast.emit('deleted', data); 119 | }); 120 | }); 121 | }); 122 | 123 | /* 124 | Handles 'edit' namespace 125 | function: edit a todo 126 | response: edited todo, json object 127 | */ 128 | socket.on('edit', function(data) { 129 | Todo.findById(data.id, function(err, todo){ 130 | todo.title = data.title; 131 | todo.save(function(err){ 132 | if(err) throw err; 133 | socket.emit('edited', todo); 134 | socket.broadcast.emit('edited', todo); 135 | }); 136 | }); 137 | }); 138 | 139 | /* 140 | Handles 'changestatus' namespace 141 | function: change the status of a todo 142 | response: the todo that was edited, json object 143 | */ 144 | socket.on('changestatus', function(data) { 145 | Todo.findById(data.id, function(err, todo) { 146 | todo.complete = data.status == 'complete' ? true : false; 147 | todo.save(function(err) { 148 | if(err) throw err; 149 | socket.emit('statuschanged', data ); 150 | socket.broadcast.emit('statuschanged', data); 151 | }); 152 | }); 153 | }); 154 | 155 | /* 156 | Handles 'allchangestatus' namespace 157 | function: change the status of all todos 158 | response: the status, json object 159 | */ 160 | socket.on('allchangestatus', function(data) { 161 | var master_status = data.status == 'complete' ? true : false; 162 | Todo.find({}, function(err, todos) { 163 | for(var i = 0; i < todos.length; i++) { 164 | todos[i].complete = master_status; 165 | todos[i].save(function(err) { 166 | if (err) throw err; 167 | socket.emit('allstatuschanged', data); 168 | socket.broadcast.emit('allstatuschanged', data); 169 | }); 170 | } 171 | }); 172 | }); 173 | 174 | //disconnect state 175 | socket.on('disconnect', function() { 176 | var socketid = address_list[address].list; 177 | delete socketid[socketid.indexOf(socket.id)]; 178 | if(Object.keys(socketid).length == 0) { 179 | delete address_list[address]; 180 | } 181 | users = Object.keys(address_list).length; 182 | socket.emit('count', { count: users }); 183 | socket.broadcast.emit('count', { count: users }); 184 | }); 185 | 186 | }); 187 | 188 | //Our index page 189 | app.get('/', routes.index); 190 | -------------------------------------------------------------------------------- /models/todos.js: -------------------------------------------------------------------------------- 1 | // The model! 2 | function init(Schema, mongoose) { 3 | var TheSchema = new Schema({ 4 | title: String, 5 | complete: Boolean 6 | }); 7 | 8 | return mongoose.model('Todos', TheSchema); 9 | } 10 | 11 | module.exports.init = init; 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app" 7 | }, 8 | "dependencies": { 9 | "express": ">=3.11.0", 10 | "jade": "*", 11 | "mongoose": "*", 12 | "socket.io": "*" 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /public/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ishuah/Nodejs-MongoDb-TodoMVC/44ad89b2b39e95b7093dcaeeb2c6d1e14b6c027e/public/images/bg.png -------------------------------------------------------------------------------- /public/javascripts/backbone/lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.9.9 2 | 3 | // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | (function(){var k=this,y=k.Backbone,h=[],z=h.push,r=h.slice,A=h.splice,g;g="undefined"!==typeof exports?exports:k.Backbone={};g.VERSION="0.9.9";var e=k._;!e&&"undefined"!==typeof require&&(e=require("underscore"));g.$=k.jQuery||k.Zepto||k.ender;g.noConflict=function(){k.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var s=/\s+/,n=function(a,b,c,d){if(!c)return!0;if("object"===typeof c)for(var f in c)a[b].apply(a,[f,c[f]].concat(d));else if(s.test(c)){c=c.split(s);f=0;for(var e=c.length;f< 8 | e;f++)a[b].apply(a,[c[f]].concat(d))}else return!0},t=function(a,b,c){var d,a=-1,f=b.length;switch(c.length){case 0:for(;++a=b);this.root=("/"+this.root+"/").replace(F,"/");b&&this._wantsHashChange&&(this.iframe=g.$('