var whiteboard = { canvas : null, ctx : null, drawcolor : "black", tool : "pen", thickness : 4, prevX : null, prevY : null, drawFlag : false, oldGCO : null, mouseover : false, lineCap : "round", //butt, square backgroundGrid : null, canvasElement : null, cursorContainer : null, imgContainer : null, svgContainer : null, //For draw prev mouseOverlay : null, drawBuffer : [], drawId : 0, //Used for undo function imgDragActive : false, settings : { whiteboardId : "0", username : "defaultuser", sendFunction : null, canvasWidth : 3000, canvasHeight : 2000, backgroundGridUrl : './img/KtEBa2.png' }, loadWhiteboard : function(whiteboardContainer, newSettings) { var svgns = "http://www.w3.org/2000/svg"; var _this = this; for(var i in newSettings) { this.settings[i] = newSettings[i]; } var startCoords = []; var svgLine = null; var svgRect = null; var svgCirle = null; var latestTouchCoods = null; //Background _this.backgroundGrid = $('
'); _this.imgContainer = $('
'); //Canvas _this.canvasElement = $(''); _this.svgContainer = $(''); _this.cursorContainer = $('
'); _this.dropIndicator = $('
') _this.mouseOverlay = $('
'); $(whiteboardContainer).append(_this.backgroundGrid).append(_this.imgContainer).append( _this.canvasElement).append(_this.svgContainer).append(_this.dropIndicator).append(_this.cursorContainer).append(this.mouseOverlay); this.canvas = $("#whiteboardCanvas")[0]; this.canvas.height = _this.settings.canvasHeight; this.canvas.width = _this.settings.canvasWidth; this.ctx = this.canvas.getContext("2d"); this.oldGCO = this.ctx.globalCompositeOperation; $(_this.mouseOverlay).on("mousedown touchstart", function(e) { if(_this.imgDragActive) { return; } _this.drawFlag = true; _this.prevX = (e.offsetX || e.pageX - $(e.target).offset().left); _this.prevY = (e.offsetY || e.pageY - $(e.target).offset().top); if(!_this.prevX || !_this.prevY) { var touche= e.touches[0]; _this.prevX = touche.clientX - $(_this.mouseOverlay).offset().left; _this.prevY = touche.clientY - $(_this.mouseOverlay).offset().top; latestTouchCoods = [_this.prevX, _this.prevY]; } if(_this.tool==="pen") { _this.drawPenLine(_this.prevX, _this.prevY, _this.prevX, _this.prevY, _this.drawcolor, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[_this.prevX, _this.prevY, _this.prevX, _this.prevY], "c":_this.drawcolor, "th":_this.thickness}); } else if(_this.tool==="eraser") { _this.drawEraserLine(_this.prevX, _this.prevY, _this.prevX, _this.prevY, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[_this.prevX, _this.prevY, _this.prevX, _this.prevY], "th":_this.thickness}); } else if(_this.tool==="line") { startCoords = [_this.prevX, _this.prevY]; svgLine = document.createElementNS(svgns,'line'); svgLine.setAttribute('stroke','gray'); svgLine.setAttribute('stroke-dasharray','5, 5'); svgLine.setAttribute('x1',_this.prevX); svgLine.setAttribute('y1',_this.prevY); svgLine.setAttribute('x2',_this.prevX+1); svgLine.setAttribute('y2',_this.prevY+1); _this.svgContainer.append(svgLine); } else if(_this.tool==="rect" || _this.tool==="recSelect") { _this.svgContainer.find("rect").remove(); svgRect = document.createElementNS(svgns,'rect'); svgRect.setAttribute('stroke','gray'); svgRect.setAttribute('stroke-dasharray','5, 5'); svgRect.setAttribute('style','fill-opacity:0.0;'); svgRect.setAttribute('x', _this.prevX); svgRect.setAttribute('y', _this.prevY); svgRect.setAttribute('width',0); svgRect.setAttribute('height',0); _this.svgContainer.append(svgRect); startCoords = [_this.prevX, _this.prevY]; } else if(_this.tool==="circle") { svgCirle = document.createElementNS(svgns,'circle'); svgCirle.setAttribute('stroke','gray'); svgCirle.setAttribute('stroke-dasharray','5, 5'); svgCirle.setAttribute('style','fill-opacity:0.0;'); svgCirle.setAttribute('cx', _this.prevX); svgCirle.setAttribute('cy', _this.prevY); svgCirle.setAttribute('r', 0); _this.svgContainer.append(svgCirle); startCoords = [_this.prevX, _this.prevY]; } }); $(_this.mouseOverlay).on("mousemove touchmove", function(e) { e.preventDefault(); if(_this.imgDragActive) { return; } var currX = (e.offsetX || e.pageX - $(e.target).offset().left); var currY = (e.offsetY || e.pageY - $(e.target).offset().top); if((!currX || !currY) && e.touches && e.touches[0]) { var touche= e.touches[0]; currX = touche.clientX - $(_this.mouseOverlay).offset().left; currY = touche.clientY - $(_this.mouseOverlay).offset().top; latestTouchCoods = [currX, currY]; } if(_this.drawFlag) { if(_this.tool==="pen") { _this.drawPenLine(currX, currY, _this.prevX, _this.prevY, _this.drawcolor, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[currX, currY, _this.prevX, _this.prevY], "c":_this.drawcolor, "th":_this.thickness}); } else if(_this.tool=="eraser") { _this.drawEraserLine(currX, currY, _this.prevX, _this.prevY, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[currX, currY, _this.prevX, _this.prevY], "th":_this.thickness}); } _this.prevX =currX; _this.prevY =currY; } if(_this.tool==="eraser") { var left = currX-_this.thickness; var top = currY-_this.thickness; _this.cursorContainer.find("#ownCursor").css({"top":top+"px", "left":left+"px"}); } else if(_this.tool==="pen") { var left = currX-_this.thickness/2; var top = currY-_this.thickness/2; _this.cursorContainer.find("#ownCursor").css({"top":top+"px", "left":left+"px"}); } else if(_this.tool==="line") { if(svgLine) { if(shiftPressed) { var angs = getRoundedAngles(currX, currY); currX = angs.x; currY = angs.y; } svgLine.setAttribute('x2',currX); svgLine.setAttribute('y2',currY); } } else if(_this.tool==="rect" || (_this.tool==="recSelect" && _this.drawFlag)) { if(svgRect) { var width = Math.abs(currX-startCoords[0]); var height = Math.abs(currY-startCoords[1]); if(shiftPressed) { height=width; var x = currX0) { currY = startCoords[1]+(currX-startCoords[0]); } else { currY = startCoords[1]-(currX-startCoords[0]); } } _this.drawRec(startCoords[0], startCoords[1], currX, currY, _this.drawcolor, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[startCoords[0], startCoords[1], currX, currY], "c":_this.drawcolor, "th":_this.thickness}); _this.svgContainer.find("rect").remove(); } else if(_this.tool==="circle") { var a = currX - startCoords[0]; var b = currY - startCoords[1]; var r = Math.sqrt( a*a + b*b ); _this.drawCircle(startCoords[0], startCoords[1], r, _this.drawcolor, _this.thickness); _this.sendFunction({"t":_this.tool,"d":[startCoords[0], startCoords[1], r], "c":_this.drawcolor, "th":_this.thickness}); _this.svgContainer.find("circle").remove(); } else if(_this.tool==="recSelect") { _this.imgDragActive = true; if(shiftPressed) { if((currY-startCoords[1])*(currX-startCoords[0])>0) { currY = startCoords[1]+(currX-startCoords[0]); } else { currY = startCoords[1]-(currX-startCoords[0]); } } var width = Math.abs(startCoords[0] - currX); var height = Math.abs(startCoords[1] - currY); var left = startCoords[0]< currX ? startCoords[0] : currX; var top = startCoords[1]< currY ? startCoords[1] : currY; _this.mouseOverlay.css({"cursor":"default"}); var imgDiv = $('
'+ ''+ '
'+ ' '+ ''+ '
'+ '
'); var dragCanvas = $(imgDiv).find("canvas"); var dragOutOverlay = $('
'); _this.mouseOverlay.append(dragOutOverlay); _this.mouseOverlay.append(imgDiv); var destCanvasContext = dragCanvas[0].getContext('2d'); destCanvasContext.drawImage(_this.canvas,left,top,width,height,0,0,width,height); imgDiv.find(".xCanvasBtn").click(function() { _this.imgDragActive = false; if(_this.tool==="pen") _this.mouseOverlay.css({"cursor":"none"}); else _this.mouseOverlay.css({"cursor":"crosshair"}); imgDiv.remove(); dragOutOverlay.remove(); }); imgDiv.find(".addToCanvasBtn").click(function() { _this.imgDragActive = false; if(_this.tool==="pen") _this.mouseOverlay.css({"cursor":"none"}); else _this.mouseOverlay.css({"cursor":"crosshair"}); var widthT = imgDiv.width(); var heightT = imgDiv.height(); var p = imgDiv.position(); var leftT = Math.round(p.left*100)/100; var topT = Math.round(p.top*100)/100; //xf, yf, xt, yt, width, height _this.drawId++; _this.sendFunction({"t":_this.tool,"d":[left, top,leftT,topT,width,height]}); _this.dragCanvasRectContent(left, top,leftT,topT,width,height); imgDiv.remove(); dragOutOverlay.remove(); }); imgDiv.draggable(); _this.svgContainer.find("rect").remove(); } }); $(_this.mouseOverlay).on("mouseout", function(e) { if(_this.imgDragActive) { return; } _this.drawFlag=false; _this.mouseover = false; _this.ctx.globalCompositeOperation = _this.oldGCO; _this.cursorContainer.find("#ownCursor").remove(); _this.svgContainer.find("line").remove(); _this.svgContainer.find("rect").remove(); _this.svgContainer.find("circle").remove(); _this.sendFunction({"t":"cursor","event":"out"}); }); $(_this.mouseOverlay).on("mouseover", function(e) { if(_this.imgDragActive) { return; } if(!_this.mouseover) { var color = _this.drawcolor; var widthHeight = _this.thickness; if(_this.tool==="eraser") { color = "#00000000"; widthHeight = widthHeight * 2; } if(_this.tool==="eraser" || _this.tool==="pen") { _this.cursorContainer.append('
'); } } _this.mouseover = true; }); var strgPressed = false; var zPressed = false; var shiftPressed = false; $(document).on("keydown", function(e) { if(e.which == 17) { strgPressed = true; } else if(e.which == 90) { if(strgPressed && !zPressed) { _this.undoWhiteboardClick(); } zPressed = true; } else if(e.which == 16) { shiftPressed = true; } else if(e.which == 27) { //Esc if(!_this.drawFlag) _this.svgContainer.empty(); _this.mouseOverlay.find(".xCanvasBtn").click(); //Remove all current drops } else if(e.which == 46) { //Remove / Entf $.each(_this.mouseOverlay.find(".dragOutOverlay"), function() { var width = $(this).width(); var height = $(this).height(); var p = $(this).position(); var left = Math.round(p.left*100)/100; var top = Math.round(p.top*100)/100; _this.drawId++; _this.sendFunction({"t":"eraseRec","d":[left, top,width,height]}); _this.eraseRec(left, top, width, height); }); _this.mouseOverlay.find(".xCanvasBtn").click(); //Remove all current drops } //console.log(e.which); }); $(document).on("keyup", function(e) { if(e.which == 17) { strgPressed = false; } else if(e.which == 90) { zPressed = false; } else if(e.which == 16) { shiftPressed = false; } }); function getRoundedAngles(currX, currY) { //For drawing lines at 0,45,90° .... var x = currX-startCoords[0]; var y = currY-startCoords[1]; var angle = Math.atan2(x, y)* (180 / Math.PI); var angle45 = Math.round(angle/45)*45; if(angle45%90==0) { if(Math.abs(currX-startCoords[0])>Math.abs(currY-startCoords[1])) { currY = startCoords[1] } else { currX = startCoords[0] } } else { if((currY-startCoords[1])*(currX-startCoords[0])>0) { currX = startCoords[0]+(currY-startCoords[1]); } else { currX = startCoords[0]-(currY-startCoords[1]); } } return { "x": currX, "y" : currY}; } }, dragCanvasRectContent : function(xf, yf, xt, yt, width, height) { var tempCanvas = document.createElement('canvas'); tempCanvas.width = width; tempCanvas.height = height; var tempCanvasContext = tempCanvas.getContext('2d'); tempCanvasContext.drawImage(this.canvas,xf,yf,width,height,0,0,width,height); this.eraseRec(xf,yf,width,height); this.ctx.drawImage(tempCanvas,xt,yt); }, eraseRec : function(fromX, fromY, width, height) { var _this = this; _this.ctx.beginPath(); _this.ctx.rect(fromX,fromY,width,height); _this.ctx.fillStyle = "rgba(0,0,0,1)"; _this.ctx.globalCompositeOperation = "destination-out"; _this.ctx.fill(); _this.ctx.closePath(); _this.ctx.globalCompositeOperation = _this.oldGCO; }, drawPenLine : function(fromX, fromY, toX, toY, color, thickness) { var _this = this; _this.ctx.beginPath(); _this.ctx.moveTo(fromX, fromY); _this.ctx.lineTo(toX, toY); _this.ctx.strokeStyle = color; _this.ctx.lineWidth = thickness; _this.ctx.lineCap = _this.lineCap; _this.ctx.stroke(); _this.ctx.closePath(); }, drawEraserLine : function(fromX, fromY, toX, toY, thickness) { var _this = this; _this.ctx.beginPath(); _this.ctx.moveTo(fromX, fromY); _this.ctx.lineTo(toX, toY); _this.ctx.strokeStyle = "rgba(0,0,0,1)"; _this.ctx.lineWidth = thickness*2; _this.ctx.lineCap = _this.lineCap; _this.ctx.globalCompositeOperation = "destination-out"; _this.ctx.stroke(); _this.ctx.closePath(); _this.ctx.globalCompositeOperation = _this.oldGCO; }, drawRec : function(fromX, fromY, toX, toY, color, thickness) { var _this = this; toX = toX - fromX; toY = toY - fromY; _this.ctx.beginPath(); _this.ctx.rect(fromX,fromY,toX,toY); _this.ctx.strokeStyle = color; _this.ctx.lineWidth = thickness; _this.ctx.lineCap = _this.lineCap; _this.ctx.stroke(); _this.ctx.closePath(); }, drawCircle : function(fromX, fromY, radius, color, thickness) { var _this = this; _this.ctx.beginPath(); _this.ctx.arc(fromX, fromY, radius, 0, 2 * Math.PI, false); _this.ctx.lineWidth = thickness; _this.ctx.strokeStyle = color; _this.ctx.stroke(); }, clearWhiteboard : function() { var _this = this; _this.canvas.height = _this.canvas.height; _this.imgContainer.empty(); _this.sendFunction({"t":"clear"}); _this.drawBuffer = []; _this.drawId = 0; }, addImgToCanvasByUrl : function(url) { var _this = this; _this.imgDragActive = true; _this.mouseOverlay.css({"cursor":"default"}); var imgDiv = $('
'+ ''+ '
'+ ' '+ ' '+ ''+ '
'+ ''+ '
'); imgDiv.find(".xCanvasBtn").click(function() { _this.imgDragActive = false; if(_this.tool==="pen") { _this.mouseOverlay.css({"cursor":"none"}); } else if(_this.tool==="mouse") { _this.mouseOverlay.css({"cursor":"auto"}); } else { _this.mouseOverlay.css({"cursor":"crosshair"}); } imgDiv.remove(); }); imgDiv.find(".addToCanvasBtn").click(function() { var draw = $(this).attr("draw"); _this.imgDragActive = false; if(_this.tool==="pen") { _this.mouseOverlay.css({"cursor":"none"}); } else if(_this.tool==="mouse") { _this.mouseOverlay.css({"cursor":"auto"}); } else { _this.mouseOverlay.css({"cursor":"crosshair"}); } var width = imgDiv.width(); var height = imgDiv.height(); var p = imgDiv.position(); var left = Math.round(p.left*100)/100; var top = Math.round(p.top*100)/100; if(draw=="1") { //draw image to canvas _this.drawImgToCanvas(url,width,height,left,top); } else { //Add image to background _this.drawImgToBackground(url,width,height,left,top); } _this.sendFunction({"t":"addImgBG", "draw":draw, "url":url, "d":[width,height,left,top]}); _this.drawId++; imgDiv.remove(); }); _this.mouseOverlay.append(imgDiv); imgDiv.draggable(); imgDiv.resizable(); }, drawImgToBackground(url,width,height,left,top) { this.imgContainer.append('') }, drawImgToCanvas(url,width,height,left,top, doneCallback) { var _this = this; var img = document.createElement('img'); img.onload=function(){ _this.ctx.drawImage(img,left,top,width,height); if(doneCallback) { doneCallback(); } } img.src = url; }, undoWhiteboard : function(username) { //Not call this directly because you will get out of sync whit others... var _this = this; if(!username) { username = _this.settings.username; } for(var i=_this.drawBuffer.length-1;i>=0;i--){ if(_this.drawBuffer[i]["username"]==username) { var drawId = _this.drawBuffer[i]["drawId"]; for(var i=_this.drawBuffer.length-1;i>=0;i--){ if(_this.drawBuffer[i]["drawId"]==drawId && _this.drawBuffer[i]["username"]==username) { _this.drawBuffer.splice(i, 1); } } break; } } _this.canvas.height = _this.canvas.height; _this.imgContainer.empty(); console.log(_this.drawBuffer) _this.loadDataInSteps(_this.drawBuffer,false, function(stepData) { //Nothing to do }); }, undoWhiteboardClick : function() { this.sendFunction({"t":"undo"}); this.undoWhiteboard(); }, setTool : function(tool) { this.tool = tool; if(tool==="pen" || tool==="eraser") { this.mouseOverlay.css({"cursor":"none"}); } else if(tool==="mouse"){ this.mouseOverlay.css({"cursor":"default"}); } else { this.mouseOverlay.css({"cursor":"crosshair"}); } this.mouseOverlay.find(".xCanvasBtn").click(); }, handleEventsAndData : function(content, isNewData, doneCallback) { var _this = this; var tool = content["t"]; var data = content["d"]; var color = content["c"]; var username = content["username"]; var thickness = content["th"]; if(tool==="line" || tool==="pen") { _this.drawPenLine(data[0], data[1], data[2], data[3], color, thickness); } else if(tool==="rect"){ _this.drawRec(data[0], data[1], data[2], data[3], color, thickness); } else if(tool==="circle"){ _this.drawCircle(data[0], data[1], data[2], color, thickness); } else if(tool==="eraser"){ _this.drawEraserLine(data[0], data[1], data[2], data[3], thickness); } else if(tool==="eraseRec"){ _this.eraseRec(data[0], data[1],data[2],data[3]); } else if(tool==="recSelect"){ _this.dragCanvasRectContent(data[0], data[1],data[2],data[3],data[4],data[5]); } else if(tool==="addImgBG") { if(content["draw"]=="1") { _this.drawImgToCanvas(content["url"],data[0],data[1],data[2],data[3], doneCallback) } else { _this.drawImgToBackground(content["url"],data[0],data[1],data[2],data[3]); } } else if(tool==="clear") { _this.canvas.height = _this.canvas.height; this.imgContainer.empty(); this.drawBuffer = []; this.drawId = 0; } else if(tool==="cursor") { if(content["event"]==="move") { if(_this.cursorContainer.find("."+content["username"]).length>=1) { _this.cursorContainer.find("."+content["username"]).css({"left":data[0]+"px","top":data[1]+"px" }); } else { _this.cursorContainer.append('
'+ '
'+ content["username"]+'
'); } } else { _this.cursorContainer.find("."+content["username"]).remove(); } } else if(tool==="undo") { _this.undoWhiteboard(username); } if(isNewData && (tool==="line" || tool==="pen" || tool==="rect" || tool==="circle" || tool==="eraser" || tool==="addImgBG" || tool==="recSelect" || tool==="eraseRec")) { content["drawId"] = content["drawId"] ? content["drawId"] : _this.drawId; content["username"] = content["username"] ? content["username"] : _this.settings.username; _this.drawBuffer.push(content); } }, userLeftWhiteboard(username) { this.cursorContainer.find("."+username).remove(); }, getImageDataBase64() { _this = this; var width = this.mouseOverlay.width(); var height = this.mouseOverlay.height(); var copyCanvas = document.createElement('canvas'); copyCanvas.width = width; copyCanvas.height = height; var ctx = copyCanvas.getContext("2d"); $.each(_this.imgContainer.find("img"), function() { var width = $(this).width(); var height = $(this).height(); var p = $(this).position(); var left = Math.round(p.left*100)/100; var top = Math.round(p.top*100)/100; ctx.drawImage(this,left,top,width,height); }); var destCtx = copyCanvas.getContext('2d'); destCtx.drawImage(this.canvas, 0, 0); var url = copyCanvas.toDataURL(); return url; }, getImageDataJson() { var sendObj = []; for(var i=0;i=content.length-1) { //Done with all data _this.drawId++; } }); }, sendFunction : function(content) { //Sends every draw to server var _this = this; content["wid"] = _this.settings.whiteboardId; content["username"] = _this.settings.username; content["drawId"] = _this.drawId; var tool = content["t"]; if(_this.settings.sendFunction) { _this.settings.sendFunction(content); } if(tool==="line" || tool==="pen" || tool==="rect" || tool==="circle" || tool==="eraser" || tool==="addImgBG" || tool==="recSelect" || tool==="eraseRec") { _this.drawBuffer.push(content); } }, isRecRecCollision : function(rx1,ry1,rw1,rh1,rx2,ry2,rw2,rh2) { return rx1 < rx2 + rw2 && rx1 + rw1 > rx2 && ry1 < ry2 + rh2 && rh1 + ry1 > ry2; }, isRecPointCollision : function(rx, ry, rw, rh, px, py) { return rx <= px && px <= rx + rw && ry <= py && py <= ry + rh; } }