# Web Applications

## Building a Chat Web Application

It's now time to move onto building a proper web application. Rather than the simple todo application you would build in other systems, for node the standard is a chat application (which is a lot more exciting!).

To do this, we will use [Primus](https://github.com/primus/primus) and [ws](https://github.com/websockets/ws) both by [Arnout Kazemier](https://github.com/3rd-Eden).

Primus is a wrapper library over many different [WebSockets](https://en.wikipedia.org/wiki/Websocket) libraries. WebSockets is a technology that allows browser clients to communicate with servers. Until WebSockets became a thing in 2011, such functionality was achieved via [several dodgy workarounds](http://slides.com/balupton/what).

We can install Primus for our project like so:

```bash
npm init  # setup our project
npm install --save express  # install express, which we will attach Primus to
npm install --save primus  # install Primus
npm install --save ws  # the WebSockets library we will sue with Primus
```

### Server to Browser Relay

The most basic example of this is the following, which will allow the server to broadcast to all clients, while receiving responses from clients:

{% tabs %}
{% tab title="socket-server.js" %}

```javascript
'use strict'

// Requires
const express = require('express')
const Primus = require('primus')
const pathUtil = require('path')

// Application
const app = require('express')()
const server = require('http').createServer(app)
const primus = new Primus(server, { transformer: 'websockets' })

// Middlewares
app.get('/', function (req, res) {
	require('fs').createReadStream(pathUtil.join(__dirname, 'socket-client.html')).pipe(res)
})
app.use(function (req, res) {
	res.status(404).send('404 Not Found. 🙁 \n')
})

// Socket
primus.on('connection', function (spark) {
	console.log('connection has the following headers', spark.headers)
	console.log('connection was made from', spark.address)
	console.log('connection id', spark.id)

	// Receive messages
	spark.on('data', function (message) {
		console.log('connection', spark.id, 'sends', message.toString())
	})

	// Send messages
	process.stdin.on('data', function (message) {
		spark.write('The server has spoken: ' + message.toString())
	})

	// Send an initial hello
	spark.write('Hello user. I am the server communicating to you.')
})

// Listen
server.listen(8080)
```

{% endtab %}

{% tab title="socket-client.html" %}

```markup
<!DOCTYPE html>
<html>

<head>
	<title>My Web App</title>
</head>

<body>
	<!-- Scripts -->
	<script src="/primus/primus.js"></script>
	<script>
		// Tell primus to create a new connect to the current domain/port/protocol
		var primus = new Primus()

		// Ask for a response if we receive data
		primus.on('data', function (message) {
			alert(message)
			var response = prompt('What would you like to say?')
			if (response) {
				primus.write(response)
				alert('Sent')
			}
		})
	</script>
	Hello.
</body>

</html>
```

{% endtab %}
{% endtabs %}

Test it: <http://localhost:8080/>

### Browser to Browser Broadcasting

A more useful example of this, is your basic chat app, which allows clients to broadcast to other clients:

{% tabs %}
{% tab title="chat-server.js" %}

```javascript
'use strict'

// Requires
const express = require('express')
const Primus = require('primus')
const pathUtil = require('path')

// Application
const app = require('express')()
const server = require('http').createServer(app)
const primus = new Primus(server, { transformer: 'websockets' })

// Middlewares
app.get('/', function (req, res) {
	require('fs').createReadStream(pathUtil.join(__dirname, 'chat-client.html')).pipe(res)
})
app.use(function (req, res) {
	res.status(404).send('404 Not Found. 🙁 \n')
})

// Socket
primus.on('connection', function (spark) {
	console.log('connection has the following headers', spark.headers)
	console.log('connection was made from', spark.address)
	console.log('connection id', spark.id)

	// Receive messages
	spark.on('data', function (message) {
		// Broadcast them back to everyone
		primus.write('user ' + spark.id + ' sends ' + message.toString())
	})

	// Send an initial hello
	spark.write('Hello user ' + spark.id + '. I am the server communicating to you.')
})

// Listen
server.listen(8080)
```

{% endtab %}

{% tab title="chat-client.html" %}

```markup
<!DOCTYPE html>
<html>

<head>
	<title>My Web App</title>
	<style>
		.messages {
			max-height: 500px;
			border: 1px solid gray;
			overflow: auto;
		}
		.messageInput {
			width: 100%;
			padding: 1em;
		}
	</style>
</head>

<body>
	<!-- App -->
	<div class="app">
		<ul class="messages"></ul>
		<input type="text" disabled class="messageInput" placeholder="Enter your message here" />
	</div>

	<!-- Scripts -->
	<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
	<script src="/primus/primus.js"></script>
	<script>
		// Tell primus to create a new connect to the current domain/port/protocol
		var primus = new Primus()

		// Ready
		$(function () {
			// Fetch
			var $app = $('.app')
			var $messages = $app.find('.messages')
			var $messageInput = $app.find('.messageInput')

			// Enable input once connection is open
			primus.on('open', function () {
				$messageInput.removeAttr('disabled').focus()
			})

			// Receive message
			primus.on('data', function (message) {
				$message = $('<li>', {
					'class': 'message',
					text: message
				})
				$messages.append($message)
			})

			// Send message
			$messageInput.on('keypress', function (event) {
				if (event.keyCode === 13) {  // enter
					var message = $messageInput.val().trim()
					primus.write(message)
					$messageInput.val('')  // clear input
				}
			})
		})
	</script>
</body>

</html>
```

{% endtab %}
{% endtabs %}

Test it: <http://localhost:8080/>

## Where can this go?

It is now your turn to have a go and mash up your own solution. To help you get started, here are a bunch of ideas on how you can extend the chat application:

* Chat Rooms
  * Add support for two chat rooms
  * Add support for unlimited chat rooms
  * Allow users to change the names of the chat rooms
* Users
  * Give each user a randomly generated name - e.g. `User ${Math.random()}`
  * Next to each message, show the user who sent it - e.g.`${user.name} says: ${message.text}`
  * Show user connection and disconnection events as messages in the chat - e.g. `${user.name} joined the chat"`
  * Remember the user's details if they leave the page and come back - e.g. localstorage or sessions
  * Allow the user to change their name
  * Show user name change events as messages in the chat - e.g.`${oldName} changed their name to ${newName}`
  * Give each user their own randomly selected color - e.g. `style="color: hsl(50,50,50);"`
  * Create a sidebar that lists all active members in the chat room
  * Allow users to specify their email
  * If a user has an email specified, display their avatar next to their username in the message listing
  * When a user changes their details, automatically update all prior mentions of their details
* Abstractions
  * Experiment with pre-processors - [DocPad](http://docpad.org) could help with this
* Messages
  * Relative times
  * Markdown support
  * Webkit notifications
