Skip to content

Instantly share code, notes, and snippets.

@daffl
Last active May 17, 2016 20:39
Show Gist options
  • Save daffl/6665992 to your computer and use it in GitHub Desktop.
Save daffl/6665992 to your computer and use it in GitHub Desktop.

Revisions

  1. daffl revised this gist Jun 6, 2015. 1 changed file with 54 additions and 49 deletions.
    103 changes: 54 additions & 49 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -23,75 +23,80 @@ <h1>Feathers real-time Todos</h1>
    </form>
    </div>

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="http://rawgit.com/feathersjs/feathers-client/release/dist/feathers.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
    var socket = io();
    var el = $('#todos');
    var socket = io();
    var app = feathers()
    .configure(feathers.socketio(socket));
    var todos = app.service('todos');


    function getElement(todo) {
    return el.find('[data-id="' + todo.id + '"]')
    }

    function addTodo(todo) {
    var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
    '<label><input type="checkbox" name="done">' +
    todo.text +
    '</label><a href="javascript://" class="pull-right delete">' +
    '<span class="glyphicon glyphicon-remove"></span>' +
    '</a></li>';

    el.find('.todos').append(html);
    updateTodo(todo);
    }

    function removeTodo(todo) {
    getElement(todo).remove();
    }

    var app = {
    getElement: function(todo) {
    return el.find('[data-id="' + todo.id + '"]')
    },

    addTodo: function(todo) {
    var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
    '<label><input type="checkbox" name="done">' +
    todo.text +
    '</label><a href="javascript://" class="pull-right delete">' +
    '<span class="glyphicon glyphicon-remove"></span>' +
    '</a></li>';

    el.find('.todos').append(html);
    app.updateTodo(todo);
    },
    removeTodo: function(todo) {
    app.getElement(todo).remove();
    },
    updateTodo: function(todo) {
    var element = app.getElement(todo);
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    element.toggleClass('done', todo.complete);
    checkbox.prop('checked', todo.complete);
    },
    errorHandler: function(error) {
    if(error) {
    alert(error.message);
    }
    }
    };
    function updateTodo(todo) {
    var element = getElement(todo);
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    element.toggleClass('done', todo.complete);
    checkbox.prop('checked', todo.complete);
    }

    el.on('submit', 'form', function (ev) {
    var field = $(this).find('[name="description"]');
    socket.emit('todos::create', {

    todos.create({
    text: field.val(),
    complete: false
    }, {}, app.errorHandler);
    });

    field.val('');
    ev.preventDefault();
    }).on('click', '.delete', function (ev) {
    });

    el.on('click', '.delete', function (ev) {
    var id = $(this).parents('li').data('id');
    socket.emit('todos::remove', id, {}, app.errorHandler);
    todos.remove(id);
    ev.preventDefault();
    }).on('click', '[name="done"]', function(ev) {
    });

    el.on('click', '[name="done"]', function(ev) {
    var id = $(this).parents('li').data('id');

    $(this).attr('disabled', 'disabled');

    socket.emit('todos::update', id, {
    todos.update(id, {
    complete: $(this).is(':checked')
    }, {}, app.errorHandler);
    });
    });

    socket.on('todos updated', app.updateTodo);
    socket.on('todos removed', app.removeTodo);
    socket.on('todos created', app.addTodo);
    socket.emit('todos::find', {}, function (error, todos) {
    todos.forEach(app.addTodo);
    todos.on('updated', updateTodo);
    todos.on('removed', removeTodo);
    todos.on('created', addTodo);

    todos.find(function(error, todos) {
    todos.forEach(addTodo);
    });
    </script>
    </body>
    </html>
    </html>
  2. daffl revised this gist Sep 30, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ <h1>Feathers real-time Todos</h1>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
    var socket = io.connect();
    var socket = io();
    var el = $('#todos');

    var app = {
  3. daffl revised this gist Sep 30, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -66,7 +66,8 @@ <h1>Feathers real-time Todos</h1>
    var field = $(this).find('[name="description"]');

    socket.emit('todos::create', {
    text: field.val()
    text: field.val(),
    complete: false
    }, {}, app.errorHandler);

    field.val('');
  4. daffl renamed this gist Sep 30, 2014. 1 changed file with 3 additions and 9 deletions.
    12 changes: 3 additions & 9 deletions server.js → app.js
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,7 @@
    // To get up and running with this example, download the Gist from
    // https://gist.github.com/daffl/6665992/download
    // Unpack and in the new folder run
    // > npm install body-parser feathers feathers-memory
    // > node server.js
    // Then go to http://localhost:8080
    // The REST API will be available at http://localhost:8080/todos

    var feathers = require('feathers');
    var memory = require('feathers-memory');
    var bodyParser = require('body-parser');
    // An in-memory service implementation
    var memory = require('feathers-memory');
    // Create an in-memory CRUD service for our Todos
    var todoService = memory();

    @@ -19,6 +12,7 @@ var app = feathers()
    // Parse HTTP bodies
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
    // Host the current directory (for index.html)
    .use(feathers.static(__dirname))
    // Host our Todos service on the /todos path
    .use('/todos', todoService);
  5. daffl revised this gist Aug 23, 2014. 1 changed file with 4 additions and 7 deletions.
    11 changes: 4 additions & 7 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -13,17 +13,14 @@ var bodyParser = require('body-parser');
    var todoService = memory();

    var app = feathers()
    // Set up REST API
    // Set up REST and SocketIO APIs
    .configure(feathers.rest())
    // Set up SocketIO
    .configure(feathers.socketio())
    // Enable parsing for POST, PUT and PATCH requests
    // in JSON and URL encoded forms
    // Parse HTTP bodies
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
    // Host index.html
    .use(feathers.static(__dirname))
    // Set up the Todo service
    .use('/todos',todoService);
    // Host our Todos service on the /todos path
    .use('/todos', todoService);

    app.listen(8080);
  6. daffl revised this gist Aug 10, 2014. 3 changed files with 25 additions and 97 deletions.
    17 changes: 8 additions & 9 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -30,6 +30,10 @@ <h1>Feathers real-time Todos</h1>
    var el = $('#todos');

    var app = {
    getElement: function(todo) {
    return el.find('[data-id="' + todo.id + '"]')
    },

    addTodo: function(todo) {
    var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
    '<label><input type="checkbox" name="done">' +
    @@ -42,19 +46,14 @@ <h1>Feathers real-time Todos</h1>
    app.updateTodo(todo);
    },
    removeTodo: function(todo) {
    el.find('[data-id="' + todo.id + '"]').remove();
    app.getElement(todo).remove();
    },
    updateTodo: function(todo) {
    var element = el.find('[data-id="' + todo.id + '"]');
    var element = app.getElement(todo);
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    if(todo.complete) {
    element.addClass('done');
    checkbox.prop('checked', true);
    } else {
    element.removeClass('done');
    checkbox.prop('checked', false);
    }
    element.toggleClass('done', todo.complete);
    checkbox.prop('checked', todo.complete);
    },
    errorHandler: function(error) {
    if(error) {
    23 changes: 17 additions & 6 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -1,18 +1,29 @@
    // To get up and running with this example, download the Gist from
    // https://gist.github.com/daffl/6665992/download
    // Unpack and in the new folder run
    // > npm install body-parser feathers
    // > npm install body-parser feathers feathers-memory
    // > node server.js
    // Then go to http://localhost:8080
    // The REST API will be available at http://localhost:8080/todos

    var feathers = require('feathers');
    var memory = require('feathers-memory');
    var bodyParser = require('body-parser');
    var TodoService = require('./todos');
    // Create an in-memory CRUD service for our Todos
    var todoService = memory();

    feathers()
    var app = feathers()
    // Set up REST API
    .configure(feathers.rest())
    // Set up SocketIO
    .configure(feathers.socketio())
    .use(bodyParser())
    // Enable parsing for POST, PUT and PATCH requests
    // in JSON and URL encoded forms
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
    // Host index.html
    .use(feathers.static(__dirname))
    .use('/todos', new TodoService())
    .listen(8080);
    // Set up the Todo service
    .use('/todos',todoService);

    app.listen(8080);
    82 changes: 0 additions & 82 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -1,82 +0,0 @@
    var tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
    };

    // Escapes HTML so that evil people can't inject mean things into the page
    function escapeHtml(str) {
    return str.replace(/[&<>]/g, function (tag) {
    return tagsToReplace[tag] || tag;
    });
    }

    function TodoStore() {
    this.todos = [];
    this.lastId = 0;
    }

    // Returns a Todo by it's id
    TodoStore.prototype.getById = function (id) {
    var currentTodo;
    for (var i = 0; i < this.todos.length; i++) {
    currentTodo = this.todos[i];
    if (currentTodo.id == id) {
    return currentTodo;
    }
    }

    return null;
    }

    TodoStore.prototype.find = function (params, callback) {
    callback(null, this.todos);
    }

    TodoStore.prototype.get = function (id, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Todo not found'));
    }

    callback(null, todo);
    }

    TodoStore.prototype.create = function (data, params, callback) {
    // Create our actual Todo object so that we only get what we really want
    var newTodo = {
    id: this.lastId++,
    text: escapeHtml(data.text),
    complete: !!data.complete
    };

    this.todos.push(newTodo);

    callback(null, newTodo);
    }

    TodoStore.prototype.update = function (id, data, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Todo does not exist'));
    }

    // We only want to update the `done` property
    // !! is used for sanitization turning everything into a real booelan
    todo.complete = !!data.complete;

    callback(null, todo);
    }

    TodoStore.prototype.remove = function (id, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Can not delete Todo'));
    }

    // Just splice our todo out of the array
    this.todos.splice(this.todos.indexOf(todo), 1);
    callback(null, todo);
    }

    module.exports = TodoStore;
  7. daffl revised this gist Jun 26, 2014. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,18 @@
    // To get up and running with this example, download the Gist from
    // https://gist.github.com/daffl/6665992/download
    // Unpack and in the new folder run
    // > npm install feathers
    // > npm install body-parser feathers
    // > node server.js
    // Then go to http://localhost:8080

    var feathers = require('feathers');
    var bodyParser = require('body-parser');
    var TodoService = require('./todos');

    feathers().configure(feathers.socketio())
    feathers()
    .configure(feathers.rest())
    .configure(feathers.socketio())
    .use(bodyParser())
    .use(feathers.static(__dirname))
    .use('/todos', new TodoService())
    .listen(8080);
  8. daffl revised this gist Apr 11, 2014. 2 changed files with 4 additions and 4 deletions.
    4 changes: 2 additions & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -48,7 +48,7 @@ <h1>Feathers real-time Todos</h1>
    var element = el.find('[data-id="' + todo.id + '"]');
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    if(todo.completed) {
    if(todo.complete) {
    element.addClass('done');
    checkbox.prop('checked', true);
    } else {
    @@ -82,7 +82,7 @@ <h1>Feathers real-time Todos</h1>
    $(this).attr('disabled', 'disabled');

    socket.emit('todos::update', id, {
    completed: $(this).is(':checked')
    complete: $(this).is(':checked')
    }, {}, app.errorHandler);
    });

    4 changes: 2 additions & 2 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,7 @@ TodoStore.prototype.create = function (data, params, callback) {
    var newTodo = {
    id: this.lastId++,
    text: escapeHtml(data.text),
    completed: !!data.completed
    complete: !!data.complete
    };

    this.todos.push(newTodo);
    @@ -63,7 +63,7 @@ TodoStore.prototype.update = function (id, data, params, callback) {

    // We only want to update the `done` property
    // !! is used for sanitization turning everything into a real booelan
    todo.completed = !!data.completed;
    todo.complete = !!data.complete;

    callback(null, todo);
    }
  9. daffl revised this gist Apr 11, 2014. 2 changed files with 7 additions and 7 deletions.
    8 changes: 4 additions & 4 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -33,7 +33,7 @@ <h1>Feathers real-time Todos</h1>
    addTodo: function(todo) {
    var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
    '<label><input type="checkbox" name="done">' +
    todo.description +
    todo.text +
    '</label><a href="javascript://" class="pull-right delete">' +
    '<span class="glyphicon glyphicon-remove"></span>' +
    '</a></li>';
    @@ -48,7 +48,7 @@ <h1>Feathers real-time Todos</h1>
    var element = el.find('[data-id="' + todo.id + '"]');
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    if(todo.done) {
    if(todo.completed) {
    element.addClass('done');
    checkbox.prop('checked', true);
    } else {
    @@ -67,7 +67,7 @@ <h1>Feathers real-time Todos</h1>
    var field = $(this).find('[name="description"]');

    socket.emit('todos::create', {
    description: field.val()
    text: field.val()
    }, {}, app.errorHandler);

    field.val('');
    @@ -82,7 +82,7 @@ <h1>Feathers real-time Todos</h1>
    $(this).attr('disabled', 'disabled');

    socket.emit('todos::update', id, {
    done: $(this).is(':checked')
    completed: $(this).is(':checked')
    }, {}, app.errorHandler);
    });

    6 changes: 3 additions & 3 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -46,8 +46,8 @@ TodoStore.prototype.create = function (data, params, callback) {
    // Create our actual Todo object so that we only get what we really want
    var newTodo = {
    id: this.lastId++,
    description: escapeHtml(data.description),
    done: !!data.done
    text: escapeHtml(data.text),
    completed: !!data.completed
    };

    this.todos.push(newTodo);
    @@ -63,7 +63,7 @@ TodoStore.prototype.update = function (id, data, params, callback) {

    // We only want to update the `done` property
    // !! is used for sanitization turning everything into a real booelan
    todo.done = !!data.done;
    todo.completed = !!data.completed;

    callback(null, todo);
    }
  10. daffl revised this gist Dec 11, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion todos.js
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,7 @@ TodoStore.prototype.getById = function (id) {
    var currentTodo;
    for (var i = 0; i < this.todos.length; i++) {
    currentTodo = this.todos[i];
    if (currentTodo.id === id) {
    if (currentTodo.id == id) {
    return currentTodo;
    }
    }
  11. daffl revised this gist Sep 27, 2013. 2 changed files with 10 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,14 @@
    // To get up and running with this example, download the Gist from
    // https://gist.github.com/daffl/6665992/download
    // Unpack and in the new folder run
    // > npm install feathers
    // > node server.js
    // Then go to http://localhost:8080

    var feathers = require('feathers');
    var TodoStore = require('./todos');
    var TodoService = require('./todos');

    feathers().configure(feathers.socketio())
    .use(feathers.static(__dirname))
    .use('/todos', new TodoStore())
    .use('/todos', new TodoService())
    .listen(8080);
    1 change: 1 addition & 0 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,7 @@ function escapeHtml(str) {
    }

    function TodoStore() {
    this.todos = [];
    this.lastId = 0;
    }

  12. daffl revised this gist Sep 27, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion server.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    var feathers = require('feathers');
    var TodoStore = require('./service');
    var TodoStore = require('./todos');

    feathers().configure(feathers.socketio())
    .use(feathers.static(__dirname))
  13. daffl revised this gist Sep 27, 2013. 2 changed files with 9 additions and 5 deletions.
    11 changes: 8 additions & 3 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,12 @@
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <style type="text/css">
    .done {
    text-decoration: line-through;
    }
    </style>

    <div class="container" id="todos">
    <h1>Feathers real-time Todos</h1>

    @@ -16,7 +22,6 @@ <h1>Feathers real-time Todos</h1>
    <button type="submit" class="btn btn-info col-md-12">Add Todo</button>
    </form>
    </div>
    </div>

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    @@ -30,7 +35,7 @@ <h1>Feathers real-time Todos</h1>
    '<label><input type="checkbox" name="done">' +
    todo.description +
    '</label><a href="javascript://" class="pull-right delete">' +
    '<span class="icon-remove"></span>' +
    '<span class="glyphicon glyphicon-remove"></span>' +
    '</a></li>';

    el.find('.todos').append(html);
    @@ -89,4 +94,4 @@ <h1>Feathers real-time Todos</h1>
    });
    </script>
    </body>
    </html>
    </html>
    3 changes: 1 addition & 2 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,6 @@ function escapeHtml(str) {
    }

    function TodoStore() {
    this.todos = [];
    this.lastId = 0;
    }

    @@ -46,7 +45,7 @@ TodoStore.prototype.create = function (data, params, callback) {
    // Create our actual Todo object so that we only get what we really want
    var newTodo = {
    id: this.lastId++,
    description: escape(data.description),
    description: escapeHtml(data.description),
    done: !!data.done
    };

  14. daffl revised this gist Sep 27, 2013. 2 changed files with 88 additions and 55 deletions.
    108 changes: 58 additions & 50 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -5,79 +5,87 @@
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
    <div class="container" id="todos">
    <h1>Feathers real-time Todos</h1>

    <div class="alert alert-danger" style="display: none;">Bla</div>

    <table class="table todos">
    <thead>
    <tr>
    <th>Description</th>
    <th></th>
    </tr>
    </thead>
    <tbody></tbody>
    </table>

    <ul class="todos list-unstyled"></ul>
    <form role="form" class="create-todo">
    <div class="form-group">
    <label for="description">New Todo</label>
    <input type="text" class="form-control" id="description" placeholder="New Todo description">
    <input type="text" class="form-control" name="description" placeholder="Add a new Todo">
    </div>
    <button type="submit" class="btn btn-primary pull-right">Submit</button>
    <button type="submit" class="btn btn-info col-md-12">Add Todo</button>
    </form>
    </div>
    </div>

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
    var socket = io.connect();
    var el = $('#todos');

    function addTodo(todo) {
    var html = '<tr data-id="' + todo.id + '">' +
    '<td>' + todo.description + '</td>' +
    '<td><a href="#" class="delete">remove</a></td>' +
    '</tr>';
    var app = {
    addTodo: function(todo) {
    var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
    '<label><input type="checkbox" name="done">' +
    todo.description +
    '</label><a href="javascript://" class="pull-right delete">' +
    '<span class="icon-remove"></span>' +
    '</a></li>';

    $('.todos tbody').append(html);
    }
    el.find('.todos').append(html);
    app.updateTodo(todo);
    },
    removeTodo: function(todo) {
    el.find('[data-id="' + todo.id + '"]').remove();
    },
    updateTodo: function(todo) {
    var element = el.find('[data-id="' + todo.id + '"]');
    var checkbox = element.find('[name="done"]').removeAttr('disabled');

    function removeTodo(id) {
    $('[data-id="' + id + '"]').remove();
    }

    function errorHandler(error) {
    if (error) {
    $('.alert').show().html(error.message);
    if(todo.done) {
    element.addClass('done');
    checkbox.prop('checked', true);
    } else {
    element.removeClass('done');
    checkbox.prop('checked', false);
    }
    },
    errorHandler: function(error) {
    if(error) {
    alert(error.message);
    }
    }
    }

    socket.on('todos removed', function (todo) {
    removeTodo(todo.id);
    });

    socket.on('todos created', addTodo);

    socket.emit('todos::find', {}, function (error, todos) {
    todos.forEach(addTodo);
    });

    $('.create-todo').submit(function (ev) {
    var field = $('#description');
    };

    el.on('submit', 'form', function (ev) {
    var field = $(this).find('[name="description"]');

    socket.emit('todos::create', {
    description: field.val()
    }, {}, errorHandler);

    }, {}, app.errorHandler);
    field.val('');
    ev.preventDefault();
    }).on('click', '.delete', function (ev) {
    var id = $(this).parents('li').data('id');
    socket.emit('todos::remove', id, {}, app.errorHandler);
    ev.preventDefault();
    }).on('click', '[name="done"]', function(ev) {
    var id = $(this).parents('li').data('id');

    $(this).attr('disabled', 'disabled');

    socket.emit('todos::update', id, {
    done: $(this).is(':checked')
    }, {}, app.errorHandler);
    });

    $('body').on('click', '.delete', function (ev) {
    var id = $(this).parents('tr').data('id');
    socket.emit('todos::remove', id, {}, errorHandler);
    ev.preventDefault();
    socket.on('todos updated', app.updateTodo);
    socket.on('todos removed', app.removeTodo);
    socket.on('todos created', app.addTodo);
    socket.emit('todos::find', {}, function (error, todos) {
    todos.forEach(app.addTodo);
    });
    </script>
    </body>
    35 changes: 30 additions & 5 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,22 @@
    var tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
    };

    // Escapes HTML so that evil people can't inject mean things into the page
    function escapeHtml(str) {
    return str.replace(/[&<>]/g, function (tag) {
    return tagsToReplace[tag] || tag;
    });
    }

    function TodoStore() {
    this.todos = [];
    this.lastId = 0;
    }

    // Returns a Todo by it's id
    TodoStore.prototype.getById = function (id) {
    var currentTodo;
    for (var i = 0; i < this.todos.length; i++) {
    @@ -29,9 +43,16 @@ TodoStore.prototype.get = function (id, params, callback) {
    }

    TodoStore.prototype.create = function (data, params, callback) {
    data.id = this.lastId++;
    this.todos.push(data);
    callback(null, data);
    // Create our actual Todo object so that we only get what we really want
    var newTodo = {
    id: this.lastId++,
    description: escape(data.description),
    done: !!data.done
    };

    this.todos.push(newTodo);

    callback(null, newTodo);
    }

    TodoStore.prototype.update = function (id, data, params, callback) {
    @@ -40,8 +61,11 @@ TodoStore.prototype.update = function (id, data, params, callback) {
    return callback(new Error('Todo does not exist'));
    }

    this.todos[this.todos.indexOf(todo)] = data;
    callback(null, data);
    // We only want to update the `done` property
    // !! is used for sanitization turning everything into a real booelan
    todo.done = !!data.done;

    callback(null, todo);
    }

    TodoStore.prototype.remove = function (id, params, callback) {
    @@ -50,6 +74,7 @@ TodoStore.prototype.remove = function (id, params, callback) {
    return callback(new Error('Can not delete Todo'));
    }

    // Just splice our todo out of the array
    this.todos.splice(this.todos.indexOf(todo), 1);
    callback(null, todo);
    }
  15. daffl created this gist Sep 23, 2013.
    84 changes: 84 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    <!DOCTYPE html>
    <html>
    <head>
    <title>Feathers real-time Todos</title>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
    <h1>Feathers real-time Todos</h1>

    <div class="alert alert-danger" style="display: none;">Bla</div>

    <table class="table todos">
    <thead>
    <tr>
    <th>Description</th>
    <th></th>
    </tr>
    </thead>
    <tbody></tbody>
    </table>

    <form role="form" class="create-todo">
    <div class="form-group">
    <label for="description">New Todo</label>
    <input type="text" class="form-control" id="description" placeholder="New Todo description">
    </div>
    <button type="submit" class="btn btn-primary pull-right">Submit</button>
    </form>
    </div>

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
    var socket = io.connect();

    function addTodo(todo) {
    var html = '<tr data-id="' + todo.id + '">' +
    '<td>' + todo.description + '</td>' +
    '<td><a href="#" class="delete">remove</a></td>' +
    '</tr>';

    $('.todos tbody').append(html);
    }

    function removeTodo(id) {
    $('[data-id="' + id + '"]').remove();
    }

    function errorHandler(error) {
    if (error) {
    $('.alert').show().html(error.message);
    }
    }

    socket.on('todos removed', function (todo) {
    removeTodo(todo.id);
    });

    socket.on('todos created', addTodo);

    socket.emit('todos::find', {}, function (error, todos) {
    todos.forEach(addTodo);
    });

    $('.create-todo').submit(function (ev) {
    var field = $('#description');

    socket.emit('todos::create', {
    description: field.val()
    }, {}, errorHandler);

    field.val('');
    ev.preventDefault();
    });

    $('body').on('click', '.delete', function (ev) {
    var id = $(this).parents('tr').data('id');
    socket.emit('todos::remove', id, {}, errorHandler);
    ev.preventDefault();
    });
    </script>
    </body>
    </html>
    7 changes: 7 additions & 0 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    var feathers = require('feathers');
    var TodoStore = require('./service');

    feathers().configure(feathers.socketio())
    .use(feathers.static(__dirname))
    .use('/todos', new TodoStore())
    .listen(8080);
    57 changes: 57 additions & 0 deletions todos.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    function TodoStore() {
    this.todos = [];
    this.lastId = 0;
    }

    TodoStore.prototype.getById = function (id) {
    var currentTodo;
    for (var i = 0; i < this.todos.length; i++) {
    currentTodo = this.todos[i];
    if (currentTodo.id === id) {
    return currentTodo;
    }
    }

    return null;
    }

    TodoStore.prototype.find = function (params, callback) {
    callback(null, this.todos);
    }

    TodoStore.prototype.get = function (id, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Todo not found'));
    }

    callback(null, todo);
    }

    TodoStore.prototype.create = function (data, params, callback) {
    data.id = this.lastId++;
    this.todos.push(data);
    callback(null, data);
    }

    TodoStore.prototype.update = function (id, data, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Todo does not exist'));
    }

    this.todos[this.todos.indexOf(todo)] = data;
    callback(null, data);
    }

    TodoStore.prototype.remove = function (id, params, callback) {
    var todo = this.getById(id);
    if (todo === null) {
    return callback(new Error('Can not delete Todo'));
    }

    this.todos.splice(this.todos.indexOf(todo), 1);
    callback(null, todo);
    }

    module.exports = TodoStore;