Merge remote-tracking branch 'upstream/master'
3
.gitignore
vendored
|
@ -2,4 +2,5 @@
|
||||||
rsync.sh
|
rsync.sh
|
||||||
*.swp
|
*.swp
|
||||||
*.log
|
*.log
|
||||||
.monitor
|
.monitor
|
||||||
|
node_modules/
|
||||||
|
|
|
@ -40,22 +40,14 @@ how to install and run on your own computer (linux/osx)
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
|
||||||
- install redis v2.2.2
|
- install redis v2.2.2
|
||||||
- install node.js >= 0.4.1
|
- install node.js >= 0.4.7
|
||||||
- install npm
|
- install npm
|
||||||
- install these npm packages:
|
- cd to the scrumblr directory; you should see server.js and config.js and other files.
|
||||||
- async
|
- run `npm install`
|
||||||
- express
|
- If you get errors about express, you may need to change in package.json to have "express": ">=2.4.x",
|
||||||
- jade
|
- run redis `redis-server`
|
||||||
- redis-client
|
- run scrumblr `node server.js 80` where "80" is the port you have opened in your firewall and want scrumblr to run on.
|
||||||
- redis
|
- open a browser to `http://localhost:80` where "80" is the port you chose in the previous step.
|
||||||
- sanitizer
|
|
||||||
- socket.io
|
|
||||||
- simplesets
|
|
||||||
- connect-redis
|
|
||||||
- (and perhaps more which you will notice when you try to start it)
|
|
||||||
- now start redis ($ redis-server)
|
|
||||||
- now start ($ node server.js 80) where "80" is the port you want it to run on.
|
|
||||||
|
|
||||||
license
|
license
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
BIN
client/css/bg/45degreee_fabric.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
client/css/bg/bright_squares.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
client/css/bg/cardboard.png
Normal file
After Width: | Height: | Size: 200 KiB |
BIN
client/css/bg/circles.png
Normal file
After Width: | Height: | Size: 247 B |
BIN
client/css/bg/concrete_wall_2.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
client/css/bg/concrete_wall_2_2.png
Normal file
After Width: | Height: | Size: 361 KiB |
BIN
client/css/bg/concrete_wall_3.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
client/css/bg/grunge_wall.png
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
client/css/bg/noisy.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
client/css/bg/paper_3.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
client/css/bg/rockywall.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
client/css/bg/soft_wallpaper.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
client/css/bg/stucco.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
client/css/bg/whitey.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
client/css/scribbles.jpeg
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
client/css/scribbles2.png
Normal file
After Width: | Height: | Size: 143 KiB |
|
@ -46,16 +46,39 @@
|
||||||
display: block;
|
display: block;
|
||||||
width: 79px;
|
width: 79px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
margin: 3px 10px 0px 19px;
|
margin: 1px 10px 0px 19px;
|
||||||
|
|
||||||
font-family: "Arial Rounded MT Bold" , arial, serif;
|
font-family: "Arial Rounded MT Bold" , arial, serif;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
letter-spacing: 0px;
|
letter-spacing: 0px;
|
||||||
|
line-height: 9px;
|
||||||
|
|
||||||
xtext-shadow: 0px 0px 1px #444;
|
xtext-shadow: 0px 0px 1px #444;
|
||||||
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.xcontent {
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
width: 79px;
|
||||||
|
height: 45px;
|
||||||
|
margin: 0px 10px 0px 19px;
|
||||||
|
|
||||||
|
font-family: "Arial Rounded MT Bold", "Arial" , arial, serif;
|
||||||
|
xfont-size: 10px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
line-height: 8px;
|
||||||
|
|
||||||
|
xtext-shadow: 0px 0px 1px #444;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
color: #333;
|
color: #333;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
|
|
@ -12,6 +12,17 @@ body {
|
||||||
|
|
||||||
xheight: 1400px;
|
xheight: 1400px;
|
||||||
xwidth: 2000px;
|
xwidth: 2000px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
-o-user-select:none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
background-image: url('/css/bg/concrete_wall_2_2.png');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#board {
|
#board {
|
||||||
|
@ -46,8 +57,8 @@ body {
|
||||||
xmargin-left: auto;
|
xmargin-left: auto;
|
||||||
xmargin-right: auto;
|
xmargin-right: auto;
|
||||||
|
|
||||||
-webkit-box-shadow: 1px 1px 2px #aaa;
|
-webkit-box-shadow: 1px 3px 2px #aaa;
|
||||||
-moz-box-shadow: 1px 1px 2px #aaa;
|
-moz-box-shadow: 1px 3px 2px #999;
|
||||||
|
|
||||||
x-webkit-transform:rotate(0.3deg); /*"is that a little off-level? i feel like it is off-level"*/
|
x-webkit-transform:rotate(0.3deg); /*"is that a little off-level? i feel like it is off-level"*/
|
||||||
}
|
}
|
||||||
|
@ -57,8 +68,8 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: inline;
|
display: inline;
|
||||||
opacity: .08;
|
opacity: 0.035;
|
||||||
background-image: url('nexum_whiteboard.jpg');
|
background-image: url('scribbles2.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,7 +92,7 @@ width: 16px; height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-edit-form, .card-edit-form input, .card-edit-form textarea {
|
.card-edit-form, .card-edit-form input, .card-edit-form textarea {
|
||||||
border:none;
|
border: none;
|
||||||
font-size:inherit;
|
font-size:inherit;
|
||||||
font-weight:inherit;
|
font-weight:inherit;
|
||||||
background-color:inherit;
|
background-color:inherit;
|
||||||
|
@ -95,6 +106,8 @@ width: 16px; height: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
resize: none;
|
resize: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
color: #330066;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +479,17 @@ input:hover {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#marker {
|
||||||
|
position: absolute; bottom: 0; right: 200px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eraser {
|
||||||
|
position: absolute; bottom: 0; right: 70px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
client/images/eraser.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
client/images/marker-and-eraser.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
client/images/marker.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
|
@ -656,7 +656,8 @@ $.widget("ui.mouse", {
|
||||||
.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
|
.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
|
||||||
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
|
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
|
||||||
|
|
||||||
event.preventDefault();
|
//ali: fixing this http://bugs.jqueryui.com/ticket/4261
|
||||||
|
//event.preventDefault();
|
||||||
event.originalEvent.mouseHandled = true;
|
event.originalEvent.mouseHandled = true;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -86,7 +86,7 @@ $.widget("ui.mouse", {
|
||||||
.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
|
.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
|
||||||
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
|
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
|
||||||
|
|
||||||
event.preventDefault();
|
//event.preventDefault();
|
||||||
event.originalEvent.mouseHandled = true;
|
event.originalEvent.mouseHandled = true;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -285,16 +285,16 @@
|
||||||
} else if ('submit' == settings.onblur) {
|
} else if ('submit' == settings.onblur) {
|
||||||
input.blur(function(e) {
|
input.blur(function(e) {
|
||||||
/* prevent double submit if submit was clicked */
|
/* prevent double submit if submit was clicked */
|
||||||
//t = setTimeout(function() {
|
t = setTimeout(function() {
|
||||||
form.submit();
|
form.submit();
|
||||||
//}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
//ali here: i hacked this in so that submit happens on mouseout too
|
//ali here: i hacked this in so that submit happens on mouseout too
|
||||||
input.mouseout(function(e) {
|
input.blur(function(e) {
|
||||||
/* prevent double submit if submit was clicked */
|
/* prevent double submit if submit was clicked */
|
||||||
//t = setTimeout(function() {
|
t = setTimeout(function() {
|
||||||
form.submit();
|
form.submit();
|
||||||
//}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
} else if ($.isFunction(settings.onblur)) {
|
} else if ($.isFunction(settings.onblur)) {
|
||||||
input.blur(function(e) {
|
input.blur(function(e) {
|
||||||
|
|
157
client/script.js
|
@ -5,9 +5,7 @@ var currentTheme = "bigcards";
|
||||||
var boardInitialized = false;
|
var boardInitialized = false;
|
||||||
|
|
||||||
|
|
||||||
var socket = new io.Socket( );
|
var socket = io.connect();
|
||||||
socket.connect();
|
|
||||||
|
|
||||||
|
|
||||||
//an action has happened, send it to the
|
//an action has happened, send it to the
|
||||||
//server
|
//server
|
||||||
|
@ -20,7 +18,7 @@ function sendAction(a, d)
|
||||||
data: d
|
data: d
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.send ( message );
|
socket.json.send ( message );
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on('connect', function(){
|
socket.on('connect', function(){
|
||||||
|
@ -89,10 +87,7 @@ function getMessage( m )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'moveCard':
|
case 'moveCard':
|
||||||
$("#" + data.id).animate({
|
moveCard($("#" + data.id), data.position);
|
||||||
left: data.position.left+"px",
|
|
||||||
top: data.position.top+"px"
|
|
||||||
}, 500);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'initCards':
|
case 'initCards':
|
||||||
|
@ -101,7 +96,7 @@ function getMessage( m )
|
||||||
|
|
||||||
case 'createCard':
|
case 'createCard':
|
||||||
//console.log(data);
|
//console.log(data);
|
||||||
drawNewCard(data.id, data.text, data.x, data.y, data.rot, data.colour, null);
|
drawNewCard(data.id, data.text, data.x, data.y, data.rot, data.colour, null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'deleteCard':
|
case 'deleteCard':
|
||||||
|
@ -170,9 +165,22 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
<img class="card-image" src="/images/' + colour + '-card.png">\
|
<img class="card-image" src="/images/' + colour + '-card.png">\
|
||||||
<div id="content:' + id + '" class="content stickertarget droppable">' + text + '</div>\
|
<div id="content:' + id + '" class="content stickertarget droppable">' + text + '</div>\
|
||||||
</div>';
|
</div>';
|
||||||
$(h).appendTo('#board');
|
|
||||||
|
var card = $(h);
|
||||||
|
card.appendTo('#board');
|
||||||
|
|
||||||
$( ".card" ).draggable(
|
//@TODO
|
||||||
|
//Draggable has a bug which prevents blur event
|
||||||
|
//http://bugs.jqueryui.com/ticket/4261
|
||||||
|
//So we have to blur all the cards and editable areas when
|
||||||
|
//we click on a card
|
||||||
|
//The following doesn't work so we will do the bug
|
||||||
|
//fix recommended in the above bug report
|
||||||
|
// card.click( function() {
|
||||||
|
// $(this).focus();
|
||||||
|
// } );
|
||||||
|
|
||||||
|
card.draggable(
|
||||||
{
|
{
|
||||||
snap: false,
|
snap: false,
|
||||||
snapTolerance: 5,
|
snapTolerance: 5,
|
||||||
|
@ -182,7 +190,8 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
);
|
);
|
||||||
|
|
||||||
//After a drag:
|
//After a drag:
|
||||||
$( "#" + id ).bind( "dragstop", function(event, ui) {
|
card.bind( "dragstop", function(event, ui) {
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
position: ui.position,
|
position: ui.position,
|
||||||
|
@ -192,7 +201,7 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
sendAction('moveCard', data);
|
sendAction('moveCard', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
$( ".droppable" ).droppable(
|
card.children(".droppable").droppable(
|
||||||
{
|
{
|
||||||
accept: '.sticker',
|
accept: '.sticker',
|
||||||
drop: function( event, ui ) {
|
drop: function( event, ui ) {
|
||||||
|
@ -202,7 +211,6 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
addSticker( cardId, stickerId );
|
addSticker( cardId, stickerId );
|
||||||
|
|
||||||
var data = { cardId: cardId, stickerId: stickerId };
|
var data = { cardId: cardId, stickerId: stickerId };
|
||||||
|
|
||||||
sendAction('addSticker', data);
|
sendAction('addSticker', data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,13 +218,14 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
|
|
||||||
var speed = Math.floor(Math.random() * 1000);
|
var speed = Math.floor(Math.random() * 1000);
|
||||||
if (typeof(animationspeed) != 'undefined') speed = animationspeed;
|
if (typeof(animationspeed) != 'undefined') speed = animationspeed;
|
||||||
|
|
||||||
|
|
||||||
$("#" + id).animate({
|
card.animate({
|
||||||
left: x + "px",
|
left: x + "px",
|
||||||
top: y + "px"
|
top: y + "px"
|
||||||
}, speed);
|
}, speed);
|
||||||
|
|
||||||
$("#" + id).hover(
|
card.hover(
|
||||||
function(){
|
function(){
|
||||||
$(this).addClass('hover');
|
$(this).addClass('hover');
|
||||||
$(this).children('.card-icon').fadeIn(10);
|
$(this).children('.card-icon').fadeIn(10);
|
||||||
|
@ -227,7 +236,7 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$("#" + id).children('.card-icon').hover(
|
card.children('.card-icon').hover(
|
||||||
function(){
|
function(){
|
||||||
$(this).addClass('card-icon-hover');
|
$(this).addClass('card-icon-hover');
|
||||||
},
|
},
|
||||||
|
@ -236,7 +245,7 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$("#" + id).children('.delete-card-icon').click(
|
card.children('.delete-card-icon').click(
|
||||||
function(){
|
function(){
|
||||||
$("#" + id).remove();
|
$("#" + id).remove();
|
||||||
//notify server of delete
|
//notify server of delete
|
||||||
|
@ -244,7 +253,7 @@ function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$("#" + id).children('.content').editable( "/edit-card/" + id,
|
card.children('.content').editable( "/edit-card/" + id,
|
||||||
{
|
{
|
||||||
style : 'inherit',
|
style : 'inherit',
|
||||||
cssclass : 'card-edit-form',
|
cssclass : 'card-edit-form',
|
||||||
|
@ -274,6 +283,13 @@ function onCardChange( text, result )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveCard(card, position) {
|
||||||
|
card.animate({
|
||||||
|
left: position.left+"px",
|
||||||
|
top: position.top+"px"
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
function addSticker ( cardId , stickerId )
|
function addSticker ( cardId , stickerId )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -579,8 +595,8 @@ function updateName ( sid, name )
|
||||||
|
|
||||||
function boardResizeHappened(event, ui)
|
function boardResizeHappened(event, ui)
|
||||||
{
|
{
|
||||||
var newsize = ui.size
|
var newsize = ui.size
|
||||||
|
|
||||||
sendAction( 'setBoardSize', newsize);
|
sendAction( 'setBoardSize', newsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,6 +609,66 @@ function resizeBoard (size) {
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function calcCardOffset() {
|
||||||
|
var offsets = {};
|
||||||
|
$(".card").each(function() {
|
||||||
|
var card = $(this);
|
||||||
|
$(".col").each(function(i) {
|
||||||
|
var col = $(this);
|
||||||
|
if(col.offset().left + col.outerWidth() > card.offset().left + card.outerWidth() || i === $(".col").size() - 1) {
|
||||||
|
offsets[card.attr('id')] = {
|
||||||
|
col: col,
|
||||||
|
x: ( (card.offset().left - col.offset().left) / col.outerWidth() )
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//moves cards with a resize of the Board
|
||||||
|
//doSync is false if you don't want to synchronize
|
||||||
|
//with all the other users who are in this room
|
||||||
|
function adjustCard(offsets, doSync) {
|
||||||
|
$(".card").each(function() {
|
||||||
|
var card = $(this);
|
||||||
|
var offset = offsets[this.id];
|
||||||
|
if(offset) {
|
||||||
|
var data = {
|
||||||
|
id: this.id,
|
||||||
|
position: {
|
||||||
|
left: offset.col.position().left + (offset.x * offset.col.outerWidth()),
|
||||||
|
top: parseInt(card.css('top').slice(0,-2))
|
||||||
|
},
|
||||||
|
oldposition: {
|
||||||
|
left: parseInt(card.css('left').slice(0,-2)),
|
||||||
|
top: parseInt(card.css('top').slice(0,-2))
|
||||||
|
}
|
||||||
|
}; //use .css() instead of .position() because css' rotate
|
||||||
|
//console.log(data);
|
||||||
|
if (!doSync)
|
||||||
|
{
|
||||||
|
card.css('left',data.position.left);
|
||||||
|
card.css('top',data.position.top);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//note that in this case, data.oldposition isn't accurate since
|
||||||
|
//many moves have happened since the last sync
|
||||||
|
//but that's okay becuase oldPosition isn't used right now
|
||||||
|
moveCard(card, data.position);
|
||||||
|
sendAction('moveCard', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
if (boardInitialized == false)
|
if (boardInitialized == false)
|
||||||
|
@ -729,13 +805,44 @@ $( ".board-outline" ).resizable( {
|
||||||
minHeight: 400 ,
|
minHeight: 400 ,
|
||||||
maxWidth: 3200,
|
maxWidth: 3200,
|
||||||
maxHeight: 1800,
|
maxHeight: 1800,
|
||||||
stop: function(event, ui) {
|
|
||||||
boardResizeHappened(event, ui);
|
|
||||||
}
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
//A new scope for precalculating
|
||||||
|
(function() {
|
||||||
|
var offsets;
|
||||||
|
|
||||||
|
$(".board-outline").bind("resizestart", function() {
|
||||||
|
offsets = calcCardOffset();
|
||||||
|
});
|
||||||
|
$(".board-outline").bind("resize", function(event, ui) {
|
||||||
|
adjustCard(offsets, false);
|
||||||
|
});
|
||||||
|
$(".board-outline").bind("resizestop", function(event, ui) {
|
||||||
|
boardResizeHappened(event, ui);
|
||||||
|
adjustCard(offsets, true);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('#marker').draggable(
|
||||||
|
{
|
||||||
|
axis: 'x',
|
||||||
|
containment: 'parent'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$('#eraser').draggable(
|
||||||
|
{
|
||||||
|
axis: 'x',
|
||||||
|
containment: 'parent'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,25 @@ db.prototype = {
|
||||||
{name:room},
|
{name:room},
|
||||||
{$set:doc}
|
{$set:doc}
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
getBoardSize: function(room, callback) {
|
||||||
|
this.rooms.findOne(
|
||||||
|
{name:room},
|
||||||
|
function(err, room) {
|
||||||
|
if(room) {
|
||||||
|
callback(room.size);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setBoardSize: function(room, size) {
|
||||||
|
this.room.findOne({name:room})
|
||||||
|
this.rooms.update(
|
||||||
|
{name:room},
|
||||||
|
{$set:{'size':size}}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
exports.db = db;
|
exports.db = db;
|
||||||
|
|
40
lib/rooms.js
|
@ -22,10 +22,10 @@ var sid_rooms = {};
|
||||||
|
|
||||||
// Add a client to a room and return the sid:client mapping.
|
// Add a client to a room and return the sid:client mapping.
|
||||||
exports.add_to_room = function (client, room, callback) {
|
exports.add_to_room = function (client, room, callback) {
|
||||||
//console.log('Client ' + client.username + ' (' + client.sessionId + ') added to room ' + room);
|
//console.log('Client ' + client.username + ' (' + client.id + ') added to room ' + room);
|
||||||
|
|
||||||
if (!(sid_rooms.hasOwnProperty(client.sessionId))) sid_rooms[client.sessionId] = new sets.Set();
|
if (!(sid_rooms.hasOwnProperty(client.id))) sid_rooms[client.id] = new sets.Set();
|
||||||
sid_rooms[client.sessionId].add(room);
|
sid_rooms[client.id].add(room);
|
||||||
|
|
||||||
if (!(rooms.hasOwnProperty(room))) rooms[room] = new sets.Set();
|
if (!(rooms.hasOwnProperty(room))) rooms[room] = new sets.Set();
|
||||||
rooms[room].add(client);
|
rooms[room].add(client);
|
||||||
|
@ -40,8 +40,8 @@ exports.add_to_room = function (client, room, callback) {
|
||||||
// mapping for everybody in those rooms.
|
// mapping for everybody in those rooms.
|
||||||
exports.remove_from_all_rooms = function (client, callback) {
|
exports.remove_from_all_rooms = function (client, callback) {
|
||||||
var affected_clients = new sets.Set();
|
var affected_clients = new sets.Set();
|
||||||
if (sid_rooms.hasOwnProperty(client.sessionId)) {
|
if (sid_rooms.hasOwnProperty(client.id)) {
|
||||||
var client_rooms = sid_rooms[client.sessionId].array();
|
var client_rooms = sid_rooms[client.id].array();
|
||||||
for (var i = 0; i < client_rooms.length; i++) {
|
for (var i = 0; i < client_rooms.length; i++) {
|
||||||
var room = client_rooms[i];
|
var room = client_rooms[i];
|
||||||
if (rooms.hasOwnProperty(room)) {
|
if (rooms.hasOwnProperty(room)) {
|
||||||
|
@ -61,8 +61,8 @@ exports.remove_from_all_rooms = function (client, callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('Client ' + client.username + ' (' + client.sessionId + ') disconnected.');
|
console.log('Client ' + client.username + ' (' + client.id + ') disconnected.');
|
||||||
delete sid_rooms[client.sessionId];
|
delete sid_rooms[client.id];
|
||||||
callback(affected_clients.array());
|
callback(affected_clients.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +122,9 @@ exports.room_clients_other_than_me = function(room, client) {
|
||||||
|
|
||||||
//gets the current room of the client (assumes one room -- will select first one if in multiple)
|
//gets the current room of the client (assumes one room -- will select first one if in multiple)
|
||||||
exports.get_room = function (client) {
|
exports.get_room = function (client) {
|
||||||
if (sid_rooms.hasOwnProperty(client.sessionId))
|
if (sid_rooms.hasOwnProperty(client.id))
|
||||||
{
|
{
|
||||||
var client_rooms = sid_rooms[client.sessionId].array();
|
var client_rooms = sid_rooms[client.id].array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(client_rooms) != undefined )
|
if ( typeof(client_rooms) != undefined )
|
||||||
|
@ -143,8 +143,8 @@ exports.add_to_room_and_announce = function (client, room, msg) {
|
||||||
// Broadcast new-user notification
|
// Broadcast new-user notification
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (var i = 0; i < clients.length; i++)
|
||||||
{
|
{
|
||||||
if (clients[i].sessionId != client.sessionId)
|
if (clients[i].id != client.id)
|
||||||
clients[i].send(msg);
|
clients[i].json.send(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,8 +170,8 @@ exports.remove_from_all_rooms_and_announce = function (client, msg) {
|
||||||
exports.remove_from_all_rooms(client, function(clients) {
|
exports.remove_from_all_rooms(client, function(clients) {
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (var i = 0; i < clients.length; i++)
|
||||||
{
|
{
|
||||||
if (clients[i].sessionId != client.sessionId)
|
if (clients[i].id != client.id)
|
||||||
clients[i].send(msg);
|
clients[i].json.send(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -192,16 +192,16 @@ exports.broadcast = function(msg) {
|
||||||
exports.broadcast_room = function(room, msg) {
|
exports.broadcast_room = function(room, msg) {
|
||||||
var clients = exports.room_clients(room);
|
var clients = exports.room_clients(room);
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (var i = 0; i < clients.length; i++)
|
||||||
clients[i].send(msg);
|
clients[i].json.send(msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Broadcast message to all the other clients that are in rooms with this client
|
// Broadcast message to all the other clients that are in rooms with this client
|
||||||
exports.broadcast_to_roommates = function (client, msg) {
|
exports.broadcast_to_roommates = function (client, msg) {
|
||||||
var roommates = new sets.Set();
|
var roommates = new sets.Set();
|
||||||
|
|
||||||
if (sid_rooms.hasOwnProperty(client.sessionId))
|
if (sid_rooms.hasOwnProperty(client.id))
|
||||||
{
|
{
|
||||||
var client_rooms = sid_rooms[client.sessionId].array();
|
var client_rooms = sid_rooms[client.id].array();
|
||||||
for (var i = 0; i < client_rooms.length; i++)
|
for (var i = 0; i < client_rooms.length; i++)
|
||||||
{
|
{
|
||||||
var room = client_rooms[i];
|
var room = client_rooms[i];
|
||||||
|
@ -218,12 +218,12 @@ exports.broadcast_to_roommates = function (client, msg) {
|
||||||
roommates.remove(client);
|
roommates.remove(client);
|
||||||
roommates = roommates.array();
|
roommates = roommates.array();
|
||||||
|
|
||||||
console.log('client: ' + client.sessionId + " is broadcasting to: ");
|
console.log('client: ' + client.id + " is broadcasting to: ");
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < roommates.length; i++)
|
for (var i = 0; i < roommates.length; i++)
|
||||||
{
|
{
|
||||||
console.log(' - ' + roommates[i].sessionId);
|
console.log(' - ' + roommates[i].id);
|
||||||
roommates[i].send(msg);
|
roommates[i].json.send(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
package.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "scrumblr",
|
||||||
|
"description": "Web-based simulation of a physical agile sprint board that supports real-time collaboration.",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"repository": {
|
||||||
|
"url": "http://github.com/aliasaria/scrumblr"
|
||||||
|
},
|
||||||
|
"author": "Ali Asaria",
|
||||||
|
"main": "server.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib/"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "0.4.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"async": "0.1.x",
|
||||||
|
"connect": "1.7.x",
|
||||||
|
"redis-client": "0.3.x",
|
||||||
|
"redis": "0.6.x",
|
||||||
|
"sanitizer": "0.0.x",
|
||||||
|
"socket.io": "0.8.x",
|
||||||
|
"simplesets": "1.1.x",
|
||||||
|
"connect-redis":"1.0.x",
|
||||||
|
"express": "2.4.x",
|
||||||
|
"jade": "0.14.x"
|
||||||
|
}
|
||||||
|
}
|
54
server.js
|
@ -1,5 +1,4 @@
|
||||||
var http = require('http'),
|
var http = require('http'),
|
||||||
io = require('socket.io'), // for npm, otherwise use require('./path/to/socket.io')
|
|
||||||
express = require('express'),
|
express = require('express'),
|
||||||
connect = require('connect');
|
connect = require('connect');
|
||||||
|
|
||||||
|
@ -37,8 +36,11 @@ app.configure( function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', function(req, res) {
|
app.get('/', function(req, res) {
|
||||||
|
console.log(req.header('host'));
|
||||||
|
url = req.header('host');
|
||||||
res.render('home.jade', {
|
res.render('home.jade', {
|
||||||
layout: false
|
layout: false,
|
||||||
|
locals: {url: url}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,17 +68,25 @@ app.post('/edit-column', function(req, res) {
|
||||||
res.send(req.body.value);
|
res.send(req.body.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.listen(process.argv[2]);
|
app.listen(process.argv[2] || 8124);
|
||||||
|
|
||||||
//I limit the number of potential transports because xhr was causing trouble
|
//I limit the number of potential transports because xhr was causing trouble
|
||||||
//with frequent disconnects
|
//with frequent disconnects
|
||||||
var socketio_options = {
|
var socketio_options = {
|
||||||
transports: ['websocket', 'flashsocket', 'htmlfile', 'jsonp-polling']
|
transports: ['websocket', 'flashsocket', 'htmlfile', 'jsonp-polling']
|
||||||
};
|
};
|
||||||
|
|
||||||
// socket.io SETUP
|
// socket.io SETUP
|
||||||
var socket = io.listen(app, socketio_options);
|
var io = require('socket.io').listen(app);
|
||||||
socket.on('connection', function(client){
|
io.configure(function () {
|
||||||
|
io.set('transports', [
|
||||||
|
'websocket'
|
||||||
|
, 'flashsocket'
|
||||||
|
, 'htmlfile'
|
||||||
|
// , 'xhr-polling'
|
||||||
|
, 'jsonp-polling'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
io.sockets.on('connection', function (client) {
|
||||||
// new client is here!
|
// new client is here!
|
||||||
//console.dir(client.request.headers);
|
//console.dir(client.request.headers);
|
||||||
//
|
//
|
||||||
|
@ -127,7 +137,7 @@ function scrub( text ) {
|
||||||
|
|
||||||
joinRoom(client, message.data, function(clients) {
|
joinRoom(client, message.data, function(clients) {
|
||||||
|
|
||||||
client.send( { action: 'roomAccept', data: '' } );
|
client.json.send( { action: 'roomAccept', data: '' } );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -278,7 +288,7 @@ function scrub( text ) {
|
||||||
|
|
||||||
var msg = {};
|
var msg = {};
|
||||||
msg.action = 'nameChangeAnnounce';
|
msg.action = 'nameChangeAnnounce';
|
||||||
msg.data = { sid: client.sessionId, user_name: clean_message.data };
|
msg.data = { sid: client.id, user_name: clean_message.data };
|
||||||
broadcastToRoom( client, msg );
|
broadcastToRoom( client, msg );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -333,7 +343,7 @@ function initClient ( client )
|
||||||
|
|
||||||
db.getAllCards( room , function (cards) {
|
db.getAllCards( room , function (cards) {
|
||||||
|
|
||||||
client.send(
|
client.json.send(
|
||||||
{
|
{
|
||||||
action: 'initCards',
|
action: 'initCards',
|
||||||
data: cards
|
data: cards
|
||||||
|
@ -344,7 +354,7 @@ function initClient ( client )
|
||||||
|
|
||||||
|
|
||||||
db.getAllColumns ( room, function (columns) {
|
db.getAllColumns ( room, function (columns) {
|
||||||
client.send(
|
client.json.send(
|
||||||
{
|
{
|
||||||
action: 'initColumns',
|
action: 'initColumns',
|
||||||
data: columns
|
data: columns
|
||||||
|
@ -357,7 +367,7 @@ function initClient ( client )
|
||||||
|
|
||||||
if (theme == null) theme = 'bigcards';
|
if (theme == null) theme = 'bigcards';
|
||||||
|
|
||||||
client.send(
|
client.json.send(
|
||||||
{
|
{
|
||||||
action: 'changeTheme',
|
action: 'changeTheme',
|
||||||
data: theme
|
data: theme
|
||||||
|
@ -368,7 +378,7 @@ function initClient ( client )
|
||||||
db.getBoardSize( room, function(size) {
|
db.getBoardSize( room, function(size) {
|
||||||
|
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
client.send(
|
client.json.send(
|
||||||
{
|
{
|
||||||
action: 'setBoardSize',
|
action: 'setBoardSize',
|
||||||
data: size
|
data: size
|
||||||
|
@ -383,18 +393,18 @@ function initClient ( client )
|
||||||
var j = 0;
|
var j = 0;
|
||||||
for (i in roommates_clients)
|
for (i in roommates_clients)
|
||||||
{
|
{
|
||||||
if (roommates_clients[i].sessionId != client.sessionId)
|
if (roommates_clients[i].id != client.id)
|
||||||
{
|
{
|
||||||
roommates[j] = {
|
roommates[j] = {
|
||||||
sid: roommates_clients[i].sessionId,
|
sid: roommates_clients[i].id,
|
||||||
user_name: sids_to_user_names[roommates_clients[i].sessionId]
|
user_name: sids_to_user_names[roommates_clients[i].id]
|
||||||
};
|
};
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('initialusers: ' + roommates);
|
console.log('initialusers: ' + roommates);
|
||||||
client.send(
|
client.json.send(
|
||||||
{
|
{
|
||||||
action: 'initialUsers',
|
action: 'initialUsers',
|
||||||
data: roommates
|
data: roommates
|
||||||
|
@ -409,7 +419,7 @@ function joinRoom (client, room, successFunction)
|
||||||
{
|
{
|
||||||
var msg = {};
|
var msg = {};
|
||||||
msg.action = 'join-announce';
|
msg.action = 'join-announce';
|
||||||
msg.data = { sid: client.sessionId, user_name: client.user_name };
|
msg.data = { sid: client.id, user_name: client.user_name };
|
||||||
|
|
||||||
rooms.add_to_room_and_announce(client, room, msg);
|
rooms.add_to_room_and_announce(client, room, msg);
|
||||||
successFunction();
|
successFunction();
|
||||||
|
@ -417,13 +427,13 @@ function joinRoom (client, room, successFunction)
|
||||||
|
|
||||||
function leaveRoom (client)
|
function leaveRoom (client)
|
||||||
{
|
{
|
||||||
console.log (client.sessionId + ' just left');
|
console.log (client.id + ' just left');
|
||||||
var msg = {};
|
var msg = {};
|
||||||
msg.action = 'leave-announce';
|
msg.action = 'leave-announce';
|
||||||
msg.data = { sid: client.sessionId };
|
msg.data = { sid: client.id };
|
||||||
rooms.remove_from_all_rooms_and_announce(client, msg);
|
rooms.remove_from_all_rooms_and_announce(client, msg);
|
||||||
|
|
||||||
delete sids_to_user_names[client.sessionId];
|
delete sids_to_user_names[client.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastToRoom ( client, message ) {
|
function broadcastToRoom ( client, message ) {
|
||||||
|
@ -457,7 +467,7 @@ function roundRand( max )
|
||||||
function getRoom( client , callback )
|
function getRoom( client , callback )
|
||||||
{
|
{
|
||||||
room = rooms.get_room( client );
|
room = rooms.get_room( client );
|
||||||
//console.log( 'client: ' + client.sessionId + " is in " + room);
|
//console.log( 'client: ' + client.id + " is in " + room);
|
||||||
callback(room);
|
callback(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +475,7 @@ function getRoom( client , callback )
|
||||||
function setUserName ( client, name )
|
function setUserName ( client, name )
|
||||||
{
|
{
|
||||||
client.user_name = name;
|
client.user_name = name;
|
||||||
sids_to_user_names[client.sessionId] = name;
|
sids_to_user_names[client.id] = name;
|
||||||
console.log('sids to user names: ');
|
console.log('sids to user names: ');
|
||||||
console.dir(sids_to_user_names);
|
console.dir(sids_to_user_names);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ body
|
||||||
a#go(onclick="return go();") go.
|
a#go(onclick="return go();") go.
|
||||||
<br><br>
|
<br><br>
|
||||||
p.home example board:
|
p.home example board:
|
||||||
p.home <a href="http://scrumblr.ca/demo">http://scrumblr.ca/demo</a>
|
p.home!= '<a href="http://' + locals.url + '/demo">' + locals.url + '/demo</a>'
|
||||||
<br><br><br>
|
<br><br><br>
|
||||||
p.home.small sourcecode at <a href="https://github.com/aliasaria/scrumblr">github</a>
|
p.home.small sourcecode at <a href="https://github.com/aliasaria/scrumblr">github</a>
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ h1 <a href="http://scrumblr.ca">scrumblr</a> by <a href="http://aliasaria.ca">al
|
||||||
div.board-outline
|
div.board-outline
|
||||||
div#board
|
div#board
|
||||||
div#board-doodles
|
div#board-doodles
|
||||||
|
image#marker(src='/images/marker.png')
|
||||||
|
image#eraser(src='/images/eraser.png')
|
||||||
|
|
||||||
|
|
||||||
table#board-table.board-table
|
table#board-table.board-table
|
||||||
|
|
|
@ -6,7 +6,9 @@ html(lang="en")
|
||||||
|
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
<script src="/lib/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script>
|
<!-- <script src="/lib/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script> -->
|
||||||
|
<script src="/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.9.custom.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="/lib/jquery.jeditable.js"></script>
|
<script src="/lib/jquery.jeditable.js"></script>
|
||||||
|
|
||||||
|
|