bigbluebutton-Github/labs/bbb-html5-client/public/js/chat/whiteboard.coffee

916 lines
32 KiB
CoffeeScript
Raw Normal View History

define [ "jquery", "raphael", "cs!chat/connection", "colorwheel" ], ($, Raphael, Connection) ->
Whiteboard = {}
# As is done in Connection
PRESENTATION_SERVER = window.location.protocol + "//" + window.location.host
PRESENTATION_SERVER = PRESENTATION_SERVER.replace(/:\d+/, "/") # remove :port
gw = undefined
gh = undefined
cx2 = undefined
cy2 = undefined
cx1 = undefined
cy1 = undefined
px = undefined
py = undefined
cx = undefined
cy = undefined
sw = undefined
sh = undefined
slides = undefined
textx = undefined
texty = undefined
text = undefined
paper = undefined
cur = undefined
s_top = undefined
s_left = undefined
current_url = undefined
ex = undefined
ey = undefined
ellipse = undefined
line = undefined
scrollh = undefined
scrollw = undefined
textoffset = undefined
current_colour = undefined
current_thickness = undefined
path = undefined
rect = undefined
sx = undefined
sy = undefined
current_shapes = undefined
sw_orig = undefined
sh_orig = undefined
vw = undefined
vh = undefined
shift_pressed = undefined
zoom_level = 1
fitToPage = true
path_max = 30
path_count = 0
default_colour = "#FF0000"
default_thickness = 1
dcr = 3
slide_obj = document.getElementById("slide")
textbox = document.getElementById("area")
$("#area").autosize()
# 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}
drawThicknessView = (thickness, colour) ->
current_thickness = thickness
tctx.fillStyle = "#FFFFFF"
tctx.fillRect 0, 0, 20, 20
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}
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}
Whiteboard.toggleColourPicker = ->
if cpVisible
cpVisible = false
cp.raphael.forEach (i) ->
i.hide()
else
cpVisible = true
cp.raphael.forEach (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}
Whiteboard.turnOn = (tool) ->
current_tool = tool
console.log "it's here the tool:" + tool
switch tool
when "line"
cur.undrag()
cur.drag curDragging, curDragStart, curDragStop
when "rect"
cur.undrag()
cur.drag curRectDragging, curRectDragStart, curRectDragStop
when "panzoom"
cur.undrag()
cur.drag panDragging, panGo, panStop
when "ellipse"
cur.undrag()
cur.drag curEllipseDragging, curEllipseDragStart, curEllipseDragStop
when "text"
cur.undrag()
cur.drag curRectDragging, curTextStart, curTextStop
else
console.log "ERROR: Cannot turn on tool, invalid tool: " + tool
# Initializes the "Paper" which is the Raphael term for
# the entire SVG object on the webpage.
# @return {undefined}
initPaper = ->
# paper is embedded within the div#slide of the page.
paper = paper or Raphael("slide", gw, gh)
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
unless navigator.userAgent.indexOf("Firefox") is -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}
Whiteboard.updatePaperFromServer = (cx_, cy_, sw_, sh_) ->
# if updating the slide size (zooming!)
if sw_ and 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
sy = 0 if 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
z = paper._viewBox[2] / gw
cur.attr r: dcr * z
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.
Whiteboard.setFitToPage = (fit) ->
fitToPage = fit
temp = slides
Whiteboard.removeAllImagesFromPaper()
slides = temp
# re-add all the images as they should fit differently
rebuildPaper()
# set to default zoom level
Connection.emitPaperUpdate 0, 0, 1, 1
# get the shapes to reprocess
Connection.emitAllShapes()
# 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
Whiteboard.addImageToPaper = (url, w, h) ->
console.log "addIMageToPaper show me url:" + url
img = undefined
if fitToPage
# solve for the ratio of what length is going to fit more than the other
xr = w / vw
yr = h / vh
max = Math.max(xr, yr)
# fit it all in appropriately
# TODO: temporary solution
url = PRESENTATION_SERVER + url
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
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
unless current_url
img.toBack()
current_url = url
else if current_url is url
img.toBack()
else
img.hide()
img.mousemove onCursorMove
$(img.node).bind "mousewheel", zoomSlide
img
# Removes all the images from the Raphael paper.
# @return {undefined}
Whiteboard.removeAllImagesFromPaper = ->
img = undefined
for url of 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}
Whiteboard.drawListOfShapes = (shapes) ->
current_shapes = paper.set()
i = shapes.length - 1
while i >= 0
data = JSON.parse(shapes[i].data)
switch shapes[i].shape
when "path"
drawLine.apply drawLine, data
when "rect"
drawRect.apply drawRect, data
when "ellipse"
drawEllipse.apply drawEllipse, data
when "text"
drawText.apply drawText, data
else
i--
# make sure the cursor is still on top
bringCursorToFront()
# Re-add the images to the paper that are found
# in the slides array (an object of urls and dimensions).
# @return {undefined}
rebuildPaper = ->
current_url = null
for url of slides
if slides.hasOwnProperty(url)
Whiteboard.addImageToPaper url, slides[url].w, slides[url].h
# 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}
Whiteboard.showImageFromPaper = (url) ->
unless current_url is url
# TODO: temporary solution
url = PRESENTATION_SERVER + url
hideImageFromPaper current_url
next = getImageFromPaper(url)
if next
next.show()
next.toFront()
current_shapes.forEach (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
getImageFromPaper = (url) ->
console.log "show me url:" + url
if slides[url]
id = slides[url].id
if id
paper.getById id
else
null
else
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}
hideImageFromPaper = (url) ->
img = getImageFromPaper(url)
img.hide() if img
# Puts the cursor on top so it doesn't
# get hidden behind any objects/images.
# @return {undefined}
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}
panGo = (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}
panDragging = (dx, dy) ->
# ensuring that we cannot pan outside of the boundaries
x = (px - dx)
# cannot pan past the left edge of the page
x = (if x < 0 then 0 else x)
y = (py - dy)
# cannot pan past the top of the page
y = (if y < 0 then 0 else y)
if fitToPage
x2 = gw + x
else
x2 = vw + x
# cannot pan past the width
x = (if x2 > sw then sw - (vw - sx * 2) else x)
if fitToPage
y2 = gh + y
else
# height of image could be greater (or less) than the box it fits in
y2 = vh + y
# cannot pan below the height
y = (if y2 > sh then sh - (vh - sy * 2) else y)
Connection.emitPaperUpdate x / sw, y / sh, null, null
# When panning finishes
# @param {Event} e the mouse event
panStop = (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
curDragStart = (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
Connection.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}
curDragging = (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
Connection.emitUpdateShape "line", [ cx2 / sw, cy2 / sh, false ]
else
path_count++
if path_count < path_max
Connection.emitUpdateShape "line", [ cx2 / sw, cy2 / sh, true ]
else
path_count = 0
# save the last path of the line
line.attrs.path.pop()
path = line.attrs.path.join(" ")
line.attr path: (path + "L" + cx1 + " " + cy1)
# scale the path appropriately before sending
Connection.emitPublishShape "path", [ line.attrs.path.join(",").toScaledPath(1 / gw, 1 / gh), current_colour, current_thickness ]
Connection.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}
curDragStop = (e) ->
path = line.attrs.path
line = null # any late updates will be blocked by this
# scale the path appropriately before sending
Connection.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}
Whiteboard.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}
drawLine = (path, colour, thickness) ->
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}
Whiteboard.updateLine = (x2, y2, add) ->
x2 *= gw
y2 *= gh
if add
# if adding to the line
line.attr path: (line.attrs.path + "L" + x2 + " " + y2) if line
else
# if simply updating the last portion (for drawing a straight line)
if line
line.attrs.path.pop()
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}
Whiteboard.updateText = (t, x, y, w, spacing, colour, font, fontsize) ->
x = x * gw
y = y * gh
unless 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
cell = text.node
cell.removeChild cell.firstChild while cell.hasChildNodes()
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}
drawText = (t, x, y, w, spacing, colour, font, fontsize) ->
x = x * gw
y = y * gh
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
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}
curTextStart = (x, y) ->
if text
Connection.emitPublishShape "text", [ textbox.value, text.attrs.x / gw, text.attrs.y / gh, textbox.clientWidth, 16, current_colour, "Arial", 14 ]
Connection.emitTextDone()
textbox.value = ""
textbox.style.visibility = "hidden"
textx = x
texty = y
cx2 = (x - s_left - sx + cx) / sw
cy2 = (y - s_top - sy + cy) / sh
Connection.emitMakeShape "rect", [ cx2, cy2, "#000", 1 ]
# Finished drawing the rectangle that the text will fit into
# @param {Event} e the mouse event
# @return {undefined}
curTextStop = (e) ->
rect.hide() if rect
tboxw = (e.pageX - textx)
tboxh = (e.pageY - texty)
if tboxw >= 14 or 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 = ""
x = textx - s_left - sx + cx + 1 # 1px random padding
y = texty - s_top - sy + cy
textbox.focus()
# if you click outside, it will automatically sumbit
textbox.onblur = (e) ->
if text
Connection.emitPublishShape "text", [ @value, text.attrs.x / gw, text.attrs.y / gh, textbox.clientWidth, 16, current_colour, "Arial", 14 ]
Connection.emitTextDone()
textbox.value = ""
textbox.style.visibility = "hidden"
# if user presses enter key, then automatically submit
textbox.onkeypress = (e) ->
if e.keyCode is "13"
e.preventDefault()
e.stopPropagation()
@onblur()
# update everyone with the new text at every change
textbox.onkeyup = (e) ->
@style.color = current_colour
@value = @value.replace(/\n{1,}/g, " ").replace(/\s{2,}/g, " ")
Connection.emitUpdateShape "text", [ @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}
Whiteboard.textDone = ->
if text
text = null
rect.hide() if rect
# 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}
curRectDragStart = (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
Connection.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}
curRectDragging = (dx, dy, x, y, e) ->
x1 = undefined
y1 = undefined
# if shift is pressed, make it a square
dy = dx if shift_pressed
dx = dx / sw
dy = dy / sh
# adjust for negative values as well
unless dx >= 0
x1 = cx2 + dx
dx = -dx
unless dy >= 0
y1 = cy2 + dy
dy = -dy
Connection.emitUpdateShape "rect", [ x1, y1, dx, dy ]
# When rectangle finished being drawn
# @param {Event} e the mouse event
# @return {undefined}
curRectDragStop = (e) ->
r = undefined
r = rect.attrs if rect
Connection.emitPublishShape "rect", [ r.x / gw, r.y / gh, r.width / gw, r.height / gh, current_colour, current_thickness ] if r
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}
Whiteboard.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}
drawRect = (x, y, w, h, colour, thickness) ->
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}
Whiteboard.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}
curEllipseDragStart = (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)
Connection.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}
Whiteboard.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}
drawEllipse = (cx, cy, rx, ry, colour, thickness) ->
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}
curEllipseDragging = (dx, dy, x, y, e) ->
# if shift is pressed, draw a circle instead of ellipse
dy = dx if shift_pressed
dx = dx / 2
dy = dy / 2
# adjust for negative values as well
x = ex + dx
y = ey + dy
dx = (if dx < 0 then -dx else dx)
dy = (if dy < 0 then -dy else dy)
Connection.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}
Whiteboard.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}
curEllipseDragStop = (e) ->
attrs = undefined
attrs = ellipse.attrs if ellipse
Connection.emitPublishShape "ellipse", [ attrs.cx / gw, attrs.cy / gh, attrs.rx / gw, attrs.ry / gh, current_colour, current_thickness ] if attrs
ellipse = null # late updates will be blocked by this
# Called when the cursor is moved over the presentation.
# Sends 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}
onCursorMove = (e, x, y) ->
xLocal = (x - sx - s_left + cx) / sw
yLocal = (y - sy - s_top + cy) / sh
Connection.emitMoveCursor xLocal, yLocal
# 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}
Whiteboard.mvCur = (x, y) ->
cur.attr
cx: x * gw
cy: y * gh
# Socket response - Clear canvas
# @return {undefined}
Whiteboard.clearPaper = ->
if current_shapes
current_shapes.forEach (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}
zoomSlide = (event, delta) ->
Connection.emitZoom delta
# Socket response - Update zoom variables and viewbox
# @param {number} d the delta value from the scroll event
# @return {undefined}
Whiteboard.setZoom = (d) ->
step = 0.05 # step size
if d < 0
zoom_level += step # zooming out
else
zoom_level -= step # zooming in
x = cx / sw
y = cy / sh
# cannot zoom out further than 100%
z = (if zoom_level > 1 then 1 else zoom_level)
# cannot zoom in further than 400% (1/4)
z = (if z < 0.25 then 0.25 else z)
# cannot zoom to make corner less than (x,y) = (0,0)
x = (if x < 0 then 0 else x)
y = (if y < 0 then 0 else y)
# cannot view more than the bottom corners
zz = 1 - z
x = (if x > zz then zz else x)
y = (if y > zz then zz else y)
Connection.emitPaperUpdate x, y, z, z # send update to all clients
initPaper()
c = document.getElementById("colourView")
tc = document.getElementById("thicknessView")
cptext = document.getElementById("colourText")
ctx = c.getContext("2d")
tctx = tc.getContext("2d")
s_left = slide_obj.offsetLeft
s_top = slide_obj.offsetTop
vw = slide_obj.clientWidth
vh = slide_obj.clientHeight
drawThicknessView default_thickness, default_colour
drawColourView default_colour
# create colour picker
cp = Raphael.colorwheel(-75, -75, 75, default_colour)
# hide it
cp.raphael.forEach (item) -> item.hide()
cpVisible = false
$ ->
$("#thickness").slider
value: 1
min: 1
max: 20
$("#thickness").bind "slide", (event, ui) ->
drawThicknessView ui.value, current_colour
# upload without a refresh
$("#uploadForm").submit ->
$("#uploadStatus").text "Uploading..."
$(this).ajaxSubmit
error: (xhr) ->
console.log "Error: " + xhr.status
success: (response) ->
# Have to stop the form from submitting and causing refresh
false
# automatically upload the file if it is chosen
$("#uploadFile").change ->
$("#uploadForm").submit()
# when the colour picker colour changes
cp.onchange = ->
drawColourView @color()
drawThicknessView current_thickness, @color()
# when finished typing a colour into the colour text box
cptext.onkeyup = ->
drawColourView @value
drawThicknessView current_thickness, @value
# when pressing down on a key at anytime
document.onkeydown = (event) ->
unless event
keyCode = window.event.keyCode
else
keyCode = event.keyCode
switch keyCode
when 16 # shift key
shift_pressed = true
# when releasing any key at any time
document.onkeyup = (event) ->
unless event
keyCode = window.event.keyCode
else
keyCode = event.keyCode
switch keyCode
when 16 # shift key
shift_pressed = false
window.onresize = ->
Whiteboard.windowResized()
Whiteboard.windowResized = (div) ->
s_top = slide_obj.offsetTop
s_left = slide_obj.offsetLeft
s_left += $("#presentation")[0].offsetLeft if div
console.log "window resized"
# 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::toScaledPath = (w, h) ->
path = undefined
points = @match(/(\d+[.]?\d*)/g)
len = points.length
j = 0
# go through each point and multiply it by the new height and width
while j < len
if j isnt 0
path += "L" + (points[j] * w) + "," + (points[j + 1] * h)
else
path = "M" + (points[j] * w) + "," + (points[j + 1] * h)
j += 2
path
Whiteboard