Skip to content

Commit

Permalink
Add SocketStream app
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Jul 15, 2012
1 parent 8a84177 commit f00d362
Show file tree
Hide file tree
Showing 16 changed files with 752 additions and 0 deletions.
1 change: 1 addition & 0 deletions labs/architecture-examples/socketstream/.gitignore
@@ -0,0 +1 @@
node_modules
6 changes: 6 additions & 0 deletions labs/architecture-examples/socketstream/.nodemonignore
@@ -0,0 +1,6 @@
# Ignore file for Nodemon: https://github.com/remy/nodemon
# Install with 'npm install -g nodemon' then start your app with 'nodemon app.js'
# From then on, all changes you make to /server will cause your app to automatically restart

/client/*
./README.md
12 changes: 12 additions & 0 deletions labs/architecture-examples/socketstream/README.md
@@ -0,0 +1,12 @@
# SocketStream TodoMVC app

> A fast, modular Node.js web framework dedicated to building realtime single-page apps

## Getting Started

Running this app requires [Node.js](http://nodejs.org)

- `cd` into this folder and run `npm install` to fetch all the required dependencies
- `node app.js` to start the app
- Open `localhost:3000` in two side-by-side browser windows, add a todo and see the magic
30 changes: 30 additions & 0 deletions labs/architecture-examples/socketstream/app.js
@@ -0,0 +1,30 @@
var http = require('http'),
ss = require('socketstream');

// Define a single-page client
ss.client.define('main', {
view: 'app.html',
css: ['base.css'],
code: [ 'libs', 'app' ],
tmpl: '*'
});

// Serve this client on the root URL
ss.http.route( '/', function( req, res ) {
res.serveClient('main');
});

// Use server-side compiled Hogan (Mustache) templates. Others engines available
ss.client.templateEngine.use( require('ss-hogan') );

// Minimize and pack assets if you type: SS_ENV=production node app.js
if ( ss.env === 'production' ) {
ss.client.packAssets();
}

// Start web server
var server = http.Server( ss.http.middleware );
server.listen(3000);

// Start SocketStream
ss.start( server );
159 changes: 159 additions & 0 deletions labs/architecture-examples/socketstream/client/code/app/app.js
@@ -0,0 +1,159 @@
/*global $, ss */
'use strict';

var Utils = {
// https://gist.github.com/1308368
uuid: function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b},
pluralize: function( count, word ) {
return count === 1 ? word : word + 's';
}
};

var App = {
init: function() {
var self = this;
this.ENTER_KEY = 13;
ss.rpc('todos.getAll', function( todos ) {
self.todos = todos;
self.cacheElements();
self.bindEvents();
self.render();
});
},
cacheElements: function() {
this.$todoApp = $('#todoapp');
this.$newTodo = $('#new-todo');
this.$toggleAll = $('#toggle-all');
this.$main = $('#main');
this.$todoList = $('#todo-list');
this.$footer = this.$todoApp.find('#footer');
this.$count = $('#todo-count');
this.$clearBtn = $('#clear-completed');
},
bindEvents: function() {
var list = this.$todoList;
this.$newTodo.on( 'keyup', this.create );
this.$toggleAll.on( 'change', this.toggleAll );
this.$footer.on( 'click', '#clear-completed', this.destroyCompleted );
list.on( 'change', '.toggle', this.toggle );
list.on( 'dblclick', '.view', this.edit );
list.on( 'keypress', '.edit', this.blurOnEnter );
list.on( 'blur', '.edit', this.update );
list.on( 'click', '.destroy', this.destroy );
ss.event.on( 'updateTodos', this.updateTodos );
},
updateTodos: function( todos ) {
App.todos = todos;
App.render( true );
},
render: function( preventRpc ) {
var html = this.todos.map(function( el ) {
return ss.tmpl.todo.render( el );
}).join('');

this.$todoList.html( html );
this.$main.toggle( !!this.todos.length );
this.$toggleAll.prop( 'checked', !this.activeTodoCount() );
this.renderFooter();

if ( !preventRpc ) {
ss.rpc( 'todos.update', this.todos );
}
},
renderFooter: function() {
var todoCount = this.todos.length,
activeTodoCount = this.activeTodoCount(),
footer = {
activeTodoCount: activeTodoCount,
activeTodoWord: Utils.pluralize( activeTodoCount, 'item' ),
completedTodos: todoCount - activeTodoCount
};

this.$footer.toggle( !!todoCount );
this.$footer.html( ss.tmpl.footer.render( footer ) );
},
toggleAll: function() {
var isChecked = $( this ).prop('checked');
$.each( App.todos, function( i, val ) {
val.completed = isChecked;
});
App.render();
},
activeTodoCount: function() {
var count = 0;
$.each( this.todos, function( i, val ) {
if ( !val.completed ) {
count++;
}
});
return count;
},
destroyCompleted: function() {
var todos = App.todos,
l = todos.length;
while ( l-- ) {
if ( todos[l].completed ) {
todos.splice( l, 1 );
}
}
App.render();
},
// Accepts an element from inside the ".item" div and
// returns the corresponding todo in the todos array
getTodo: function( elem, callback ) {
var id = $( elem ).closest('li').data('id');
$.each( this.todos, function( i, val ) {
if ( val.id === id ) {
callback.apply( App, arguments );
return false;
}
});
},
create: function(e) {
var $input = $(this),
val = $.trim( $input.val() );
if ( e.which !== App.ENTER_KEY || !val ) {
return;
}
App.todos.push({
id: Utils.uuid(),
title: val,
completed: false
});
$input.val('');
App.render();
},
toggle: function() {
App.getTodo( this, function( i, val ) {
val.completed = !val.completed;
});
App.render();
},
edit: function() {
$(this).closest('li').addClass('editing').find('.edit').focus();
},
blurOnEnter: function( e ) {
if ( e.keyCode === App.ENTER_KEY ) {
e.target.blur();
}
},
update: function() {
var val = $.trim( $(this).removeClass('editing').val() );
App.getTodo( this, function( i ) {
if ( val ) {
this.todos[ i ].title = val;
} else {
this.todos.splice( i, 1 );
}
this.render();
});
},
destroy: function() {
App.getTodo( this, function( i ) {
this.todos.splice( i, 1 );
this.render();
});
}
};

App.init();
24 changes: 24 additions & 0 deletions labs/architecture-examples/socketstream/client/code/app/entry.js
@@ -0,0 +1,24 @@
// This file automatically gets called first by SocketStream and must always exist

// Make 'ss' available to all modules and the browser console
window.ss = require('socketstream');

ss.server.on('disconnect', function() {
console.log('Connection down :-(');
});

ss.server.on('reconnect', function() {
console.log('Connection back up :-)');
});

ss.server.on('ready', function() {

// Wait for the DOM to finish loading
$(function() {

// Load app
require('/app');

});

});
@@ -0,0 +1,7 @@
(function( window ) {
'use strict';

if ( location.hostname === 'todomvc.com' ) {
var _gaq=[['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
}
})( window );

0 comments on commit f00d362

Please sign in to comment.