var cards = {}; var totalcolumns = 0; var columns = []; var currentTheme = "bigcards"; var boardInitialized = false; var keyTrap = null; var socket = io.connect(); //an action has happened, send it to the //server function sendAction(a, d) { //console.log('--> ' + a); var message = { action: a, data: d } socket.json.send ( message ); } socket.on('connect', function(){ //console.log('successful socket.io connect'); //let the path be the room name var path = location.pathname; //imediately join the room which will trigger the initializations sendAction('joinRoom', path); }) socket.on('disconnect', function(){ blockUI("Server disconnected. Refresh page to try and reconnect..."); //$('.blockOverlay').click($.unblockUI); }); socket.on('message', function(data){ getMessage(data); }) function unblockUI() { $.unblockUI(); } function blockUI(message) { message = message || 'Waiting...'; $.blockUI({ message: message, css: { border: 'none', padding: '15px', backgroundColor: '#000', '-webkit-border-radius': '10px', '-moz-border-radius': '10px', opacity: .5, color: '#fff', fontSize: '20px' } }); } //respond to an action event function getMessage( m ) { var message = m; //JSON.parse(m); var action = message.action; var data = message.data; //console.log('<-- ' + action); switch (action) { case 'roomAccept': //okay we're accepted, then request initialization //(this is a bit of unnessary back and forth but that's okay for now) sendAction('initializeMe', null); break; case 'roomDeny': //this doesn't happen yet break; case 'moveCard': moveCard($("#" + data.id), data.position); break; case 'initCards': initCards(data); break; case 'createCard': //console.log(data); drawNewCard(data.id, data.text, data.x, data.y, data.rot, data.colour, null); break; case 'deleteCard': $("#" + data.id).fadeOut(500, function() {$(this).remove();} ); break; case 'editCard': $("#" + data.id).children('.content:first').text(data.value); break; case 'initColumns': initColumns(data); break; case 'updateColumns': initColumns(data); break; case 'changeTheme': changeThemeTo(data); break; case 'join-announce': displayUserJoined(data.sid, data.user_name); break; case 'leave-announce': displayUserLeft(data.sid); break; case 'initialUsers': displayInitialUsers(data); break; case 'nameChangeAnnounce': updateName( message.data.sid, message.data.user_name ); break; case 'addSticker': addSticker( message.data.cardId, message.data.stickerId ); break; case 'setBoardSize': resizeBoard( message.data ); break; default: //unknown message alert('unknown action: ' + JSON.stringify(message)); break; } } $(document).bind('keyup', function(event) { keyTrap = event.which; }); function drawNewCard(id, text, x, y, rot, colour, sticker, animationspeed) { //cards[id] = {id: id, text: text, x: x, y: y, rot: rot, colour: colour}; var h = '
\ \ \
' + text + '
\
'; var card = $(h); card.appendTo('#board'); //@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, snapTolerance: 5, containment: [0,0,2000,2000], stack: ".card", start: function (event, ui) { keyTrap = null; }, drag: function (event, ui) { if (keyTrap == 27) { ui.helper.css(ui.originalPosition); return false; } } } ); //After a drag: card.bind( "dragstop", function(event, ui) { if (keyTrap == 27) { keyTrap = null; return; } var data = { id: this.id, position: ui.position, oldposition: ui.originalPosition, }; sendAction('moveCard', data); }); card.children(".droppable").droppable( { accept: '.sticker', drop: function( event, ui ) { var stickerId = ui.draggable.attr("id"); var cardId = $(this).parent().attr('id'); addSticker( cardId, stickerId ); var data = { cardId: cardId, stickerId: stickerId }; sendAction('addSticker', data); //remove hover state to everything on the board to prevent //a jquery bug where it gets left around $('.card-hover-draggable').removeClass('card-hover-draggable'); }, hoverClass: 'card-hover-draggable' } ); var speed = Math.floor(Math.random() * 1000); if (typeof(animationspeed) != 'undefined') speed = animationspeed; card.animate({ left: x + "px", top: y + "px" }, speed); card.hover( function(){ $(this).addClass('hover'); $(this).children('.card-icon').fadeIn(10); }, function(){ $(this).removeClass('hover'); $(this).children('.card-icon').fadeOut(150); } ); card.children('.card-icon').hover( function(){ $(this).addClass('card-icon-hover'); }, function(){ $(this).removeClass('card-icon-hover'); } ); card.children('.delete-card-icon').click( function(){ $("#" + id).remove(); //notify server of delete sendAction( 'deleteCard' , { 'id': id }); } ); card.children('.content').editable(function(value, settings) { onCardChange( id, value ); return(value); }, { type : 'textarea', submit : 'OK', style : 'inherit', cssclass : 'card-edit-form', type : 'textarea', placeholder : 'Double Click to Edit.', onblur: 'submit', event: 'dblclick', //event: 'mouseover' }); //add applicable sticker if (sticker != null) $("#" + id).children('.content').addClass( sticker ); } function onCardChange( id, text ) { sendAction('editCard', { id: id, value: text }); } function moveCard(card, position) { card.animate({ left: position.left+"px", top: position.top+"px" }, 500); } function addSticker ( cardId , stickerId ) { cardContent = $('#' + cardId).children('.content'); cardContent.removeClass("sticker-red"); cardContent.removeClass("sticker-blue"); cardContent.removeClass("sticker-green"); cardContent.removeClass("sticker-yellow"); cardContent.removeClass("sticker-gold"); cardContent.removeClass("sticker-silverstar"); cardContent.removeClass("sticker-bluestar"); cardContent.removeClass("sticker-redstar"); cardContent.removeClass("sticker-orange"); cardContent.removeClass("sticker-pink"); cardContent.removeClass("sticker-purple"); cardContent.removeClass("sticker-lightblue"); cardContent.addClass( stickerId ); } //---------------------------------- // cards //---------------------------------- function createCard( id, text, x, y, rot, colour ) { drawNewCard(id, text, x, y, rot, colour, null); var action = "createCard"; var data = { id: id, text: text, x: x, y: y, rot: rot, colour: colour }; sendAction(action, data); } function randomCardColour() { var colours = ['yellow', 'green', 'blue', 'white']; var i = Math.floor(Math.random() * colours.length); return colours[i]; } function initCards( cardArray ) { //first delete any cards that exist $('.card').remove(); cards = cardArray; for (i in cardArray) { card = cardArray[i]; drawNewCard( card.id, card.text, card.x, card.y, card.rot, card.colour, card.sticker ); } boardInitialized = true; unblockUI(); } //---------------------------------- // cols //---------------------------------- function drawNewColumn (columnName) { var cls = "col"; if (totalcolumns == 0) { cls = "col first"; } $('#icon-col').before('

