mirror of
https://github.com/lovasoa/whitebophir
synced 2024-11-10 06:24:17 +00:00
Add a better infrastructure, in order to prepare the introduction of new modules (called "tools").
This commit is contained in:
parent
5775cc1888
commit
7b4b5e24bc
11 changed files with 2773 additions and 81 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
node_modules
|
||||
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
|
|
54
client-data/board.css
Normal file
54
client-data/board.css
Normal 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
40
client-data/board.html
Normal 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
42
client-data/error.html
Normal 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>
|
|
@ -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
21
client-data/js/minitpl.js
Normal 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;
|
||||
}
|
19
package.json
19
package.json
|
@ -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
2418
server-data/history.txt
Normal file
File diff suppressed because it is too large
Load diff
45
server.js
45
server.js
|
@ -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
42
server/server.js
Normal 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
80
server/sockets.js
Normal 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;
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue