1023 lines
31 KiB
JavaScript
Executable File
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;
|
|
};
|