' + columnName + '

'); $('.editable').editable(function(value, settings) { onColumnChange( this.id, value ); return(value); }, { style : 'inherit', cssclass : 'card-edit-form', type : 'textarea', placeholder : 'New', onblur: 'submit', width: '', height: '', xindicator: '', event: 'dblclick', //event: 'mouseover' }); $('.col:last').fadeIn(1500); totalcolumns ++; } function onColumnChange( id, text ) { var names = Array(); //Get the names of all the columns right from the DOM (ignore what was sent in function) $('.col').each(function() { names.push( $(this).text() ); }); updateColumns(names); } function displayRemoveColumn() { if (totalcolumns <= 0) return false; $('.col:last').fadeOut( 150, function() { $(this).remove(); } ); totalcolumns --; } function createColumn( name ) { if (totalcolumns >= 8) return false; drawNewColumn( name ); columns.push(name); var action = "updateColumns"; var data = columns; sendAction(action, data); } function deleteColumn() { if (totalcolumns <= 0) return false; displayRemoveColumn(); columns.pop(); var action = "updateColumns"; var data = columns; sendAction(action, data); } function updateColumns( c ) { columns = c; var action = "updateColumns"; var data = columns; sendAction(action, data); } function deleteColumns( next ) { //delete all existing columns: $('.col').fadeOut( 'slow', next() ); } function initColumns( columnArray ) { totalcolumns = 0; columns = columnArray; $('.col').remove(); for (i in columnArray) { column = columnArray[i]; drawNewColumn( column ); } } function changeThemeTo( theme ) { currentTheme = theme; $("link[title=cardsize]").attr("href", "/css/" + theme + ".css"); } ////////////////////////////////////////////////////////// ////////// NAMES STUFF /////////////////////////////////// ////////////////////////////////////////////////////////// function setCookie(c_name,value,exdays) { var exdate=new Date(); exdate.setDate(exdate.getDate() + exdays); var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString()); document.cookie=c_name + "=" + c_value; } function getCookie(c_name) { var i,x,y,ARRcookies=document.cookie.split(";"); for (i=0;i' + name + '') } function displayUserLeft ( sid ) { name = ''; if (name) name = user_name; else name = sid; var id = '#user-' + sid.toString(); $('#names-ul').children(id).fadeOut( 1000 , function() { $(this).remove(); }); } function updateName ( sid, name ) { var id = '#user-' + sid.toString(); $('#names-ul').children(id).text( name ); } ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// function boardResizeHappened(event, ui) { var newsize = ui.size sendAction( 'setBoardSize', newsize); } function resizeBoard (size) { $( ".board-outline" ).animate( { height: size.height, width: size.width } ); } ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// 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() { if (boardInitialized == false) blockUI(''); //setTimeout($.unblockUI, 2000); $( "#create-card" ) .click(function() { var rotation = Math.random() * 10 - 5; //add a bit of random rotation (+/- 10deg) uniqueID = Math.round(Math.random()*99999999); //is this big enough to assure uniqueness? //alert(uniqueID); createCard( 'card' + uniqueID, '', 58, $('div.board-outline').height(),// hack - not a great way to get the new card coordinates, but most consistant ATM rotation, randomCardColour()); }); // Style changer $("#smallify").click(function(){ if (currentTheme == "bigcards") { changeThemeTo('smallcards'); } else if (currentTheme == "smallcards") { changeThemeTo('bigcards'); } /*else if (currentTheme == "nocards") { currentTheme = "bigcards"; $("link[title=cardsize]").attr("href", "css/bigcards.css"); }*/ sendAction('changeTheme', currentTheme); return false; }); $('#icon-col').hover( function() { $('.col-icon').fadeIn(10); }, function() { $('.col-icon').fadeOut(150); } ); $('#add-col').click( function(){ createColumn('New'); return false; } ); $('#delete-col').click( function(){ deleteColumn(); return false; } ); // $('#cog-button').click( function(){ // $('#config-dropdown').fadeToggle(); // } ); // $('#config-dropdown').hover( // function(){ /*$('#config-dropdown').fadeIn()*/ }, // function(){ $('#config-dropdown').fadeOut() } // ); // var user_name = getCookie('scrumscrum-username'); $("#yourname-input").focus(function() { if ($(this).val() == 'unknown') { $(this).val(""); } $(this).addClass('focused'); }); $("#yourname-input").blur(function() { if ($(this).val() == "") { $(this).val('unknown'); } $(this).removeClass('focused'); setName($(this).val()); }); $("#yourname-input").val(user_name); $("#yourname-input").blur(); $("#yourname-li").hide(); $("#yourname-input").keypress(function(e) { code= (e.keyCode ? e.keyCode : e.which); if (code == 10 || code == 13) { $(this).blur(); } }); $( ".sticker" ).draggable({ revert: true, zIndex: 1000 }); $( ".board-outline" ).resizable( { ghost: false, minWidth: 700, minHeight: 400 , maxWidth: 3200, maxHeight: 1800, } ); //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' } ); //disable image dragging window.ondragstart = function() { return false; } });