Permalink
Browse files

Add SocketStream app

  • Loading branch information...
1 parent 8a84177 commit f00d362449a4c20d8e1d01363a82927098471160 @sindresorhus sindresorhus committed Jul 15, 2012
@@ -0,0 +1 @@
+node_modules
@@ -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
@@ -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
@@ -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 );
@@ -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();
@@ -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 );
Oops, something went wrong.

0 comments on commit f00d362

Please sign in to comment.