bigbluebutton-Github/labs/html5-embed/public/js/chat/whiteboard.js

1023 lines
31 KiB
JavaScript
Executable File

var PRESENTATION_SERVER = "http://192.168.1.218/";
//object references
slide_obj = document.getElementById("slide");
textbox = document.getElementById('area');
$('#area').autosize();
var gw, gh, cx2, cy2, cx1, cy1, px, py, cx, cy, sw, sh, slides, textx, texty, text,
paper, cur, s_top, s_left, current_url, ex, ey, ellipse, line, scrollh, scrollw, textoffset,
current_colour, current_thickness, path, rect, sx, sy, current_shapes, sw_orig, sh_orig, vw, vh, shift_pressed;
var zoom_level = 1, fitToPage = true, path_max = 30,
path_count = 0, default_colour = "#FF0000", default_thickness = 1,
dcr = 3;
/**
* Drawing the thickness viewer for client feedback.
* No messages are sent to the server, it is completely
* local. Shows visual of thickness for drawing tools.
* @param {number} thickness the thickness value
* @param {string} colour the colour it should be displayed as
* @return {undefined}
*/
function drawThicknessView(thickness, colour){
current_thickness = thickness;
tctx.fillStyle='#FFFFFF';
tctx.fillRect(0,0,20,20);
var center = Math.round((20-thickness+1)/2);
tctx.fillStyle=colour;
tctx.fillRect(center,center,thickness+1,thickness+1);
}
/**
* Drawing the colour viewer for client feedback.
* No messages are sent to the server, it is
* completely local. Shows colour visual for drawing
* tools.
* @param {string} colour the colour it should be displayed as
* @return {undefined}
*/
function drawColourView(colour) {
current_colour = colour;
ctx.fillStyle = colour;
cptext.value = colour;
ctx.fillRect(0,0,12,12);
}
/**
* Toggles the visibility of the colour picker,
* which is hidden by default. The picker is a
* RaphaelJS object, so each node of the object
* must be shown/hidden individually.
* @return {undefined}
*/
function toggleColourPicker() {
if(cpVisible) {
cpVisible = false;
cp.raphael.forEach(function(i){ i.hide(); });
}
else {
cpVisible = true;
cp.raphael.forEach(function(i){ i.show(); });
}
}
/**
* Switches the tool and thus the functions that get
* called when certain events are fired from Raphael.
* @param {string} tool the tool to turn on
* @return {undefined}
*/
function turnOn(tool) {
current_tool = tool;
console.log("it's here the tool:"+tool);
switch(tool) {
case 'line':
cur.undrag();
cur.drag(curDragging, curDragStart, curDragStop);
break;
case 'rect':
cur.undrag();
cur.drag(curRectDragging, curRectDragStart, curRectDragStop);
break;
case 'panzoom':
cur.undrag();
cur.drag(panDragging, panGo, panStop);
break;
case 'ellipse':
cur.undrag();
cur.drag(curEllipseDragging, curEllipseDragStart, curEllipseDragStop);
break;
case 'text':
cur.undrag();
cur.drag(curRectDragging, curTextStart, curTextStop);
break;
default:
console.log("ERROR: Cannot turn on tool, invalid tool: " + tool);
break;
}
}
/**
* Initializes the "Paper" which is the Raphael term for
* the entire SVG object on the webpage.
* @return {undefined}
*/
function initPaper() {
//paper is embedded within the div#slide of the page.
paper = paper || Raphael('slide', gw, gh); //create a SVG object using RaphaelJS
paper.canvas.setAttribute('preserveAspectRatio', 'xMinYMin slice');
cur = paper.circle(0, 0, dcr);
cur.attr('fill', 'red');
$(cur.node).bind('mousewheel', zoomSlide);
if(slides) {
rebuildPaper();
}
else slides = {}; //if previously loaded
if (navigator.userAgent.indexOf("Firefox") != -1) {
paper.renderfix();
}
}
/**
* Updates the paper from the server values.
* @param {number} cx_ the x-offset value as a percentage of the original width
* @param {number} cy_ the y-offset value as a percentage of the original height
* @param {number} sw_ the slide width value as a percentage of the original width
* @param {number} sh_ the slide height value as a percentage of the original height
* @return {undefined}
*/
function updatePaperFromServer(cx_, cy_, sw_, sh_) {
//if updating the slide size (zooming!)
if(sw_ && sh_) {
paper.setViewBox(cx_*gw, cy_*gh, sw_*gw, sh_*gh);
sw = gw/sw_;
sh = gh/sh_;
}
//just panning, so use old slide size values
else {
paper.setViewBox(cx_*gw, cy_*gh, paper._viewBox[2], paper._viewBox[3]);
}
//update corners
cx = cx_*sw;
cy = cy_*sh;
//update position of svg object in the window
sx = (vw - gw)/2;
sy = (vh - gh)/2;
if(sy < 0) sy = 0; // ??
paper.canvas.style.left = sx + "px";
paper.canvas.style.top = sy + "px";
paper.setSize(gw-2, gh-2);
//update zoom level and cursor position
var z = paper._viewBox[2]/gw;
cur.attr({ r : dcr*z }); //adjust cursor size
zoom_level = z;
//force the slice attribute despite Raphael changing it
paper.canvas.setAttribute('preserveAspectRatio', 'xMinYMin slice');
}
/**
* Sets the fit to page.
* @param {boolean} fit fit == true ? -> fit to page. fit == false ? -> fit to width.
*/
function setFitToPage(fit) {
fitToPage = fit;
var temp = slides;
removeAllImagesFromPaper();
slides = temp;
rebuildPaper(); //re-add all the images as they should fit differently
sendPaperUpdate(0, 0, 1, 1); //set to default zoom level
getShapesFromServer(); //reprocess the shapes
}
/**
* Add an image to the paper.
* @param {string} url the URL of the image to add to the paper
* @param {number} w the width of the image (in pixels)
* @param {number} h the height of the image (in pixels)
* @return {Raphael.image} the image object added to the whiteboard
*/
function addImageToPaper(url, w, h) {
console.log("addIMageToPaper show me url:" + url);
var img;
if(fitToPage) {
//solve for the ratio of what length is going to fit more than the other
var xr = w/vw;
var yr = h/vh;
var max = Math.max(xr, yr);
//fit it all in appropriately
//temporary solution
url = PRESENTATION_SERVER + url;
//img = paper.image(url, cx = 0, cy = 0, gw = w/max, gh = h/max);
img = paper.image(url, cx = 0, cy = 0, gw = w, gh = h);
console.log(img);
//update the global variables we will need to use
sw = w/max;
sh = h/max;
sw_orig = sw;
sh_orig = sh;
}
else {
//fit to width
//assume it will fit width ways
var wr = w/vw;
img = paper.image(url, cx = 0, cy = 0, w/wr, h/wr);
sw = w/wr;
sh = h/wr;
sw_orig = sw;
sh_orig = sh;
gw = sw;
gh = sh;
}
slides[url] = { 'id' : img.id, 'w' : w, 'h' : h};
if(!current_url) {
img.toBack();
current_url = url;
}
else if(current_url == url) {
img.toBack();
}
else {
img.hide();
}
img.mousemove(mvingCur);
$(img.node).bind('mousewheel', zoomSlide);
return img;
}
/**
* Removes all the images from the Raphael paper.
* @return {undefined}
*/
function removeAllImagesFromPaper() {
var img;
for (url in slides) {
if(slides.hasOwnProperty(url)) {
paper.getById(slides[url].id).remove();
$('#preload' + slides[url].id).remove();
}
}
slides = {};
current_url = null;
}
/**
* Draws an array of shapes to the paper.
* @param {array} shapes the array of shapes to draw
* @return {undefined}
*/
function drawListOfShapes(shapes) {
current_shapes = paper.set();
for (var i = shapes.length - 1; i >= 0; i--) {
var data = JSON.parse(shapes[i].data);
switch(shapes[i].shape) {
case 'path':
drawLine.apply(drawLine, data);
break;
case 'rect':
drawRect.apply(drawRect, data);
break;
case 'ellipse':
drawEllipse.apply(drawEllipse, data);
break;
case 'text':
drawText.apply(drawText, data);
break;
default:
break;
}
}
bringCursorToFront(); //make sure the cursor is still on top;
}
/**
* Re-add the images to the paper that are found
* in the slides array (an object of urls and dimensions).
* @return {undefined}
*/
function rebuildPaper() {
current_url = null;
for(url in slides) {
if(slides.hasOwnProperty(url)) {
addImageToPaper(url, slides[url].w, slides[url].h, function(img) {
});
}
}
}
/**
* Shows an image from the paper.
* The url must be in the slides array.
* @param {string} url the url of the image (must be in slides array)
* @return {undefined}
*/
function showImageFromPaper(url) {
if(current_url != url) {
//temporary solution
url = PRESENTATION_SERVER + url;
hideImageFromPaper(current_url);
var next = getImageFromPaper(url);
if(next) {
next.show();
next.toFront();
current_shapes.forEach(function(element) {
element.toFront();
});
cur.toFront();
}
current_url = url;
}
}
/**
* Retrieves an image element from the paper.
* The url must be in the slides array.
* @param {string} url the url of the image (must be in slides array)
* @return {Raphael.image} return the image or null if not found
*/
function getImageFromPaper(url) {
console.log("show me url:" + url);
if(slides[url]) {
var id = slides[url].id;
if(id) {
return paper.getById(id);
}
else return null;
}
else return null;
}
/**
* Hides an image from the paper given the URL.
* The url must be in the slides array.
* @param {string} url the url of the image (must be in slides array)
* @return {undefined}
*/
function hideImageFromPaper(url) {
var img = getImageFromPaper(url);
if(img) img.hide();
}
/**
* Puts the cursor on top so it doesn't
* get hidden behind any objects/images.
* @return {undefined}
*/
function bringCursorToFront() {
cur.toFront();
}
/**
* When panning starts
* @param {number} x the x value of the cursor
* @param {number} y the y value of the cursor
* @return {undefined}
*/
var panGo = function(x, y) {
px = cx;
py = cy;
};
/**
* When the user is dragging the cursor (click + move)
* @param {number} dx the difference between the x value from panGo and now
* @param {number} dy the difference between the y value from panGo and now
* @return {undefined}
*/
var panDragging = function(dx, dy) {
//ensuring that we cannot pan outside of the boundaries
var x = (px - dx);
x = x < 0 ? 0 : x; //cannot pan past the left edge of the page
var y = (py - dy);
y = y < 0 ? 0 : y; //cannot pan past the top of the page
var x2;
if(fitToPage) x2 = gw + x;
else x2 = vw + x;
x = x2 > sw ? sw - (vw - sx*2) : x; //cannot pan past the width
var y2;
if(fitToPage) y2 = gh + y;
else y2 = vh + y; //height of image could be greater (or less) than the box it fits in
y = y2 > sh ? sh - (vh - sy*2) : y; //cannot pan below the height
sendPaperUpdate(x/sw, y/sh, null, null);
};
/**
* When panning finishes
* @param {Event} e the mouse event
* @return {undefined}
*/
var panStop = function(e) {
//nothing to do
};
/**
* When dragging for drawing lines starts
* @param {number} x the x value of the cursor
* @param {number} y the y value of the cursor
* @return {undefined}
*/
var curDragStart = function(x, y) {
//find the x and y values in relation to the whiteboard
cx1 = x - s_left - sx + cx;
cy1 = y - s_top - sy + cy;
emitMakeShape('line', [cx1/sw, cy1/sh, current_colour, current_thickness]);
};
/**
* As line drawing drag continues
* @param {number} dx the difference between the x value from curDragStart and now
* @param {number} dy the difference between the y value from curDragStart and now
* @param {number} x the x value of the cursor
* @param {number} y the y value of the cursor
* @return {undefined}
*/
var curDragging = function(dx, dy, x, y) {
//find the x and y values in relation to the whiteboard
cx2 = x - s_left - sx + cx;
cy2 = y - s_top - sy + cy;
if(shift_pressed) {
emitUpdateShape('line', [cx2/sw, cy2/sh, false]);
}
else {
path_count++;
if(path_count < path_max) {
emitUpdateShape('line', [cx2/sw, cy2/sh, true]);
}
else {
path_count = 0;
//save the last path of the line
line.attrs.path.pop();
var path = line.attrs.path.join(' ');
line.attr({ path : (path + "L" + cx1 + " " + cy1) });
//scale the path appropriately before sending
emitPublishShape('path', [line.attrs.path.join(',').toScaledPath(1/gw, 1/gh), current_colour, current_thickness]);
emitMakeShape('line', [cx1/sw, cy1/sh, current_colour, current_thickness]);
}
cx1 = cx2;
cy1 = cy2;
}
};
/**
* Drawing line has ended
* @param {Event} e the mouse event
* @return {undefined}
*/
var curDragStop = function(e) {
var path = line.attrs.path;
line = null; //any late updates will be blocked by this
//scale the path appropriately before sending
emitPublishShape('path', [path.join(',').toScaledPath(1/gw, 1/gh), current_colour, current_thickness]);
};
/**
* Make a line on the whiteboard that could be updated shortly after
* @param {number} x the x value of the line start point as a percentage of the original width
* @param {number} y the y value of the line start point as a percentage of the original height
* @param {string} colour the colour of the shape to be drawn
* @param {number} thickness the thickness of the line to be drawn
* @return {undefined}
*/
function makeLine(x, y, colour, thickness) {
x *= gw;
y *= gh;
line = paper.path("M" + x + " " + y + "L" + x + " " + y);
if(colour) line.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(line);
}
/**
* Drawing a line from the list o
* @param {string} path height of the shape as a percentage of the original height
* @param {string} colour the colour of the shape to be drawn
* @param {number} thickness the thickness of the line to be drawn
* @return {undefined}
*/
function drawLine(path, colour, thickness) {
var l = paper.path(path.toScaledPath(gw, gh));
l.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(l);
}
/**
* Updating drawing the line
* @param {number} x2 the next x point to be added to the line as a percentage of the original width
* @param {number} y2 the next y point to be added to the line as a percentage of the original height
* @param {boolean} add true if the line should be added to the current line, false if it should replace the last point
* @return {undefined}
*/
function updateLine(x2, y2, add) {
x2 *= gw;
y2 *= gh;
if(add) {
//if adding to the line
if(line) line.attr({ path : (line.attrs.path + "L" + x2 + " " + y2) });
}
else {
//if simply updating the last portion (for drawing a straight line)
if(line) {
line.attrs.path.pop();
var path = line.attrs.path.join(' ');
line.attr({ path : (path + "L" + x2 + " " + y2) });
}
}
}
/**
* Updating the text from the messages on the socket
* @param {string} t the text of the text object
* @param {number} x the x value of the object as a percentage of the original width
* @param {number} y the y value of the object as a percentage of the original height
* @param {number} w the width of the text box as a percentage of the original width
* @param {number} spacing the spacing between the letters
* @param {string} colour the colour of the text
* @param {string} font the font family of the text
* @param {number} fontsize the size of the font (in PIXELS)
* @return {undefined}
*/
function updateText(t, x, y, w, spacing, colour, font, fontsize) {
x = x*gw;
y = y*gh;
if(!text) {
text = paper.text(x, y, "").attr({fill: colour, 'font-family' : font, 'font-size' : fontsize});
text.node.style['text-anchor'] = 'start'; //force left align
text.node.style['textAnchor'] = 'start'; //for firefox, 'cause they like to be different.
current_shapes.push(text);
}
else {
text.attr({fill: colour});
var cell = text.node;
while(cell.hasChildNodes()) cell.removeChild(cell.firstChild);
var dy = textFlow(t, cell, w, x, spacing, false);
}
cur.toFront();
}
/**
* Drawing the text on the whiteboard from object
* @param {string} t the text of the text object
* @param {number} x the x value of the object as a percentage of the original width
* @param {number} y the y value of the object as a percentage of the original height
* @param {number} w the width of the text box as a percentage of the original width
* @param {number} spacing the spacing between the letters
* @param {string} colour the colour of the text
* @param {string} font the font family of the text
* @param {number} fontsize the size of the font (in PIXELS)
* @return {undefined}
*/
function drawText(t, x, y, w, spacing, colour, font, fontsize) {
x = x*gw;
y = y*gh;
var txt = paper.text(x, y, "").attr({fill: colour, 'font-family' : font, 'font-size' : fontsize});
txt.node.style['text-anchor'] = 'start'; //force left align
txt.node.style['textAnchor'] = 'start'; //for firefox, 'cause they like to be different.
var dy = textFlow(t, txt.node, w, x, spacing, false);
current_shapes.push(txt);
}
/**
* When first dragging the mouse to create the textbox size
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @return {undefined}
*/
var curTextStart = function(x, y) {
if(text) {
emitPublishShape('text', [textbox.value, text.attrs.x/gw, text.attrs.y/gh, textbox.clientWidth, 16, current_colour, 'Arial', 14]);
emitDoneText();
}
textbox.value = "";
textbox.style.visibility = "hidden";
textx = x;
texty = y;
cx2 = (x - s_left - sx + cx)/sw;
cy2 = (y - s_top - sy + cy)/sh;
emitMakeShape('rect', [cx2, cy2, '#000', 1]);
};
/**
* Finished drawing the rectangle that the text will fit into
* @param {Event} e the mouse event
* @return {undefined}
*/
var curTextStop = function(e) {
if(rect) rect.hide();
var tboxw = (e.pageX - textx);
var tboxh = (e.pageY - texty);
if(tboxw >= 14 || tboxh >= 14) { //restrict size
textbox.style.width = tboxw*(gw/sw)+"px";
textbox.style.visibility = "visible";
textbox.style['font-size'] = 14 + "px";
textbox.style['fontSize'] = 14 + "px"; //firefox
textbox.style.color = current_colour;
textbox.value = "";
var x = textx - s_left - sx + cx + 1; // 1px random padding
var y = texty - s_top - sy + cy;
textbox.focus();
//if you click outside, it will automatically sumbit
textbox.onblur = function(e) {
if(text) {
emitPublishShape('text', [this.value, text.attrs.x/gw, text.attrs.y/gh, textbox.clientWidth, 16, current_colour, 'Arial', 14]);
emitDoneText();
}
textbox.value = "";
textbox.style.visibility = "hidden";
};
//if user presses enter key, then automatically submit
textbox.onkeypress = function(e) {
if(e.keyCode == '13') {
e.preventDefault();
e.stopPropagation();
this.onblur();
}
};
//update everyone with the new text at every change
textbox.onkeyup = function(e) {
this.style.color = current_colour;
this.value = this.value.replace(/\n{1,}/g, ' ').replace(/\s{2,}/g, ' '); //enforce no 2 or greater consecutive spaces, no new lines
emitUpdateShape('text', [this.value, x/sw, (y+(14*(sh/gh)))/sh, tboxw*(gw/sw), 16, current_colour, 'Arial', 14]);
};
}
};
/**
* The server has said the text is finished,
* so set it to null for the next text object
* @return {undefined}
*/
function textDone() {
if(text) {
text = null;
if(rect) rect.hide();
}
}
/**
* Creating a rectangle has started
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @return {undefined}
*/
var curRectDragStart = function(x, y) {
//find the x and y values in relation to the whiteboard
cx2 = (x - s_left - sx + cx)/sw;
cy2 = (y - s_top - sy + cy)/sh;
emitMakeShape('rect', [cx2, cy2, current_colour, current_thickness]);
};
/**
* Adjusting rectangle continues
* @param {number} dx the difference in the x value at the start as opposed to the x value now
* @param {number} dy the difference in the y value at the start as opposed to the y value now
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @param {Event} e the mouse event
* @return {undefined}
*/
var curRectDragging = function(dx, dy, x, y, e) {
var x1;
var y1;
//if shift is pressed, make it a square
if(shift_pressed) dy = dx;
dx = dx/sw;
dy = dy/sh;
//adjust for negative values as well
if(dx >= 0) x1 = cx2;
else {
x1 = cx2 + dx;
dx = -dx;
}
if(dy >= 0) y1 = cy2;
else {
y1 = cy2 + dy;
dy = -dy;
}
emitUpdateShape('rect', [x1, y1, dx, dy]);
};
/**
* When rectangle finished being drawn
* @param {Event} e the mouse event
* @return {undefined}
*/
var curRectDragStop = function(e) {
var r;
if(rect) r = rect.attrs;
if(r) emitPublishShape('rect', [r.x/gw, r.y/gh, r.width/gw, r.height/gh, current_colour, current_thickness]);
rect = null;
};
/**
* Socket response - Make rectangle on canvas
* @param {number} x the x value of the object as a percentage of the original width
* @param {number} y the y value of the object as a percentage of the original height
* @param {string} colour the colour of the object
* @param {number} thickness the thickness of the object's line(s)
* @return {undefined}
*/
function makeRect(x, y, colour, thickness) {
rect = paper.rect(x*gw, y*gh, 0, 0);
if(colour) rect.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(rect);
}
/**
* Draw a rectangle on the paper
* @param {number} x the x value of the object as a percentage of the original width
* @param {number} y the y value of the object as a percentage of the original height
* @param {number} w width of the shape as a percentage of the original width
* @param {number} h height of the shape as a percentage of the original height
* @param {string} colour the colour of the object
* @param {number} thickness the thickness of the object's line(s)
* @return {undefined}
*/
function drawRect(x, y, w, h, colour, thickness) {
var r = paper.rect(x*gw, y*gh, w*gw, h*gh);
if(colour) r.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(r);
}
/**
* Socket response - Update rectangle drawn
* @param {number} x1 the x value of the object as a percentage of the original width
* @param {number} y1 the y value of the object as a percentage of the original height
* @param {number} w width of the shape as a percentage of the original width
* @param {number} h height of the shape as a percentage of the original height
* @return {undefined}
*/
function updateRect(x1, y1, w, h) {
if(rect) rect.attr({ x: (x1)*gw, y: (y1)*gh, width: w*gw, height: h*gh });
}
/**
* When first starting drawing the ellipse
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @return {undefined}
*/
var curEllipseDragStart = function(x, y) {
//find the x and y values in relation to the whiteboard
ex = (x - s_left - sx + cx);
ey = (y - s_top - sy + cy);
emitMakeShape('ellipse', [ex/sw, ey/sh, current_colour, current_thickness]);
};
/**
* Make an ellipse on the whiteboard
* @param {[type]} cx the x value of the center as a percentage of the original width
* @param {[type]} cy the y value of the center as a percentage of the original height
* @param {string} colour the colour of the object
* @param {number} thickness the thickness of the object's line(s)
* @return {undefined}
*/
function makeEllipse(cx, cy, colour, thickness) {
ellipse = paper.ellipse(cx*gw, cy*gh, 0, 0);
if(colour) ellipse.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(ellipse);
}
/**
* Draw an ellipse on the whiteboard
* @param {[type]} cx the x value of the center as a percentage of the original width
* @param {[type]} cy the y value of the center as a percentage of the original height
* @param {[type]} rx the radius-x of the ellipse as a percentage of the original width
* @param {[type]} ry the radius-y of the ellipse as a percentage of the original height
* @param {string} colour the colour of the object
* @param {number} thickness the thickness of the object's line(s)
* @return {undefined}
*/
function drawEllipse(cx, cy, rx, ry, colour, thickness) {
var elip = paper.ellipse(cx*gw, cy*gh, rx*gw, ry*gh);
if(colour) elip.attr({ 'stroke' : colour, 'stroke-width' : thickness });
current_shapes.push(elip);
}
/**
* When first starting to draw an ellipse
* @param {number} dx the difference in the x value at the start as opposed to the x value now
* @param {number} dy the difference in the y value at the start as opposed to the y value now
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @param {Event} e the mouse event
* @return {undefined}
*/
var curEllipseDragging = function(dx, dy, x, y, e) {
//if shift is pressed, draw a circle instead of ellipse
if(shift_pressed) dy = dx;
dx = dx/2;
dy = dy/2;
//adjust for negative values as well
x = ex+dx;
y = ey+dy;
dx = dx < 0 ? -dx : dx;
dy = dy < 0 ? -dy : dy;
emitUpdateShape('ellipse', [x/sw, y/sh, dx/sw, dy/sh]);
};
/**
* Socket response - Update rectangle drawn
* @param {number} x the x value of the object as a percentage of the original width
* @param {number} y the y value of the object as a percentage of the original height
* @param {number} w width of the shape as a percentage of the original width
* @param {number} h height of the shape as a percentage of the original height
* @return {undefined}
*/
function updateEllipse(x, y, w, h) {
if(ellipse) ellipse.attr({cx: x*gw, cy: y*gh, rx: w*gw, ry: h*gh });
}
/**
* When releasing the mouse after drawing the ellipse
* @param {Event} e the mouse event
* @return {undefined}
*/
var curEllipseDragStop = function(e) {
var attrs;
if(ellipse) attrs = ellipse.attrs;
if(attrs) emitPublishShape('ellipse', [attrs.cx/gw, attrs.cy/gh, attrs.rx/gw, attrs.ry/gh, current_colour, current_thickness]);
ellipse = null; //late updates will be blocked by this
};
/**
* Send cursor moving event to server
* @param {Event} e the mouse event
* @param {number} x the x value of cursor at the time in relation to the left side of the browser
* @param {number} y the y value of cursor at the time in relation to the top of the browser
* @return {undefined}
*/
var mvingCur = function(e, x, y) {
emMvCur((x - sx - s_left + cx)/sw, (y - sy - s_top + cy)/sh);
};
/**
* Socket response - Update the cursor position on screen
* @param {number} x the x value of the cursor as a percentage of the width
* @param {number} y the y value of the cursor as a percentage of the height
* @return {undefined}
*/
function mvCur(x, y) {
cur.attr({ cx: x*gw, cy: y*gh });
}
/**
* Socket response - Clear canvas
* @return {undefined}
*/
function clearPaper() {
if(current_shapes){
current_shapes.forEach(function(element) {
element.remove();
});
}
}
/**
* Update zoom variables on all clients
* @param {Event} event the event that occurs when scrolling
* @param {number} delta the speed/direction at which the scroll occurred
* @return {undefined}
*/
var zoomSlide = function(event, delta) {
emZoom(delta);
};
/**
* Socket response - Update zoom variables and viewbox
* @param {number} d the delta value from the scroll event
* @return {undefined}
*/
function setZoom(d) {
var step = 0.05; //step size
if(d < 0) zoom_level += step; //zooming out
else zoom_level -= step; //zooming in
var x = cx/sw, y = cy/sh, z = zoom_level > 1 ? 1 : zoom_level; //cannot zoom out further than 100%
z = z < 0.25 ? 0.25 : z; //cannot zoom in further than 400% (1/4)
//cannot zoom to make corner less than (x,y) = (0,0)
x = x < 0 ? 0 : x;
y = y < 0 ? 0 : y;
//cannot view more than the bottom corners
var zz = 1 - z;
x = x > zz ? zz : x;
y = y > zz ? zz : y;
sendPaperUpdate(x, y, z, z); //send update to all clients
}
initPaper();
var c = document.getElementById("colourView");
var tc = document.getElementById('thicknessView');
var cptext = document.getElementById("colourText");
var ctx = c.getContext("2d");
var tctx = tc.getContext('2d');
s_left = slide_obj.offsetLeft; //the offset from the left of the page to the whiteboard frame border
s_top = slide_obj.offsetTop; // the offset from the top of the page to the whiteboard frame border
vw = slide_obj.clientWidth;
vh = slide_obj.clientHeight;
drawThicknessView(default_thickness, default_colour);
drawColourView(default_colour);
cp = Raphael.colorwheel(-75, -75, 75, default_colour); //create colour picker
cp.raphael.forEach(function(item) { item.hide(); }); //hide it
var cpVisible = false;
$(function() {
$("#thickness").slider({ value: 1, min: 1, max: 20 });
$("#thickness").bind("slide", function(event, ui) {
drawThicknessView(ui.value, current_colour);
});
});
//when the colour picker colour changes
cp.onchange = function() {
drawColourView(this.color());
drawThicknessView(current_thickness, this.color());
};
//when finished typing a colour into the colour text box
cptext.onkeyup = function() {
drawColourView(this.value);
drawThicknessView(current_thickness, this.value);
};
//when pressing down on a key at anytime
document.onkeydown = function(event) {
var keyCode;
if(!event) keyCode = window.event.keyCode;
else keyCode = event.keyCode;
switch(keyCode) {
case 16: //shift key
shift_pressed = true;
break;
default:
//nothing
break;
}
};
function windowResized(div) {
s_top = slide_obj.offsetTop;
s_left = slide_obj.offsetLeft;
if(div) {
s_left += $('#presentation')[0].offsetLeft;
}
console.log('window resized');
}
//when releasing any key at any time
document.onkeyup = function(event) {
var keyCode;
if(!event) keyCode = window.event.keyCode;
else keyCode = event.keyCode;
switch(keyCode) {
case 16: //shift key
shift_pressed = false;
break;
default:
//nothing
break;
}
};
//upload without a refresh
$('#uploadForm').submit(function() {
$('#uploadStatus').text("Uploading...");
$(this).ajaxSubmit({
error: function(xhr) {
console.log('Error: ' + xhr.status);
},
success: function(response) {
}
});
// Have to stop the form from submitting and causing refresh
return false;
});
//automatically upload the file if it is chosen
$('#uploadFile').change(function() {
$("#uploadForm").submit();
});
window.onresize = function () {
windowResized();
}
/**
* Scales a path string to fit within a width and height of the new paper size
* @param {number} w width of the shape as a percentage of the original width
* @param {number} h height of the shape as a percentage of the original height
* @return {string} the path string after being manipulated to new paper size
*/
String.prototype.toScaledPath = function(w, h) {
var path;
var points = this.match(/(\d+[.]?\d*)/g);
var len = points.length;
//go through each point and multiply it by the new height and width
for(var j = 0; j < len; j+=2) {
if(j !== 0) path += "L" + (points[j] * w) + "," + (points[j+1] * h);
else path = "M" + (points[j] * w) + "," + (points[j+1] * h);
}
return path;
};