Add a better infrastructure, in order to prepare the introduction of new modules (called "tools").

This commit is contained in:
Ophir LOJKINE 2013-09-27 12:39:47 +02:00
parent 5775cc1888
commit 7b4b5e24bc
11 changed files with 2773 additions and 81 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
node_modules
lib-cov
*.seed
*.log

54
client-data/board.css Normal file
View file

@ -0,0 +1,54 @@
html, body, svg {
padding:0;
margin:0;
}
#menu {
position:absolute;
left:0;
top:0;
color:#EEE;
background-color:rgba(0,0,0,0.4);
width:20px;
height:100%;
overflow:hidden;
}
#menu:hover {
background-color:rgba(0,0,0,0.8);
width:200px;
height:1000px;
transition-duration:1s;
}
#menu #menuItems {
display:none;
}
#menu:hover #menuItems {
display:block;
}
#menu h2{ /*Menu title ("Menu")*/
font-size:1em;
color:black;
text-align:center;
word-wrap:break-word;
padding:0;
margin:0;
}
#menu:hover h2{
color:#EEE;
word-wrap:normal;
font-size:2em;
}
#menu li {
width:150px;
color:#999;
}
#menu li:hover {
color:white;
}

40
client-data/board.html Normal file
View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="board.css">
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
</head>
<body>
<div id="menu">
<h2>Menu</h2>
<div id="menuItems">
<h3>Tools</h3>
<ul id="tools">
<li class="tool">Tool template</li>
</ul>
<h3>Configuration</h3>
</div>
</div>
<svg id="canvas"
width="500" height="500" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<style type="text/css"><![CDATA[
polyline {
fill: none;
stroke: black;
stroke-width: 3;
}
]]></style>
</defs>
</svg>
<script type="text/javascript" src="js/board.js"></script>
</body>
</html>

42
client-data/error.html Normal file
View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WBO - Error</title>
<style>
html {
background-color : #303;
}
h1, h2 {
font-family : monospace;
color : #F77;
margin:auto;
margin-top:100px;
width:50%;
font-size:5em;
}
@keyframes rotation
{
0% {color: #FA0;transform:rotate(0deg);}
50% {color: #F11;transform:rotate(150deg);font-size:3em;}
100% {color: #F0A;transform:rotate(30deg);}
}
h1 {
animation: rotation 5s infinite;
animation-delay:3s;
animation-direction:alternate;
width:30px;
}
h1:hover {
animation-play-state:paused;
}
</style>
</head>
<body>
<h1>:-(</h1>
<h2>Sorry, an error occured...</h2>
</body>
</html>

View file

@ -1,35 +1,71 @@
<!DOCTYPE html>
<html>
var Tools = {};
Tools.svg = document.getElementById("canvas");
Tools.socket = io.connect('');
Tools.list = {}; // An array of all known tools. {"toolName" : {toolObject}}
<head>
<meta charset="utf-8" />
<script src="/socket.io/socket.io.js"></script>
<style type="text/css">
html, body, svg {
padding:0;
margin:0;
Tools.add = function (newTool) {
if (newTool.name in Tools.list) {
console.log("Tools.add: The tool '"+newTool.name+"' is already" +
"in the list. Updating it...");
}
//Add the tool to the list
Tools.list[newTool.name] = newTool;
}
Tools.change = function (toolName){
//There is not necessarily already a curTool
if (Tools.curTool) {
//Remove the old event listeners
for (var event in Tools.curTool.listeners) {
var listener = Tools.curTool.listeners[event];
Tools.svg.removeEventListener(event, listener);
}
</style>
</head>
}
//Add the new event listeners
for (var event in newtool.listeners) {
var listener = newtool.listeners[event];
Tools.svg.addEventListener(event, listener);
}
Tools.curTool = Tools.list[toolName];
}
<body>
Tools.drawAndSend = function (data) {
Tools.curTool.draw(data);
Tools.socket.emit('broadcast', {
'tool' : curTool.name,
'data' : data
});
}
<svg id="canvas"
width="500" height="500" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<style type="text/css"><![CDATA[
polyline {
fill: none;
stroke: black;
stroke-width: 3;
}
]]></style>
</defs>
</svg>
Tools.socket.on("broadcast", function (message){
var tool = Tools.list[message.tool];
if (tool) {
tool.draw(message.data);
}
});
/**
What does a "tool" object look like?
newtool = {
"name" : "SuperTool",
"listeners" : {
"mousedown" : function(x){...},
"mousemove" : function(x){...}
},
"draw" : function(data){
//Print the data on Tools.svg
}
}
<script>
*/
(function(){
var pen = {
"name" : "pen"
}; //pen is a tool
})();
var socket = io.connect('');
socket.on('broadcast', function (data) {
@ -125,6 +161,3 @@ function generateUID(prefix, suffix) {
if (suffix) rndStr = rndStr + suffix;
return rndStr;
}
</script>
</body>
</html>

21
client-data/js/minitpl.js Normal file
View file

@ -0,0 +1,21 @@
function Minitpl(elem, data) {
this.elem = (typeof(elem)==="string") ? document.querySelector(elem) : elem;
this.parent = this.elem.parentNode;
this.parent.removeChild(this.elem);
}
Minitpl.prototype.add = function(data) {
var newElem = this.elem.cloneNode();
for (var key in data) {
var val = data[key];
var matches = newElem.querySelectorAll(key);
for (var i=0; i<matches.length; i++) {
if (typeof(val)==="function") {
matches[i].textContent = val(matches[i]);
} else {
matches[i].textContent = val;
}
}
}
this.parent.appendChild(newElem);
return newElem;
}

View file

@ -1,9 +1,14 @@
{
"name" : "Whitebophir",
"version" : "0.0.0",
"description" : "Online collaborative whiteboard",
"main" : "server.js",
"dependencies" : {
"socket.io" : "0.9.x"
}
"keywords" : [
"collaborative",
"whiteboard"
],
"version" : "0.0.1",
"dependencies" : {
"node-static" : "0.7.x",
"socket.io" : "0.9.x"
},
"name" : "Whitebophir",
"description" : "Online collaborative whiteboard",
"main" : "./server/server.js"
}

2418
server-data/history.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,45 +0,0 @@
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app, {log:false})
, fs = require('fs')
var PORT = 8080;
app.listen(PORT);
console.log("Server listening on "+PORT);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
var MAX_HISTORY_LENGTH = 1e5; //Size of the history
var history = [];
io.sockets.on('connection', function (socket) {
//On the first connection, send all previously broadcasted data
for (var i=0;i<history.length;i++){
socket.emit("broadcast", history[i]);
}
socket.on('broadcast', function (data) {
//Send data to all other connected users
socket.broadcast.emit('broadcast', data);
//Save the data in memory
history.push(data);
//Avoid a memory overload
if (history.length > MAX_HISTORY_LENGTH) {
history.pop();
}
});
});

42
server/server.js Normal file
View file

@ -0,0 +1,42 @@
var app = require('http').createServer(handler)
, sockets = require('./sockets.js').start(app)
, fs = require('fs')
, path = require('path')
, nodestatic = require("node-static");
/**
* Folder from which static files will be served
* @const
* @type {string}
*/
var WEBROOT = path.join(__dirname, "../client-data");
/**
* Port on which the application will listen
* @const
* @type {number}
*/
var PORT = 8080;
app.listen(PORT);
console.log("Server listening on "+PORT);
var fileserver = new nodestatic.Server(WEBROOT);
function handler (request, response) {
switch(request.url) {
case "/":
fileserver.serveFile("board.html", 200, {}, request, response);
break;
default:
fileserver.serve(request, response, function (err, res){
if (err) {
console.warn("Error serving '"+request.url+"' : "+err.message);
fileserver.serveFile('error.html', err.status, {}, request, response);
}
});
}
}

80
server/sockets.js Normal file
View file

@ -0,0 +1,80 @@
var iolib = require('socket.io')
, path = require("path")
, fs = require('fs');
/**
* Name of the file the persistent data will be written to
* if there are more messages, older ones will be erased
* @const
* @type {string}
*/
var HISTORY_FILE = path.join(__dirname, "../server-data/history.txt");
/**
* Number of messages to keep in memory
* if there are more messages, older ones will be erased
* @const
* @type {number}
*/
var MAX_HISTORY_LENGTH = 1e5;
var history = [];
//Load existing history
fs.readFile(HISTORY_FILE, 'utf8', function (file_err, history_str) {
if (file_err) {
console.log("Unable to open history file: "+file_err);
}else {
try {
history = history_str
.split("\n")
.slice(-MAX_HISTORY_LENGTH)
.filter(function(line){return line && line[0]!="#"})
.map(JSON.parse);
} catch(json_err) {
console.log("Bad history file: "+json_err);
}
}
});
function socketConnection (socket) {
//On the first connection, send all previously broadcasted data
for (var i=0;i<history.length;i++){
socket.emit("broadcast", history[i]);
}
socket.on('broadcast', function (data) {
//Send data to all other connected users
socket.broadcast.emit('broadcast', data);
addHistory(data);
});
}
function addHistory(data) {
//Save the data in memory
history.push(data);
//Save the data to a file
fs.open(HISTORY_FILE, 'a', function (err, fd){
if (err) console.error(err);
else {
var str_data = JSON.stringify(data)+'\n';
fs.write(fd, str_data);
}
});
//Avoid a memory overload
if (history.length > MAX_HISTORY_LENGTH) {
history.pop();
}
}
if (exports) {
exports.start = function(app){
io = iolib.listen(app, {'log':false});
io.sockets.on('connection', socketConnection);
return io;
};
}