moved the code from the whiteboard prototype to the html5 client. Now will start integrating it
This commit is contained in:
parent
834b48dde3
commit
cd7ea270df
102
labs/meteor-client/client/lib/scale.raphael.js
Normal file
102
labs/meteor-client/client/lib/scale.raphael.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* ScaleRaphael 0.8 by Zevan Rosser 2010
|
||||||
|
* For use with Raphael library : www.raphaeljs.com
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* www.shapevent.com/scaleraphael/
|
||||||
|
*/
|
||||||
|
(function(){
|
||||||
|
window.ScaleRaphael = function(container, width, height){
|
||||||
|
var wrapper = document.getElementById(container);
|
||||||
|
if (!wrapper.style.position) wrapper.style.position = "relative";
|
||||||
|
wrapper.style.width = width + "px";
|
||||||
|
wrapper.style.height = height + "px";
|
||||||
|
wrapper.style.overflow = "hidden";
|
||||||
|
var nestedWrapper;
|
||||||
|
|
||||||
|
if (Raphael.type == "VML"){
|
||||||
|
wrapper.innerHTML = "<rvml:group style='position : absolute; width: 1000px; height: 1000px; top: 0px; left: 0px' coordsize='1000,1000' class='rvml' id='vmlgroup'><\/rvml:group>";
|
||||||
|
nestedWrapper = document.getElementById("vmlgroup");
|
||||||
|
}else{
|
||||||
|
wrapper.innerHTML = "<div id='svggroup'><\/div>";
|
||||||
|
nestedWrapper = document.getElementById("svggroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
var paper = new Raphael(nestedWrapper, width, height);
|
||||||
|
var vmlDiv;
|
||||||
|
|
||||||
|
if (Raphael.type == "SVG"){
|
||||||
|
paper.canvas.setAttribute("viewBox", "0 0 "+width+" "+height);
|
||||||
|
}else{
|
||||||
|
vmlDiv = wrapper.getElementsByTagName("div")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
paper.changeSize = function(w, h, center, clipping){
|
||||||
|
clipping = !clipping;
|
||||||
|
|
||||||
|
var ratioW = w / width;
|
||||||
|
var ratioH = h / height;
|
||||||
|
var scale = ratioW < ratioH ? ratioW : ratioH;
|
||||||
|
|
||||||
|
var newHeight = parseInt(height * scale);
|
||||||
|
var newWidth = parseInt(width * scale);
|
||||||
|
|
||||||
|
if (Raphael.type == "VML"){
|
||||||
|
// scale the textpaths
|
||||||
|
var txt = document.getElementsByTagName("textpath");
|
||||||
|
for (var i in txt){
|
||||||
|
var curr = txt[i];
|
||||||
|
if (curr.style){
|
||||||
|
if(!curr._fontSize){
|
||||||
|
var mod = curr.style.font.split("px");
|
||||||
|
curr._fontSize = parseInt(mod[0]);
|
||||||
|
curr._font = mod[1];
|
||||||
|
}
|
||||||
|
curr.style.font = curr._fontSize * scale + "px" + curr._font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newSize;
|
||||||
|
|
||||||
|
if (newWidth < newHeight){
|
||||||
|
newSize = newWidth * 1000 / width;
|
||||||
|
}else{
|
||||||
|
newSize = newHeight * 1000 / height;
|
||||||
|
}
|
||||||
|
newSize = parseInt(newSize);
|
||||||
|
nestedWrapper.style.width = newSize + "px";
|
||||||
|
nestedWrapper.style.height = newSize + "px";
|
||||||
|
if (clipping){
|
||||||
|
nestedWrapper.style.left = parseInt((w - newWidth) / 2) + "px";
|
||||||
|
nestedWrapper.style.top = parseInt((h - newHeight) / 2) + "px";
|
||||||
|
}
|
||||||
|
vmlDiv.style.overflow = "visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipping){
|
||||||
|
newWidth = w;
|
||||||
|
newHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.style.width = newWidth + "px";
|
||||||
|
wrapper.style.height = newHeight + "px";
|
||||||
|
paper.setSize(newWidth, newHeight);
|
||||||
|
|
||||||
|
if (center){
|
||||||
|
wrapper.style.position = "absolute";
|
||||||
|
wrapper.style.left = parseInt((w - newWidth) / 2) + "px";
|
||||||
|
wrapper.style.top = parseInt((h - newHeight) / 2) + "px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paper.scaleAll = function(amount){
|
||||||
|
paper.changeSize(width * amount, height * amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
paper.changeSize(width, height);
|
||||||
|
|
||||||
|
paper.w = width;
|
||||||
|
paper.h = height;
|
||||||
|
|
||||||
|
return paper;
|
||||||
|
}
|
||||||
|
})();
|
19
labs/meteor-client/client/views/whiteboard/whiteboard.coffee
Normal file
19
labs/meteor-client/client/views/whiteboard/whiteboard.coffee
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Template.whiteboard.events
|
||||||
|
"click .drawShapes":() ->
|
||||||
|
alert "drawShapes"
|
||||||
|
wpm = new WhiteboardPaperModel('whiteboard-paper')
|
||||||
|
wpm.create()
|
||||||
|
wpm._displayPage("someSampleData")
|
||||||
|
# paper = new Raphael(document.getElementById('whiteboard-paper'), 500, 500);
|
||||||
|
# circle = paper.circle(100, 100, 80);
|
||||||
|
|
||||||
|
# console.log "shapes:" + Meteor.Shapes.find().fetch().length
|
||||||
|
for shape in Meteor.Shapes.find().fetch()
|
||||||
|
shapeType = shape.shape.payload.shape.shape_type
|
||||||
|
data = shape.shape.payload.shape.shape
|
||||||
|
console.log "shapeType=" + shapeType
|
||||||
|
console.log "data=" + JSON.stringify data
|
||||||
|
|
||||||
|
wpm.makeShape(shapeType, data)
|
||||||
|
wpm.updateShape(shapeType, data)
|
||||||
|
#circle = wpm.circle(100, 100, 80)
|
56
labs/meteor-client/client/views/whiteboard/whiteboard.css
Normal file
56
labs/meteor-client/client/views/whiteboard/whiteboard.css
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
color: rgb(220, 220, 220);
|
||||||
|
border-bottom: 1px solid rgb(100, 100, 100);
|
||||||
|
padding: 15px 5px 5px 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #heading {
|
||||||
|
float: left;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #heading {
|
||||||
|
float: left;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #heading h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 25px;
|
||||||
|
margin: 0px 0px 8px 0px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #heading h2 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #heading h2 a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px dashed white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#messages {
|
||||||
|
background: #f6f6f6;
|
||||||
|
position: fixed;
|
||||||
|
top: 60px;
|
||||||
|
right: 0px;
|
||||||
|
width: 200px;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
<template name="whiteboard">
|
<template name="whiteboard">
|
||||||
|
<input type="button" class="drawShapes" value="Draw!" />
|
||||||
{{#if getInSession "display_whiteboard"}}
|
{{#if getInSession "display_whiteboard"}}
|
||||||
<p>The whiteboard</p>
|
<p>The whiteboard</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<div id="whiteboard-paper">
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
# A base class for whiteboard tools
|
||||||
|
class @WhiteboardToolModel
|
||||||
|
|
||||||
|
initialize: (@paper) ->
|
||||||
|
console.log "paper:" + @paper
|
||||||
|
@gh = 0
|
||||||
|
@gw = 0
|
||||||
|
@obj = 0
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
@definition = []
|
||||||
|
|
||||||
|
#set the size of the paper
|
||||||
|
# @param {number} @gh gh parameter
|
||||||
|
# @param {number} @gw gw parameter
|
||||||
|
setPaperSize: (@gh, @gw) ->
|
||||||
|
|
||||||
|
setOffsets: (@xOffset, @yOffset) ->
|
||||||
|
|
||||||
|
setPaperDimensions: (@paperWidth, @paperHeight) ->
|
||||||
|
# TODO: can't we simply take the width and the height from `@paper`?
|
||||||
|
|
||||||
|
getDefinition: () ->
|
||||||
|
@definition
|
||||||
|
|
||||||
|
hide: () ->
|
||||||
|
@obj.hide() if @obj?
|
42
labs/meteor-client/client/whiteboard_models/utils.coffee
Normal file
42
labs/meteor-client/client/whiteboard_models/utils.coffee
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# General utility methods
|
||||||
|
|
||||||
|
Meteor.methods
|
||||||
|
# POST request using javascript
|
||||||
|
# @param {string} path path of submission
|
||||||
|
# @param {string} params parameters to submit
|
||||||
|
# @param {string} method method of submission ("post" is default)
|
||||||
|
# @return {undefined}
|
||||||
|
postToUrl: (path, params, method) ->
|
||||||
|
method = method or "post"
|
||||||
|
form = $("<form></form>")
|
||||||
|
form.attr
|
||||||
|
"method" : method,
|
||||||
|
"action" : path
|
||||||
|
for key of params
|
||||||
|
if params.hasOwnProperty(key)
|
||||||
|
$hiddenField = $ "input"
|
||||||
|
$hiddenField.attr
|
||||||
|
"type" : "hidden",
|
||||||
|
"name" : key,
|
||||||
|
"value" : params[key]
|
||||||
|
form.append $hiddenField
|
||||||
|
|
||||||
|
$('body').append form
|
||||||
|
form.submit()
|
||||||
|
|
||||||
|
# @param {string,int} stroke stroke color, can be a number (a hex converted to int) or a
|
||||||
|
# string (e.g. "#ffff00")
|
||||||
|
# @param {string,ing} thickness thickness as a number or string (e.g. "2" or "2px")
|
||||||
|
strokeAndThickness: (stroke, thickness) ->
|
||||||
|
stroke ?= "0"
|
||||||
|
thickness ?= "1"
|
||||||
|
r =
|
||||||
|
stroke: if stroke.toString().match(/\#.*/) then stroke else colourToHex(stroke)
|
||||||
|
"stroke-width": if thickness.toString().match(/.*px$/) then thickness else "#{thickness}px"
|
||||||
|
r
|
||||||
|
|
||||||
|
# Convert a color `value` as integer to a hex color (e.g. 255 to #0000ff)
|
||||||
|
colourToHex = (value) ->
|
||||||
|
hex = value.toString(16)
|
||||||
|
hex = "0" + hex while hex.length < 6
|
||||||
|
"##{hex}"
|
@ -0,0 +1,40 @@
|
|||||||
|
# The cursor/pointer in the whiteboard
|
||||||
|
class @WhiteboardCursorModel
|
||||||
|
|
||||||
|
constructor: (@paper, @radius=null, @color=null) ->
|
||||||
|
@radius ?= 3
|
||||||
|
@color ?= "#ff6666" # a pinkish red
|
||||||
|
@cursor = null
|
||||||
|
@paper
|
||||||
|
|
||||||
|
draw: () =>
|
||||||
|
@cursor = @paper.circle(0, 0, @radius)
|
||||||
|
@cursor.attr
|
||||||
|
"fill": @color
|
||||||
|
"stroke": @color
|
||||||
|
"opacity": "0.8"
|
||||||
|
$(@cursor.node).on "mousewheel", _.bind(@_onMouseWheel, @)
|
||||||
|
|
||||||
|
toFront: () ->
|
||||||
|
@cursor.toFront() if @cursor?
|
||||||
|
|
||||||
|
setRadius: (value) ->
|
||||||
|
if @cursor?
|
||||||
|
@cursor.attr r: value
|
||||||
|
|
||||||
|
setPosition: (x, y) ->
|
||||||
|
if @cursor?
|
||||||
|
@cursor.attr
|
||||||
|
cx: x
|
||||||
|
cy: y
|
||||||
|
|
||||||
|
undrag: () ->
|
||||||
|
@cursor.undrag() if @cursor?
|
||||||
|
|
||||||
|
drag: (onMove, onStart, onEnd, target=null) ->
|
||||||
|
if @cursor?
|
||||||
|
target or= @
|
||||||
|
@cursor.drag _.bind(onMove, target), _.bind(onStart, target), _.bind(onEnd, target)
|
||||||
|
|
||||||
|
_onMouseWheel: (e, delta) ->
|
||||||
|
@trigger("cursor:mousewheel", e, delta)
|
@ -0,0 +1,155 @@
|
|||||||
|
# An ellipse in the whiteboard
|
||||||
|
class @WhiteboardEllipseModel extends WhiteboardToolModel
|
||||||
|
|
||||||
|
constructor: (@paper) ->
|
||||||
|
super @paper
|
||||||
|
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
# format: top left x, top left y, bottom right x, bottom right y, stroke color, thickness
|
||||||
|
@definition = [0, 0, 0, 0, "#000", "0px"]
|
||||||
|
|
||||||
|
# @ellipseX = null
|
||||||
|
# @ellipseY = null
|
||||||
|
|
||||||
|
# Make an ellipse on the whiteboard
|
||||||
|
# @param {[type]} x the x value of the top left corner
|
||||||
|
# @param {[type]} y the y value of the top left corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
make: (info) ->
|
||||||
|
x = info.payload.data.coordinate.first_x
|
||||||
|
y = info.payload.data.coordinate.first_y
|
||||||
|
color = info.payload.data.line.color
|
||||||
|
thickness = info.payload.data.line.weight
|
||||||
|
|
||||||
|
@obj = @paper.ellipse(x * @gw + @xOffset, y * @gh + @yOffset, 0, 0)
|
||||||
|
@obj.attr Utils.strokeAndThickness(color, thickness)
|
||||||
|
@definition =
|
||||||
|
shape: "ellipse"
|
||||||
|
data: [x, y, y, x, @obj.attrs["stroke"], @obj.attrs["stroke-width"]]
|
||||||
|
@obj
|
||||||
|
|
||||||
|
# Update ellipse drawn
|
||||||
|
# @param {number} x1 the x value of the top left corner in percent of current slide size
|
||||||
|
# @param {number} y1 the y value of the top left corner in percent of current slide size
|
||||||
|
# @param {number} x2 the x value of the bottom right corner in percent of current slide size
|
||||||
|
# @param {number} y2 the y value of the bottom right corner in percent of current slide size
|
||||||
|
# @param {boolean} square (draw a circle or not
|
||||||
|
update: (info) ->
|
||||||
|
x1 = info.payload.data.coordinate.first_x
|
||||||
|
y1 = info.payload.data.coordinate.first_y
|
||||||
|
x2 = info.payload.data.coordinate.last_x
|
||||||
|
y2 = info.payload.data.coordinate.last_y
|
||||||
|
circle = info.payload.data.square
|
||||||
|
|
||||||
|
if @obj?
|
||||||
|
|
||||||
|
[x1, x2] = [x2, x1] if x2 < x1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if y2 < y1
|
||||||
|
[y1, y2] = [y2, y1]
|
||||||
|
reversed = true
|
||||||
|
|
||||||
|
if circle
|
||||||
|
if reversed #if reveresed, the y1 coordinate gets updated, not the y2 coordinate
|
||||||
|
y1 = y2 - (x2 - x1) * @gw / @gh
|
||||||
|
else
|
||||||
|
y2 = y1 + (x2 - x1) * @gw / @gh
|
||||||
|
|
||||||
|
#if the control key is pressed then the width and height of the ellipse are equal (a circle)
|
||||||
|
#we calculate this by making the y2 coord equal to the y1 coord plus the width of x2-x1 and corrected for the slide size
|
||||||
|
|
||||||
|
|
||||||
|
coords =
|
||||||
|
x1: x1
|
||||||
|
x2: x2
|
||||||
|
y1: y1
|
||||||
|
y2: y2
|
||||||
|
|
||||||
|
console.log(coords)
|
||||||
|
|
||||||
|
rx = (x2 - x1) / 2
|
||||||
|
ry = (y2 - y1) / 2
|
||||||
|
|
||||||
|
|
||||||
|
r=
|
||||||
|
rx: rx * @gw
|
||||||
|
ry: ry * @gh
|
||||||
|
cx: (rx + x1) * @gw + @xOffset
|
||||||
|
cy: (ry + y1) * @gh + @yOffset
|
||||||
|
console.log "------------>", r
|
||||||
|
@obj.attr(r)
|
||||||
|
|
||||||
|
console.log( "@gw: " + @gw + "\n@gh: " + @gh + "\n@xOffset: " + @xOffset + "\n@yOffset: " + @yOffset );
|
||||||
|
# we need to update all these values, specially for when shapes are drawn backwards
|
||||||
|
@definition.data[0] = x1
|
||||||
|
@definition.data[1] = y1
|
||||||
|
@definition.data[2] = x2
|
||||||
|
@definition.data[3] = y2
|
||||||
|
|
||||||
|
# Draw an ellipse on the whiteboard
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
draw: (x1, y1, x2, y2, colour, thickness) ->
|
||||||
|
[x1, x2] = [x2, x1] if x2 < x1
|
||||||
|
[y1, y2] = [y2, y1] if y2 < y1
|
||||||
|
|
||||||
|
rx = (x2 - x1) / 2
|
||||||
|
ry = (y2 - y1) / 2
|
||||||
|
x = (rx + x1) * @gw + @xOffset
|
||||||
|
y = (ry + y1) * @gh + @yOffset
|
||||||
|
elip = @paper.ellipse(x, y, rx * @gw, ry * @gh)
|
||||||
|
elip.attr Utils.strokeAndThickness(colour, thickness)
|
||||||
|
elip
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnStart: (x, y) ->
|
||||||
|
# sx = (@paperWidth - @gw) / 2
|
||||||
|
# sy = (@paperHeight - @gh) / 2
|
||||||
|
# # find the x and y values in relation to the whiteboard
|
||||||
|
# @ellipseX = (x - @containerOffsetLeft - sx + @xOffset)
|
||||||
|
# @ellipseY = (y - @containerOffsetTop - sy + @yOffset)
|
||||||
|
# globals.connection.emitMakeShape "ellipse",
|
||||||
|
# [ @ellipseX / @paperWidth, @ellipseY / @paperHeight, @currentColour, @currentThickness ]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnMove: (dx, dy, x, y, e) ->
|
||||||
|
# # if shift is pressed, draw a circle instead of ellipse
|
||||||
|
# dy = dx if @shiftPressed
|
||||||
|
# dx = dx / 2
|
||||||
|
# dy = dy / 2
|
||||||
|
# # adjust for negative values as well
|
||||||
|
# x = @ellipseX + dx
|
||||||
|
# y = @ellipseY + dy
|
||||||
|
# dx = (if dx < 0 then -dx else dx)
|
||||||
|
# dy = (if dy < 0 then -dy else dy)
|
||||||
|
# globals.connection.emitUpdateShape "ellipse",
|
||||||
|
# [ x / @paperWidth, y / @paperHeight, dx / @paperWidth, dy / @paperHeight ]
|
||||||
|
|
||||||
|
# When releasing the mouse after drawing the ellipse
|
||||||
|
# @param {Event} e the mouse event
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnStop: (e) ->
|
||||||
|
# attrs = undefined
|
||||||
|
# attrs = @currentEllipse.attrs if @currentEllipse?
|
||||||
|
# if attrs?
|
||||||
|
# globals.connection.emitPublishShape "ellipse",
|
||||||
|
# [ attrs.cx / @gw, attrs.cy / @gh, attrs.rx / @gw, attrs.ry / @gh,
|
||||||
|
# @currentColour, @currentThickness ]
|
||||||
|
# @currentEllipse = null # late updates will be blocked by this
|
@ -0,0 +1,208 @@
|
|||||||
|
MAX_PATHS_IN_SEQUENCE = 30
|
||||||
|
|
||||||
|
# A line in the whiteboard
|
||||||
|
# Note: is used to draw lines from the pencil tool and from the line tool, this is why some
|
||||||
|
# methods can receive different set of parameters.
|
||||||
|
# TODO: Maybe this should be split in WhiteboardPathModel for the pencil and
|
||||||
|
# WhiteboardLineModel for the line tool
|
||||||
|
class @WhiteboardLineModel extends WhiteboardToolModel
|
||||||
|
|
||||||
|
constructor: (@paper) ->
|
||||||
|
super @paper
|
||||||
|
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
# format: svg path, stroke color, thickness
|
||||||
|
@definition = ["", "#000", "0px"]
|
||||||
|
|
||||||
|
# @lineX = null
|
||||||
|
# @lineY = null
|
||||||
|
|
||||||
|
# Creates a line in the paper
|
||||||
|
# @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
|
||||||
|
make: (info) ->
|
||||||
|
console.log "in line MAKE(info): " + info
|
||||||
|
|
||||||
|
x = info.payload.data.coordinate.first_x
|
||||||
|
y = info.payload.data.coordinate.first_y
|
||||||
|
color = info.payload.data.line.color
|
||||||
|
thickness = info.payload.data.line.weight
|
||||||
|
|
||||||
|
x1 = x * @gw + @xOffset
|
||||||
|
y1 = y * @gh + @yOffset
|
||||||
|
path = "M" + x1 + " " + y1 + " L" + x1 + " " + y1
|
||||||
|
pathPercent = "M" + x + " " + y + " L" + x + " " + y
|
||||||
|
@obj = @paper.path(path)
|
||||||
|
@obj.attr Utils.strokeAndThickness(color, thickness)
|
||||||
|
@obj.attr({"stroke-linejoin": "round"})
|
||||||
|
|
||||||
|
@definition =
|
||||||
|
shape: "path"
|
||||||
|
data: [pathPercent, @obj.attrs["stroke"], @obj.attrs["stroke-width"]]
|
||||||
|
|
||||||
|
@obj
|
||||||
|
|
||||||
|
# Update the line dimensions
|
||||||
|
# @param {number} x1 1) the x of the first point
|
||||||
|
# 2) the next x point to be added to the line
|
||||||
|
# @param {number} y1 1) the y of the first point
|
||||||
|
# 2) the next y point to be added to the line
|
||||||
|
# @param {number,boolean} x2 1) the x of the second point
|
||||||
|
# 2) true if the line should be added to the current line,
|
||||||
|
# false if it should replace the last point
|
||||||
|
# @param {number} y2 1) the y of the second point
|
||||||
|
# 2) undefined
|
||||||
|
update: (info) ->
|
||||||
|
console.log "in line-UPDATE(info): " + info
|
||||||
|
|
||||||
|
x1 = info.payload.data.coordinate.first_x
|
||||||
|
y1 = info.payload.data.coordinate.first_y
|
||||||
|
x2 = info.payload.data.coordinate.last_x
|
||||||
|
y2 = info.payload.data.coordinate.last_y
|
||||||
|
|
||||||
|
if @obj?
|
||||||
|
|
||||||
|
# if adding points from the pencil
|
||||||
|
if _.isBoolean(info.adding)
|
||||||
|
add = info.adding
|
||||||
|
|
||||||
|
pathPercent = "L" + x1 + " " + y1
|
||||||
|
@definition.data[0] += pathPercent
|
||||||
|
|
||||||
|
x1 = x1 * @gw + @xOffset
|
||||||
|
y1 = y1 * @gh + @yOffset
|
||||||
|
|
||||||
|
# if adding to the line
|
||||||
|
if add
|
||||||
|
path = @obj.attrs.path + "L" + x1 + " " + y1
|
||||||
|
@obj.attr path: path
|
||||||
|
|
||||||
|
# if simply updating the last portion (for drawing a straight line)
|
||||||
|
else
|
||||||
|
@obj.attrs.path.pop()
|
||||||
|
path = @obj.attrs.path.join(" ")
|
||||||
|
path = path + "L" + x1 + " " + y1
|
||||||
|
@obj.attr path: path
|
||||||
|
|
||||||
|
# adding lines from the line tool
|
||||||
|
else
|
||||||
|
path = @_buildPath(x1, y1, x2, y2)
|
||||||
|
@definition.data[0] = path
|
||||||
|
|
||||||
|
path = @_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset)
|
||||||
|
@obj.attr path: path
|
||||||
|
|
||||||
|
# Draw a line on the paper
|
||||||
|
# @param {number,string} x1 1) the x value of the first point
|
||||||
|
# 2) the string path
|
||||||
|
# @param {number,string} y1 1) the y value of the first point
|
||||||
|
# 2) the colour
|
||||||
|
# @param {number} x2 1) the x value of the second point
|
||||||
|
# 2) the thickness
|
||||||
|
# @param {number} y2 1) the y value of the second point
|
||||||
|
# 2) undefined
|
||||||
|
# @param {string} colour 1) the colour of the shape to be drawn
|
||||||
|
# 2) undefined
|
||||||
|
# @param {number} thickness 1) the thickness of the line to be drawn
|
||||||
|
# 2) undefined
|
||||||
|
draw: (x1, y1, x2, y2, colour, thickness) ->
|
||||||
|
|
||||||
|
# if the drawing is from the pencil tool, it comes as a path first
|
||||||
|
if _.isString(x1)
|
||||||
|
colour = y1
|
||||||
|
thickness = x2
|
||||||
|
path = x1
|
||||||
|
|
||||||
|
# if the drawing is from the line tool, it comes with two points
|
||||||
|
else
|
||||||
|
path = @_buildPath(x1, y1, x2, y2)
|
||||||
|
|
||||||
|
line = @paper.path(@_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset))
|
||||||
|
line.attr Utils.strokeAndThickness(colour, thickness)
|
||||||
|
line.attr({"stroke-linejoin": "round"})
|
||||||
|
line
|
||||||
|
|
||||||
|
# When dragging for drawing lines starts
|
||||||
|
# @param {number} x the x value of the cursor
|
||||||
|
# @param {number} y the y value of the cursor
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnStart: (x, y) ->
|
||||||
|
# # find the x and y values in relation to the whiteboard
|
||||||
|
# sx = (@paperWidth - @gw) / 2
|
||||||
|
# sy = (@paperHeight - @gh) / 2
|
||||||
|
# @lineX = x - @containerOffsetLeft - sx + @xOffset
|
||||||
|
# @lineY = y - @containerOffsetTop - sy + @yOffset
|
||||||
|
# values = [ @lineX / @paperWidth, @lineY / @paperHeight, @currentColour, @currentThickness ]
|
||||||
|
# globals.connection.emitMakeShape "line", values
|
||||||
|
|
||||||
|
# As line drawing drag continues
|
||||||
|
# @param {number} dx the difference between the x value from _lineDragStart and now
|
||||||
|
# @param {number} dy the difference between the y value from _lineDragStart and now
|
||||||
|
# @param {number} x the x value of the cursor
|
||||||
|
# @param {number} y the y value of the cursor
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnMove: (dx, dy, x, y) ->
|
||||||
|
# sx = (@paperWidth - @gw) / 2
|
||||||
|
# sy = (@paperHeight - @gh) / 2
|
||||||
|
# [cx, cy] = @_currentSlideOffsets()
|
||||||
|
# # find the x and y values in relation to the whiteboard
|
||||||
|
# @cx2 = x - @containerOffsetLeft - sx + @xOffset
|
||||||
|
# @cy2 = y - @containerOffsetTop - sy + @yOffset
|
||||||
|
# if @shiftPressed
|
||||||
|
# globals.connection.emitUpdateShape "line", [ @cx2 / @paperWidth, @cy2 / @paperHeight, false ]
|
||||||
|
# else
|
||||||
|
# @currentPathCount++
|
||||||
|
# if @currentPathCount < MAX_PATHS_IN_SEQUENCE
|
||||||
|
# globals.connection.emitUpdateShape "line", [ @cx2 / @paperHeight, @cy2 / @paperHeight, true ]
|
||||||
|
# else if @obj?
|
||||||
|
# @currentPathCount = 0
|
||||||
|
# # save the last path of the line
|
||||||
|
# @obj.attrs.path.pop()
|
||||||
|
# path = @obj.attrs.path.join(" ")
|
||||||
|
# @obj.attr path: (path + "L" + @lineX + " " + @lineY)
|
||||||
|
|
||||||
|
# # scale the path appropriately before sending
|
||||||
|
# pathStr = @obj.attrs.path.join(",")
|
||||||
|
# globals.connection.emitPublishShape "path",
|
||||||
|
# [ @_scaleLinePath(pathStr, 1 / @gw, 1 / @gh),
|
||||||
|
# @currentColour, @currentThickness ]
|
||||||
|
# globals.connection.emitMakeShape "line",
|
||||||
|
# [ @lineX / @paperWidth, @lineY / @paperHeight, @currentColour, @currentThickness ]
|
||||||
|
# @lineX = @cx2
|
||||||
|
# @lineY = @cy2
|
||||||
|
|
||||||
|
# Drawing line has ended
|
||||||
|
# @param {Event} e the mouse event
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnEnd: (e) ->
|
||||||
|
# if @obj?
|
||||||
|
# path = @obj.attrs.path
|
||||||
|
# @obj = null # any late updates will be blocked by this
|
||||||
|
# # scale the path appropriately before sending
|
||||||
|
# globals.connection.emitPublishShape "path",
|
||||||
|
# [ @_scaleLinePath(path.join(","), 1 / @gw, 1 / @gh),
|
||||||
|
# @currentColour, @currentThickness ]
|
||||||
|
|
||||||
|
_buildPath: (x1, y1, x2, y2) ->
|
||||||
|
"M#{x1} #{y1}L#{x2} #{y2}"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
_scaleLinePath: (string, w, h, xOffset=0, yOffset=0) ->
|
||||||
|
path = null
|
||||||
|
points = string.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 + xOffset) + "," + (points[j + 1] * h + yOffset)
|
||||||
|
else
|
||||||
|
path = "M" + (points[j] * w + xOffset) + "," + (points[j + 1] * h + yOffset)
|
||||||
|
j += 2
|
||||||
|
path
|
@ -0,0 +1,827 @@
|
|||||||
|
# "Paper" which is the Raphael term for the entire SVG object on the webpage.
|
||||||
|
# This class deals with this SVG component only.
|
||||||
|
class @WhiteboardPaperModel
|
||||||
|
|
||||||
|
# Container must be a DOM element
|
||||||
|
constructor: (@container) ->
|
||||||
|
console.log "paper in WhiteboardPaperModel =" + @container
|
||||||
|
# a WhiteboardCursorModel
|
||||||
|
@cursor = null
|
||||||
|
|
||||||
|
# all slides in the presentation indexed by url
|
||||||
|
@slides = {}
|
||||||
|
# the slide being shown
|
||||||
|
@currentSlide = null
|
||||||
|
|
||||||
|
@fitToPage = true
|
||||||
|
@panX = null
|
||||||
|
@panY = null
|
||||||
|
|
||||||
|
# a raphaeljs set with all the shapes in the current slide
|
||||||
|
@currentShapes = null
|
||||||
|
# a list of shapes as passed to this client when it receives `all_slides`
|
||||||
|
# (se we are able to redraw the shapes whenever needed)
|
||||||
|
@currentShapesDefinitions = []
|
||||||
|
# pointers to the current shapes being drawn
|
||||||
|
@currentLine = null
|
||||||
|
@currentRect = null
|
||||||
|
@currentEllipse = null
|
||||||
|
@currentTriangle = null
|
||||||
|
@currentText = null
|
||||||
|
|
||||||
|
@zoomLevel = 1
|
||||||
|
@shiftPressed = false
|
||||||
|
@currentPathCount = 0
|
||||||
|
|
||||||
|
$(window).on "resize.whiteboard_paper", _.bind(@_onWindowResize, @)
|
||||||
|
$(document).on "keydown.whiteboard_paper", _.bind(@_onKeyDown, @)
|
||||||
|
$(document).on "keyup.whiteboard_paper", _.bind(@_onKeyUp, @)
|
||||||
|
|
||||||
|
# Bind to the event triggered when the client connects to the server
|
||||||
|
# if globals.connection.isConnected()
|
||||||
|
# @_registerEvents()
|
||||||
|
# else
|
||||||
|
# globals.events.on "connection:connected", =>
|
||||||
|
# @_registerEvents()
|
||||||
|
|
||||||
|
# Override the close() to unbind events.
|
||||||
|
unbindEvents: ->
|
||||||
|
$(window).off "resize.whiteboard_paper"
|
||||||
|
$(document).off "keydown.whiteboard_paper"
|
||||||
|
$(document).off "keyup.whiteboard_paper"
|
||||||
|
# TODO: other events are being used in the code and should be off() here
|
||||||
|
|
||||||
|
# Initializes the paper in the page.
|
||||||
|
# Can't do these things in initialize() because by then some elements
|
||||||
|
# are not yet created in the page.
|
||||||
|
create: ->
|
||||||
|
alert "create object"
|
||||||
|
# paper is embedded within the div#slide of the page.
|
||||||
|
console.log ("@container=" + @container)
|
||||||
|
@raphaelObj ?= ScaleRaphael(@container, "500", "500")
|
||||||
|
@raphaelObj.canvas.setAttribute "preserveAspectRatio", "xMinYMin slice"
|
||||||
|
|
||||||
|
@cursor = new WhiteboardCursorModel(@raphaelObj)
|
||||||
|
@cursor.draw()
|
||||||
|
#@cursor.on "cursor:mousewheel", _.bind(@_zoomSlide, @)
|
||||||
|
|
||||||
|
if @slides
|
||||||
|
@rebuild()
|
||||||
|
else
|
||||||
|
@slides = {} # if previously loaded
|
||||||
|
unless navigator.userAgent.indexOf("Firefox") is -1
|
||||||
|
@raphaelObj.renderfix()
|
||||||
|
|
||||||
|
# initializing border around slide to cover up areas which shouldnt show
|
||||||
|
@borders = {}
|
||||||
|
for border in ['left', 'right', 'top', 'bottom']
|
||||||
|
@borders[border] = @raphaelObj.rect(0, 0, 0, 0)
|
||||||
|
@borders[border].attr("fill", "#ababab")
|
||||||
|
@borders[border].attr( {stroke:"#ababab"} )
|
||||||
|
|
||||||
|
@raphaelObj
|
||||||
|
|
||||||
|
# Re-add the images to the paper that are found
|
||||||
|
# in the slides array (an object of urls and dimensions).
|
||||||
|
rebuild: ->
|
||||||
|
@currentSlide = null
|
||||||
|
for url of @slides
|
||||||
|
if @slides.hasOwnProperty(url)
|
||||||
|
@addImageToPaper url, @slides[url].getWidth(), @slides[url].getHeight()
|
||||||
|
|
||||||
|
# A wrapper around ScaleRaphael's `changeSize()` method, more details at:
|
||||||
|
# http://www.shapevent.com/scaleraphael/
|
||||||
|
# Also makes sure that the images are redraw in the canvas so they are actually resized.
|
||||||
|
changeSize: (windowWidth, windowHeight, center=true, clipping=false) ->
|
||||||
|
if @raphaelObj?
|
||||||
|
@raphaelObj.changeSize(windowWidth, windowHeight, center, clipping)
|
||||||
|
|
||||||
|
# TODO: we can scale the slides and drawings instead of re-adding them, but the logic
|
||||||
|
# will change quite a bit
|
||||||
|
# slides
|
||||||
|
slidesTmp = _.clone(@slides)
|
||||||
|
urlTmp = @currentSlide
|
||||||
|
@removeAllImagesFromPaper()
|
||||||
|
@slides = slidesTmp
|
||||||
|
@rebuild()
|
||||||
|
@showImageFromPaper(urlTmp?.url)
|
||||||
|
# drawings
|
||||||
|
tmp = _.clone(@currentShapesDefinitions)
|
||||||
|
@clearShapes()
|
||||||
|
@drawListOfShapes(tmp)
|
||||||
|
|
||||||
|
# Add an image to the paper.
|
||||||
|
# @param {string} url the URL of the image to add to the paper
|
||||||
|
# @param {number} width the width of the image (in pixels)
|
||||||
|
# @param {number} height the height of the image (in pixels)
|
||||||
|
# @return {Raphael.image} the image object added to the whiteboard
|
||||||
|
addImageToPaper: (url, width, height) ->
|
||||||
|
@_updateContainerDimensions()
|
||||||
|
|
||||||
|
if @fitToPage
|
||||||
|
# solve for the ratio of what length is going to fit more than the other
|
||||||
|
max = Math.max(width / @containerWidth, height / @containerHeight)
|
||||||
|
# fit it all in appropriately
|
||||||
|
# TODO: temporary solution
|
||||||
|
url = @_slideUrl(url)
|
||||||
|
sw = width / max
|
||||||
|
sh = height / max
|
||||||
|
cx = (@containerWidth / 2) - (width / 2)
|
||||||
|
cy = (@containerHeight / 2) - (height / 2)
|
||||||
|
img = @raphaelObj.image(url, cx, cy, width, height)
|
||||||
|
originalWidth = width
|
||||||
|
originalHeight = height
|
||||||
|
else
|
||||||
|
# fit to width
|
||||||
|
alert "no fit"
|
||||||
|
# assume it will fit width ways
|
||||||
|
sw = width / wr
|
||||||
|
sh = height / wr
|
||||||
|
wr = width / @containerWidth
|
||||||
|
originalWidth = sw
|
||||||
|
originalHeight = sh
|
||||||
|
sw = width / wr
|
||||||
|
sh = height / wr
|
||||||
|
img = @raphaelObj.image(url, cx = 0, cy = 0, sw, sh)
|
||||||
|
|
||||||
|
# sw slide width as percentage of original width of paper
|
||||||
|
# sh slide height as a percentage of original height of paper
|
||||||
|
# x-offset from top left corner as percentage of original width of paper
|
||||||
|
# y-offset from top left corner as percentage of original height of paper
|
||||||
|
@slides[url] = new WhiteboardSlideModel(img.id, url, img, originalWidth, originalHeight, sw, sh, cx, cy)
|
||||||
|
|
||||||
|
unless @currentSlide?
|
||||||
|
img.toBack()
|
||||||
|
@currentSlide = @slides[url]
|
||||||
|
else if @currentSlide.url is url
|
||||||
|
img.toBack()
|
||||||
|
else
|
||||||
|
img.hide()
|
||||||
|
$(@container).on "mousemove", _.bind(@_onMouseMove, @)
|
||||||
|
$(@container).on "mousewheel", _.bind(@_zoomSlide, @)
|
||||||
|
# TODO $(img.node).bind "mousewheel", zoomSlide
|
||||||
|
#@trigger('paper:image:added', img)
|
||||||
|
|
||||||
|
# TODO: other places might also required an update in these dimensions
|
||||||
|
@_updateContainerDimensions()
|
||||||
|
|
||||||
|
img
|
||||||
|
|
||||||
|
# Removes all the images from the Raphael paper.
|
||||||
|
removeAllImagesFromPaper: ->
|
||||||
|
for url of @slides
|
||||||
|
if @slides.hasOwnProperty(url)
|
||||||
|
@raphaelObj.getById(@slides[url].getId()).remove()
|
||||||
|
@trigger('paper:image:removed', @slides[url].getId())
|
||||||
|
@slides = {}
|
||||||
|
@currentSlide = null
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
showImageFromPaper: (url) ->
|
||||||
|
# TODO: temporary solution
|
||||||
|
url = @_slideUrl(url)
|
||||||
|
if not @currentSlide? or (@slides[url]? and @currentSlide.url isnt url)
|
||||||
|
@_hideImageFromPaper(@currentSlide.url) if @currentSlide?
|
||||||
|
next = @_getImageFromPaper(url)
|
||||||
|
if next
|
||||||
|
next.show()
|
||||||
|
next.toFront()
|
||||||
|
@currentShapes.forEach (element) ->
|
||||||
|
element.toFront()
|
||||||
|
@cursor.toFront()
|
||||||
|
@currentSlide = @slides[url]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: not working as it should
|
||||||
|
updatePaperFromServer: (cx_, cy_, sw_, sh_) ->
|
||||||
|
# # if updating the slide size (zooming!)
|
||||||
|
# [slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
# if sw_ and sh_
|
||||||
|
# @raphaelObj.setViewBox cx_ * slideWidth, cy_ * slideHeight, sw_ * slideWidth, sh_ * slideHeight,
|
||||||
|
# sw = slideWidth / sw_
|
||||||
|
# sh = slideHeight / sh_
|
||||||
|
# # just panning, so use old slide size values
|
||||||
|
# else
|
||||||
|
# [sw, sh] = @_currentSlideDimensions()
|
||||||
|
# @raphaelObj.setViewBox cx_ * slideWidth, cy_ * slideHeight, @raphaelObj._viewBox[2], @raphaelObj._viewBox[3]
|
||||||
|
|
||||||
|
# # update corners
|
||||||
|
# cx = cx_ * sw
|
||||||
|
# cy = cy_ * sh
|
||||||
|
# # update position of svg object in the window
|
||||||
|
# sx = (@containerWidth - slideWidth) / 2
|
||||||
|
# sy = (@containerHeight - slideHeight) / 2
|
||||||
|
# sy = 0 if sy < 0
|
||||||
|
# @raphaelObj.canvas.style.left = sx + "px"
|
||||||
|
# @raphaelObj.canvas.style.top = sy + "px"
|
||||||
|
# @raphaelObj.setSize slideWidth - 2, slideHeight - 2
|
||||||
|
|
||||||
|
# # update zoom level and cursor position
|
||||||
|
# z = @raphaelObj._viewBox[2] / slideWidth
|
||||||
|
# @zoomLevel = z
|
||||||
|
# dcr = 1
|
||||||
|
# @cursor.setRadius(dcr * z)
|
||||||
|
|
||||||
|
# # force the slice attribute despite Raphael changing it
|
||||||
|
# @raphaelObj.canvas.setAttribute "preserveAspectRatio", "xMinYMin slice"
|
||||||
|
|
||||||
|
# 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}
|
||||||
|
setCurrentTool: (tool) ->
|
||||||
|
@currentTool = tool
|
||||||
|
console.log "setting current tool to", tool
|
||||||
|
switch tool
|
||||||
|
when "path", "line"
|
||||||
|
@cursor.undrag()
|
||||||
|
@currentLine = @_createTool(tool)
|
||||||
|
@cursor.drag(@currentLine.dragOnMove, @currentLine.dragOnStart, @currentLine.dragOnEnd)
|
||||||
|
when "rect"
|
||||||
|
@cursor.undrag()
|
||||||
|
@currentRect = @_createTool(tool)
|
||||||
|
@cursor.drag(@currentRect.dragOnMove, @currentRect.dragOnStart, @currentRect.dragOnEnd)
|
||||||
|
|
||||||
|
# TODO: the shapes below are still in the old format
|
||||||
|
# when "panzoom"
|
||||||
|
# @cursor.undrag()
|
||||||
|
# @cursor.drag _.bind(@_panDragging, @),
|
||||||
|
# _.bind(@_panGo, @), _.bind(@_panStop, @)
|
||||||
|
# when "ellipse"
|
||||||
|
# @cursor.undrag()
|
||||||
|
# @cursor.drag _.bind(@_ellipseDragging, @),
|
||||||
|
# _.bind(@_ellipseDragStart, @), _.bind(@_ellipseDragStop, @)
|
||||||
|
# when "text"
|
||||||
|
# @cursor.undrag()
|
||||||
|
# @cursor.drag _.bind(@_rectDragging, @),
|
||||||
|
# _.bind(@_textStart, @), _.bind(@_textStop, @)
|
||||||
|
else
|
||||||
|
console.log "ERROR: Cannot set invalid tool:", tool
|
||||||
|
|
||||||
|
# Sets the fit to page.
|
||||||
|
# @param {boolean} value If true fit to page. If false fit to width.
|
||||||
|
# TODO: not really working as it should be
|
||||||
|
setFitToPage: (value) ->
|
||||||
|
@fitToPage = value
|
||||||
|
|
||||||
|
# TODO: we can scale the slides and drawings instead of re-adding them, but the logic
|
||||||
|
# will change quite a bit
|
||||||
|
temp = @slides
|
||||||
|
@removeAllImagesFromPaper()
|
||||||
|
@slides = temp
|
||||||
|
# re-add all the images as they should fit differently
|
||||||
|
@rebuild()
|
||||||
|
|
||||||
|
# set to default zoom level
|
||||||
|
globals.connection.emitPaperUpdate 0, 0, 1, 1
|
||||||
|
# get the shapes to reprocess
|
||||||
|
globals.connection.emitAllShapes()
|
||||||
|
|
||||||
|
# Socket response - Update zoom variables and viewbox
|
||||||
|
# @param {number} d the delta value from the scroll event
|
||||||
|
# @return {undefined}
|
||||||
|
setZoom: (d) ->
|
||||||
|
step = 0.05 # step size
|
||||||
|
if d < 0
|
||||||
|
@zoomLevel += step # zooming out
|
||||||
|
else
|
||||||
|
@zoomLevel -= step # zooming in
|
||||||
|
|
||||||
|
[sw, sh] = @_currentSlideDimensions()
|
||||||
|
[cx, cy] = @_currentSlideOffsets()
|
||||||
|
x = cx / sw
|
||||||
|
y = cy / sh
|
||||||
|
# cannot zoom out further than 100%
|
||||||
|
z = (if @zoomLevel > 1 then 1 else @zoomLevel)
|
||||||
|
# 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)
|
||||||
|
globals.connection.emitPaperUpdate x, y, z, z # send update to all clients
|
||||||
|
|
||||||
|
stopPanning: ->
|
||||||
|
# nothing to do
|
||||||
|
|
||||||
|
# Draws an array of shapes to the paper.
|
||||||
|
# @param {array} shapes the array of shapes to draw
|
||||||
|
drawListOfShapes: (shapes) ->
|
||||||
|
@currentShapesDefinitions = shapes
|
||||||
|
@currentShapes = @raphaelObj.set()
|
||||||
|
for shape in shapes
|
||||||
|
data = if _.isString(shape.data) then JSON.parse(shape.data) else shape.data
|
||||||
|
tool = @_createTool(shape.shape)
|
||||||
|
if tool?
|
||||||
|
@currentShapes.push tool.draw.apply(tool, data)
|
||||||
|
else
|
||||||
|
console.log "shape not recognized at drawListOfShapes", shape
|
||||||
|
|
||||||
|
# make sure the cursor is still on top
|
||||||
|
@cursor.toFront()
|
||||||
|
|
||||||
|
#Changes the currently displayed presentation (if any) with this one
|
||||||
|
#@param {object} containing the "presentation" object -id,name,pages[]
|
||||||
|
sharePresentation: (data) ->
|
||||||
|
globals.events.trigger("connection:all_slides", data.payload)
|
||||||
|
|
||||||
|
# Clear all shapes from this paper.
|
||||||
|
clearShapes: ->
|
||||||
|
if @currentShapes?
|
||||||
|
@currentShapes.forEach (element) ->
|
||||||
|
element.remove()
|
||||||
|
@currentShapes = []
|
||||||
|
@currentShapesDefinitions = []
|
||||||
|
|
||||||
|
|
||||||
|
# Updated a shape `shape` with the data in `data`.
|
||||||
|
# TODO: check if the objects exist before calling update, if they don't they should be created
|
||||||
|
updateShape: (shape, data) ->
|
||||||
|
alert "updating a " + shape
|
||||||
|
switch shape
|
||||||
|
when "line"
|
||||||
|
@currentLine.update(data)
|
||||||
|
when "rectangle"
|
||||||
|
@currentRect.update(data)
|
||||||
|
when "ellipse"
|
||||||
|
@currentEllipse.update(data)
|
||||||
|
when "triangle"
|
||||||
|
@currentTriangle.update(data)
|
||||||
|
when "text"
|
||||||
|
@currentText.update.apply(@currentText, data)
|
||||||
|
else
|
||||||
|
console.log "shape not recognized at updateShape", shape
|
||||||
|
|
||||||
|
# Make a shape `shape` with the data in `data`.
|
||||||
|
makeShape: (shape, data) ->
|
||||||
|
alert "making a " + shape
|
||||||
|
tool = null
|
||||||
|
switch shape
|
||||||
|
when "path", "line"
|
||||||
|
@currentLine = @_createTool(shape)
|
||||||
|
toolModel = @currentLine
|
||||||
|
tool = @currentLine.make(data)
|
||||||
|
when "rectangle"
|
||||||
|
@currentRect = @_createTool(shape)
|
||||||
|
toolModel = @currentRect
|
||||||
|
tool = @currentRect.make(data)
|
||||||
|
when "ellipse"
|
||||||
|
@currentEllipse = @_createTool(shape)
|
||||||
|
toolModel = @currentEllipse
|
||||||
|
tool = @currentEllipse.make(data)
|
||||||
|
when "triangle"
|
||||||
|
@currentTriangle = @_createTool(shape)
|
||||||
|
toolModel = @currentTriangle
|
||||||
|
tool = @currentTriangle.make(data)
|
||||||
|
when "text"
|
||||||
|
@currentText = @_createTool(shape)
|
||||||
|
toolModel = @currentText
|
||||||
|
tool = @currentText.make.apply(@currentText, data)
|
||||||
|
else
|
||||||
|
console.log "shape not recognized at makeShape", shape
|
||||||
|
if tool?
|
||||||
|
@currentShapes ?= @raphaelObj.set()
|
||||||
|
console.log "currentShapes:" + @currentShapes
|
||||||
|
@currentShapes.push(tool)
|
||||||
|
@currentShapesDefinitions.push(toolModel.getDefinition())
|
||||||
|
|
||||||
|
# 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
|
||||||
|
moveCursor: (x, y) ->
|
||||||
|
[cx, cy] = @_currentSlideOffsets()
|
||||||
|
[slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
@cursor.setPosition(x * slideWidth + cx, y * slideHeight + cy)
|
||||||
|
|
||||||
|
#if the slide is zoomed in then move the cursor based on where the viewBox is looking
|
||||||
|
if @viewBoxXpos? && @viewBoxYPos? && @viewBoxWidth? && @viewBoxHeight?
|
||||||
|
@cursor.setPosition( @viewBoxXpos + x * @viewBoxWidth, @viewBoxYPos + y * @viewBoxHeight )
|
||||||
|
|
||||||
|
# Update the slide to move and zoom
|
||||||
|
# @param {number} xOffset the x value of offset
|
||||||
|
# @param {number} yOffset the y value of offset
|
||||||
|
# @param {number} widthRatio the ratio of the previous width
|
||||||
|
# @param {number} heightRatio the ratio of the previous height
|
||||||
|
moveAndZoom: (xOffset, yOffset, widthRatio, heightRatio) ->
|
||||||
|
@globalxOffset = xOffset
|
||||||
|
@globalyOffset = yOffset
|
||||||
|
@globalwidthRatio = widthRatio
|
||||||
|
@globalheightRatio = heightRatio
|
||||||
|
|
||||||
|
[slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
#console.log("xOffset: " + xOffset + ", yOffset: " + yOffset);
|
||||||
|
#console.log("@containerWidth: " + @containerWidth + " @containerHeight: " + @containerHeight);
|
||||||
|
#console.log("slideWidth: " + slideWidth + " slideHeight: " + slideHeight);
|
||||||
|
baseWidth = (@containerWidth - slideWidth) / 2
|
||||||
|
baseHeight = (@containerHeight - slideHeight) / 2
|
||||||
|
|
||||||
|
|
||||||
|
#get the actual size of the slide, depending on the limiting factor (container width or container height)
|
||||||
|
|
||||||
|
actualWidth = @currentSlide.displayWidth
|
||||||
|
actualHeight = @currentSlide.displayHeight
|
||||||
|
#console.log("actualWidth:" + actualWidth + " actualHeight: " + actualHeight)
|
||||||
|
|
||||||
|
#calculate parameters to pass
|
||||||
|
newXPos = baseWidth - 2* xOffset * actualWidth / 100
|
||||||
|
newyPos = baseHeight - 2* yOffset * actualHeight / 100
|
||||||
|
newWidth = actualWidth / 100 * widthRatio
|
||||||
|
newHeight = actualHeight / 100 * heightRatio
|
||||||
|
|
||||||
|
@viewBoxXpos = newXPos
|
||||||
|
@viewBoxYPos = newyPos
|
||||||
|
@viewBoxWidth = newWidth
|
||||||
|
@viewBoxHeight = newHeight
|
||||||
|
|
||||||
|
#console.log("newXPos: " + newXPos + " newyPos: " + newyPos + " newWidth: " + newWidth + " newHeight: " + newHeight)
|
||||||
|
|
||||||
|
#set parameters to raphael viewbox
|
||||||
|
@raphaelObj.setViewBox(newXPos , newyPos, newWidth , newHeight , true)
|
||||||
|
|
||||||
|
|
||||||
|
# update the rectangle elements which create the border when page is zoomed
|
||||||
|
@borders.left.attr( {width:newXPos, height: @containerHeight} )
|
||||||
|
|
||||||
|
@borders.right.attr(
|
||||||
|
x: newXPos + newWidth
|
||||||
|
y: 0
|
||||||
|
width:newXPos
|
||||||
|
height:@containerHeight
|
||||||
|
)
|
||||||
|
|
||||||
|
@borders.top.attr(
|
||||||
|
width: @containerWidth
|
||||||
|
height: newyPos
|
||||||
|
)
|
||||||
|
|
||||||
|
@borders.bottom.attr(
|
||||||
|
y: newyPos + newHeight
|
||||||
|
width: @containerWidth
|
||||||
|
height: @containerHeight
|
||||||
|
)
|
||||||
|
|
||||||
|
# borders should appear infront of every other element (i.e. shapes)
|
||||||
|
for _, border of @borders
|
||||||
|
border.toFront()
|
||||||
|
|
||||||
|
#update cursor to appear the same size even when page is zoomed in
|
||||||
|
@cursor.setRadius( 3 * widthRatio / 100 )
|
||||||
|
|
||||||
|
# Registers listeners for events in the gloval event bus
|
||||||
|
_registerEvents: ->
|
||||||
|
globals.events.on "connection:all_slides", (data) =>
|
||||||
|
@removeAllImagesFromPaper()
|
||||||
|
###
|
||||||
|
urls = data.slides
|
||||||
|
for url in urls
|
||||||
|
@addImageToPaper(url[0], url[1], url[2])
|
||||||
|
#alert "registerEvents url[0]=" + url[0]
|
||||||
|
###
|
||||||
|
|
||||||
|
urls = data.presentation.pages
|
||||||
|
for url in urls
|
||||||
|
@addImageToPaper(url.png , 200, 200)
|
||||||
|
# alert "registerEvents url[0]=" + url[0]
|
||||||
|
globals.events.trigger("whiteboard:paper:all_slides", urls)
|
||||||
|
|
||||||
|
globals.events.on "connection:clrPaper", =>
|
||||||
|
@clearShapes()
|
||||||
|
|
||||||
|
globals.events.on "connection:allShapes", (allShapesEventObject) =>
|
||||||
|
# TODO: a hackish trick for making compatible the shapes from redis with the node.js
|
||||||
|
shapes = allShapesEventObject.shapes
|
||||||
|
for shape in shapes
|
||||||
|
properties = JSON.parse(shape.data)
|
||||||
|
if shape.shape is "path"
|
||||||
|
points = properties[0]
|
||||||
|
strPoints = ""
|
||||||
|
for i in [0..points.length] by 2
|
||||||
|
letter = ""
|
||||||
|
pA = points[i]
|
||||||
|
pB = points[i+1]
|
||||||
|
if i == 0
|
||||||
|
letter = "M"
|
||||||
|
else
|
||||||
|
letter = "L"
|
||||||
|
strPoints += letter + (pA/100) + "," + (pB/100)
|
||||||
|
properties[0] = strPoints
|
||||||
|
|
||||||
|
shape.data = JSON.stringify properties #TODO
|
||||||
|
|
||||||
|
@clearShapes()
|
||||||
|
@drawListOfShapes shapes
|
||||||
|
|
||||||
|
globals.events.on "connection:updShape", (shape, data) =>
|
||||||
|
@updateShape shape, data
|
||||||
|
|
||||||
|
globals.events.on "connection:whiteboard_draw_event", (shape, data) =>
|
||||||
|
@makeShape shape, data
|
||||||
|
|
||||||
|
globals.events.on "connection:share_presentation_event", (data) =>
|
||||||
|
@sharePresentation data
|
||||||
|
|
||||||
|
globals.events.on "connection:whiteboardDrawPen", (startingData) =>
|
||||||
|
type = startingData.payload.shape_type
|
||||||
|
color = startingData.payload.data.line.color
|
||||||
|
thickness = startingData.payload.data.line.weight
|
||||||
|
points = startingData.shape.points
|
||||||
|
if type is "line"
|
||||||
|
for i in [0..points.length - 1]
|
||||||
|
if i is 0
|
||||||
|
#make these compatible with a line
|
||||||
|
console.log "points[i]: " + points[i]
|
||||||
|
lineObject = {
|
||||||
|
shape: {
|
||||||
|
type: "line",
|
||||||
|
coordinate: {
|
||||||
|
firstX : points[i].x/100,
|
||||||
|
firstY : points[i].y/100
|
||||||
|
},
|
||||||
|
color: startingData.payload.data.line.color,
|
||||||
|
thickness : startingData.payload.data.line.weight
|
||||||
|
}
|
||||||
|
adding : false #tell the line object that we are NOT adding points but creating a new line
|
||||||
|
}
|
||||||
|
console.log "lineObject: " + lineObject
|
||||||
|
@makeShape type, lineObject
|
||||||
|
else
|
||||||
|
console.log "points[i]: "+ points[i]
|
||||||
|
lineObject = {
|
||||||
|
shape: {
|
||||||
|
type: "line",
|
||||||
|
coordinate: {
|
||||||
|
firstX : points[i].x/100,
|
||||||
|
firstY : points[i].y/100
|
||||||
|
},
|
||||||
|
color: startingData.payload.data.line.color,
|
||||||
|
thickness : startingData.payload.data.line.weight
|
||||||
|
}
|
||||||
|
adding : true #tell the line object that we ARE adding points and NOT creating a new line
|
||||||
|
}
|
||||||
|
console.log "lineObject: " + lineObject
|
||||||
|
@updateShape type, lineObject
|
||||||
|
|
||||||
|
globals.events.on "connection:mvCur", (x, y) =>
|
||||||
|
@moveCursor(x, y)
|
||||||
|
#console.log "x: " + x + " y: " + y
|
||||||
|
|
||||||
|
globals.events.on "connection:move_and_zoom", (xOffset, yOffset, widthRatio, heightRatio) =>
|
||||||
|
@moveAndZoom(xOffset, yOffset, widthRatio, heightRatio)
|
||||||
|
|
||||||
|
globals.events.on "connection:changeslide", (url) =>
|
||||||
|
@showImageFromPaper(url)
|
||||||
|
|
||||||
|
globals.events.on "connection:viewBox", (xperc, yperc, wperc, hperc) =>
|
||||||
|
xperc = parseFloat(xperc, 10)
|
||||||
|
yperc = parseFloat(yperc, 10)
|
||||||
|
wperc = parseFloat(wperc, 10)
|
||||||
|
hperc = parseFloat(hperc, 10)
|
||||||
|
@updatePaperFromServer(xperc, yperc, wperc, hperc)
|
||||||
|
|
||||||
|
globals.events.on "connection:fitToPage", (value) =>
|
||||||
|
@setFitToPage(value)
|
||||||
|
|
||||||
|
globals.events.on "connection:zoom", (delta) =>
|
||||||
|
@setZoom(delta)
|
||||||
|
|
||||||
|
globals.events.on "connection:paper", (cx, cy, sw, sh) =>
|
||||||
|
@updatePaperFromServer(cx, cy, sw, sh)
|
||||||
|
|
||||||
|
globals.events.on "connection:panStop", =>
|
||||||
|
@stopPanning()
|
||||||
|
|
||||||
|
globals.events.on "connection:toolChanged", (tool) =>
|
||||||
|
@setCurrentTool(tool)
|
||||||
|
|
||||||
|
globals.events.on "connection:textDone", =>
|
||||||
|
@textDone()
|
||||||
|
|
||||||
|
globals.events.on "connection:uploadStatus", (message, fade) =>
|
||||||
|
globals.events.trigger("whiteboard:paper:uploadStatus", message, fade)
|
||||||
|
|
||||||
|
|
||||||
|
# Update the dimensions of the container.
|
||||||
|
_updateContainerDimensions: ->
|
||||||
|
$container = $('#' + @container)
|
||||||
|
@containerWidth = $container.innerWidth()
|
||||||
|
@containerHeight = $container.innerHeight()
|
||||||
|
@containerOffsetLeft = $container.offset().left
|
||||||
|
@containerOffsetTop = $container.offset().top
|
||||||
|
|
||||||
|
|
||||||
|
# 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) ->
|
||||||
|
if @slides[url]
|
||||||
|
id = @slides[url].getId()
|
||||||
|
return @raphaelObj.getById(id) if id?
|
||||||
|
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)
|
||||||
|
_hideImageFromPaper: (url) ->
|
||||||
|
img = @_getImageFromPaper(url)
|
||||||
|
img.hide() if img?
|
||||||
|
|
||||||
|
# Update zoom variables on all clients
|
||||||
|
# @param {Event} e the event that occurs when scrolling
|
||||||
|
# @param {number} delta the speed/direction at which the scroll occurred
|
||||||
|
_zoomSlide: (e, delta) ->
|
||||||
|
globals.connection.emitZoom delta
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: this should only be done if the user is the presenter
|
||||||
|
_onMouseMove: (e, x, y) ->
|
||||||
|
[sw, sh] = @_currentSlideDimensions()
|
||||||
|
xLocal = (e.pageX - @containerOffsetLeft) / sw
|
||||||
|
yLocal = (e.pageY - @containerOffsetTop) / sh
|
||||||
|
globals.connection.emitMoveCursor xLocal, yLocal
|
||||||
|
|
||||||
|
# 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
|
||||||
|
_panDragging: (dx, dy) ->
|
||||||
|
[slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
sx = (@containerWidth - slideWidth) / 2
|
||||||
|
sy = (@containerHeight - slideHeight) / 2
|
||||||
|
[sw, sh] = @_currentSlideDimensions()
|
||||||
|
|
||||||
|
# ensuring that we cannot pan outside of the boundaries
|
||||||
|
x = (@panX - dx)
|
||||||
|
# cannot pan past the left edge of the page
|
||||||
|
x = (if x < 0 then 0 else x)
|
||||||
|
y = (@panY - dy)
|
||||||
|
# cannot pan past the top of the page
|
||||||
|
y = (if y < 0 then 0 else y)
|
||||||
|
if @fitToPage
|
||||||
|
x2 = slideWidth + x
|
||||||
|
else
|
||||||
|
x2 = @containerWidth + x
|
||||||
|
# cannot pan past the width
|
||||||
|
x = (if x2 > sw then sw - (@containerWidth - sx * 2) else x)
|
||||||
|
if @fitToPage
|
||||||
|
y2 = slideHeight + y
|
||||||
|
else
|
||||||
|
# height of image could be greater (or less) than the box it fits in
|
||||||
|
y2 = @containerHeight + y
|
||||||
|
# cannot pan below the height
|
||||||
|
y = (if y2 > sh then sh - (@containerHeight - sy * 2) else y)
|
||||||
|
globals.connection.emitPaperUpdate x / sw, y / sh, null, null
|
||||||
|
|
||||||
|
# When panning starts
|
||||||
|
# @param {number} x the x value of the cursor
|
||||||
|
# @param {number} y the y value of the cursor
|
||||||
|
_panGo: (x, y) ->
|
||||||
|
[cx, cy] = @_currentSlideOffsets()
|
||||||
|
@panX = cx
|
||||||
|
@panY = cy
|
||||||
|
|
||||||
|
# When panning finishes
|
||||||
|
# @param {Event} e the mouse event
|
||||||
|
_panStop: (e) ->
|
||||||
|
@stopPanning()
|
||||||
|
|
||||||
|
# Called when the application window is resized.
|
||||||
|
_onWindowResize: ->
|
||||||
|
@_updateContainerDimensions()
|
||||||
|
console.log "_onWindowResize"
|
||||||
|
|
||||||
|
#TODO: temporary hacked away fix so that the buttons resize correctly when the window resizes
|
||||||
|
$("#users-btn").click();
|
||||||
|
$("#users-btn").click();
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: maybe find solution besides these global values..no conflicts however
|
||||||
|
|
||||||
|
[slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
#console.log("xOffset: " + xOffset + ", yOffset: " + yOffset);
|
||||||
|
#console.log("@containerWidth: " + @containerWidth + " @containerHeight: " + @containerHeight);
|
||||||
|
#console.log("slideWidth: " + slideWidth + " slideHeight: " + slideHeight);
|
||||||
|
baseWidth = (@containerWidth - slideWidth) / 2
|
||||||
|
baseHeight = (@containerHeight - slideHeight) / 2
|
||||||
|
|
||||||
|
|
||||||
|
#get the actual size of the slide, depending on the limiting factor (container width or container height)
|
||||||
|
if(@containerWidth - slideWidth < @containerHeight - slideHeight)
|
||||||
|
actualHeight = @containerWidth * slideHeight / slideWidth
|
||||||
|
actualWidth = @containerWidth
|
||||||
|
else
|
||||||
|
actualWidth = @containerHeight * slideWidth / slideHeight
|
||||||
|
actualHeight = @containerHeight
|
||||||
|
|
||||||
|
#console.log("actualWidth:" + actualWidth + " actualHeight: " + actualHeight)
|
||||||
|
|
||||||
|
#calculate parameters to pass
|
||||||
|
newXPos = baseWidth
|
||||||
|
newyPos = baseHeight
|
||||||
|
newWidth = actualWidth
|
||||||
|
newHeight = actualHeight
|
||||||
|
|
||||||
|
#now the zooming will still be correct when the window is resized
|
||||||
|
#and hopefully when rotated on a mobile device
|
||||||
|
if @globalxOffset? && @globalyOffset? && @globalwidthRatio? && @globalheightRatio?
|
||||||
|
console.log "has zoomed in"
|
||||||
|
@moveAndZoom(@globalxOffset, @globalyOffset, @globalwidthRatio, @globalheightRatio)
|
||||||
|
|
||||||
|
else
|
||||||
|
obj =
|
||||||
|
globalxOffset : @globalxOffset
|
||||||
|
globalyOffset : @globalyOffset
|
||||||
|
globalwidthRatio : @globalwidthRatio
|
||||||
|
globalheightRatio : @globalheightRatio
|
||||||
|
|
||||||
|
console.log obj
|
||||||
|
console.log "not zoomed"
|
||||||
|
@raphaelObj.setViewBox(newXPos, newyPos, newWidth, newHeight,true)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# when pressing down on a key at anytime
|
||||||
|
_onKeyDown: (event) ->
|
||||||
|
unless event
|
||||||
|
keyCode = window.event.keyCode
|
||||||
|
else
|
||||||
|
keyCode = event.keyCode
|
||||||
|
switch keyCode
|
||||||
|
when 16 # shift key
|
||||||
|
@shiftPressed = true
|
||||||
|
|
||||||
|
# when releasing any key at any time
|
||||||
|
_onKeyUp: ->
|
||||||
|
unless event
|
||||||
|
keyCode = window.event.keyCode
|
||||||
|
else
|
||||||
|
keyCode = event.keyCode
|
||||||
|
switch keyCode
|
||||||
|
when 16 # shift key
|
||||||
|
@shiftPressed = false
|
||||||
|
|
||||||
|
_currentSlideDimensions: ->
|
||||||
|
if @currentSlide? then @currentSlide.getDimensions() else [0, 0]
|
||||||
|
|
||||||
|
_currentSlideOriginalDimensions: ->
|
||||||
|
if @currentSlide? then @currentSlide.getOriginalDimensions() else [0, 0]
|
||||||
|
|
||||||
|
_currentSlideOffsets: ->
|
||||||
|
if @currentSlide? then @currentSlide.getOffsets() else [0, 0]
|
||||||
|
|
||||||
|
# Wrapper method to create a tool for the whiteboard
|
||||||
|
_createTool: (type) ->
|
||||||
|
switch type
|
||||||
|
when "path", "line"
|
||||||
|
model = WhiteboardLineModel
|
||||||
|
when "rectangle"
|
||||||
|
model = WhiteboardRectModel
|
||||||
|
when "ellipse"
|
||||||
|
model = WhiteboardEllipseModel
|
||||||
|
when "triangle"
|
||||||
|
model = WhiteboardTriangleModel
|
||||||
|
when "text"
|
||||||
|
model = WhiteboardTextModel
|
||||||
|
|
||||||
|
if model?
|
||||||
|
[slideWidth, slideHeight] = @_currentSlideOriginalDimensions()
|
||||||
|
[xOffset, yOffset] = @_currentSlideOffsets()
|
||||||
|
[width, height] = @_currentSlideDimensions()
|
||||||
|
|
||||||
|
tool = new model(@raphaelObj)
|
||||||
|
# TODO: why are the parameters inverted and it works?
|
||||||
|
tool.setPaperSize(slideHeight, slideWidth)
|
||||||
|
tool.setOffsets(xOffset, yOffset)
|
||||||
|
tool.setPaperDimensions(width,height)
|
||||||
|
tool
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
# Adds the base url (the protocol+server part) to `url` if needed.
|
||||||
|
_slideUrl: (url) ->
|
||||||
|
if url?.match(/http[s]?:/)
|
||||||
|
url
|
||||||
|
else
|
||||||
|
globals.presentationServer + url
|
||||||
|
|
||||||
|
#Changes the currently displayed page/slide (if any) with this one
|
||||||
|
#@param {data} message object containing the "presentation" object
|
||||||
|
_displayPage: (data) ->
|
||||||
|
@removeAllImagesFromPaper()
|
||||||
|
page = data?.payload?.currentPage
|
||||||
|
pngSlide = "http://www.tux.org/pub/sites/ftp.gnome.org/GNOME/teams/art.gnome.org/backgrounds/ABSTRACT-BlueRidge_1280x1024.png"
|
||||||
|
#@addImageToPaper(page.png_uri, 400, 400) # TODO the dimensions should be modified
|
||||||
|
@addImageToPaper(pngSlide, 400, 400) # TODO the dimensions should be modified
|
@ -0,0 +1,150 @@
|
|||||||
|
# A rectangle in the whiteboard
|
||||||
|
class @WhiteboardRectModel extends WhiteboardToolModel
|
||||||
|
constructor: (@paper) ->
|
||||||
|
super @paper
|
||||||
|
console.log "@paper in WhiteboardRectModel=" + @paper
|
||||||
|
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
# format: x1, y1, x2, y2, stroke color, thickness
|
||||||
|
@definition = [0, 0, 0, 0, "#000", "0px"]
|
||||||
|
@paper
|
||||||
|
|
||||||
|
# Creates a rectangle in the paper
|
||||||
|
# @param {number} x the x value of the top left corner
|
||||||
|
# @param {number} y the y value of the top left corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
make: (startingData) =>
|
||||||
|
console.log "MAKING RECT"
|
||||||
|
console.log "@paper in make in WhiteboardRectModel=" + @paper
|
||||||
|
console.log "make startingData"+ JSON.stringify startingData
|
||||||
|
|
||||||
|
x = startingData.points[0]
|
||||||
|
y = startingData.points[1]
|
||||||
|
color = startingData.color
|
||||||
|
thickness = startingData.thickness
|
||||||
|
|
||||||
|
@obj = @paper.rect(x * @gw + @xOffset, y * @gh + @yOffset, 0, 0, 1)
|
||||||
|
@obj.attr Meteor.call("strokeAndThickness",color, thickness)
|
||||||
|
@definition =
|
||||||
|
shape: "rect"
|
||||||
|
data: [x, y, 0, 0, @obj.attrs["stroke"], @obj.attrs["stroke-width"]]
|
||||||
|
@obj
|
||||||
|
|
||||||
|
# Update the rectangle dimensions
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
# @param {boolean} square (draw a square or not)
|
||||||
|
update: (startingData) ->
|
||||||
|
console.log "UPDATING RECT"
|
||||||
|
x1 = startingData.points[0]
|
||||||
|
y1 = startingData.points[1]
|
||||||
|
x2 = startingData.points[2]
|
||||||
|
y2 = startingData.points[3]
|
||||||
|
console.log "updating rect points:" + x1, x2, y1, y2
|
||||||
|
square = startingData.square
|
||||||
|
if @obj?
|
||||||
|
[x1, x2] = [x2, x1] if x2 < x1
|
||||||
|
[x1, x2] = [x2, x1] if x2 < x1
|
||||||
|
|
||||||
|
if y2 < y1
|
||||||
|
[y1, y2] = [y2, y1]
|
||||||
|
reversed = true
|
||||||
|
|
||||||
|
if square
|
||||||
|
if reversed #if reveresed, the y1 coordinate gets updated, not the y2 coordinate
|
||||||
|
y1 = y2 - (x2 - x1) * @gw / @gh
|
||||||
|
else
|
||||||
|
y2 = y1 + (x2 - x1) * @gw / @gh
|
||||||
|
|
||||||
|
x = x1 * @gw + @xOffset
|
||||||
|
y = y1 * @gh + @yOffset
|
||||||
|
width = (x2 * @gw + @xOffset) - x
|
||||||
|
height = (y2 * @gh + @yOffset) - y
|
||||||
|
#if !square
|
||||||
|
@obj.attr
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
width: width
|
||||||
|
height: height
|
||||||
|
###else
|
||||||
|
@obj.attr
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
width: width
|
||||||
|
height: width###
|
||||||
|
|
||||||
|
# we need to update all these values, specially for when shapes are drawn backwards
|
||||||
|
@definition.data[0] = x1
|
||||||
|
@definition.data[1] = y1
|
||||||
|
@definition.data[2] = x2
|
||||||
|
@definition.data[3] = y2
|
||||||
|
|
||||||
|
# Draw a rectangle on the paper
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
draw: (x1, y1, x2, y2, colour, thickness) ->
|
||||||
|
[x1, x2] = [x2, x1] if x2 < x1
|
||||||
|
[y1, y2] = [y2, y1] if y2 < y1
|
||||||
|
|
||||||
|
x = x1 * @gw
|
||||||
|
y = y1 * @gh
|
||||||
|
r = @paper.rect(x + @xOffset, y + @yOffset, (x2 * @gw) - x, (y2 * @gh) - y, 1)
|
||||||
|
r.attr Meteor.call("strokeAndThickness", colour, thickness)
|
||||||
|
r
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnStart: (x, y) ->
|
||||||
|
# sx = (@paperWidth - @gw) / 2
|
||||||
|
# sy = (@paperHeight - @gh) / 2
|
||||||
|
# # find the x and y values in relation to the whiteboard
|
||||||
|
# @cx2 = (x - @containerOffsetLeft - sx + @xOffset) / @paperWidth
|
||||||
|
# @cy2 = (y - @containerOffsetTop - sy + @yOffset) / @paperHeight
|
||||||
|
# globals.connection.emitMakeShape "rect",
|
||||||
|
# [ @cx2, @cy2, @currentColour, @currentThickness ]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnMove: (dx, dy, x, y, e) ->
|
||||||
|
# # if shift is pressed, make it a square
|
||||||
|
# dy = dx if @shiftPressed
|
||||||
|
# dx = dx / @paperWidth
|
||||||
|
# dy = dy / @paperHeight
|
||||||
|
# # 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
|
||||||
|
# globals.connection.emitUpdateShape "rect", [ x1, y1, dx, dy ]
|
||||||
|
|
||||||
|
# When rectangle finished being drawn
|
||||||
|
# @param {Event} e the mouse event
|
||||||
|
# TODO: moved here but not finished
|
||||||
|
dragOnEnd: (e) ->
|
||||||
|
# if @obj?
|
||||||
|
# attrs = @obj.attrs
|
||||||
|
# if attrs?
|
||||||
|
# globals.connection.emitPublishShape "rect",
|
||||||
|
# [ attrs.x / @gw, attrs.y / @gh, attrs.width / @gw, attrs.height / @gh,
|
||||||
|
# @currentColour, @currentThickness ]
|
||||||
|
# @obj = null
|
@ -0,0 +1,21 @@
|
|||||||
|
# A slide in the whiteboard
|
||||||
|
class @WhiteboardSlideModel
|
||||||
|
|
||||||
|
# TODO: check if we really need original and display width and heights separate or if they can be the same
|
||||||
|
constructor: (@id, @url, @img, @originalWidth, @originalHeight, @displayWidth, @displayHeight, @xOffset=0, @yOffset=0) ->
|
||||||
|
|
||||||
|
getWidth: -> @displayWidth
|
||||||
|
|
||||||
|
getHeight: -> @displayHeight
|
||||||
|
|
||||||
|
getOriginalWidth: -> @originalWidth
|
||||||
|
|
||||||
|
getOriginalHeight: -> @originalHeight
|
||||||
|
|
||||||
|
getId: -> @id
|
||||||
|
|
||||||
|
getDimensions: -> [@getWidth(), @getHeight()]
|
||||||
|
|
||||||
|
getOriginalDimensions: -> [@getOriginalWidth(), @getOriginalHeight()]
|
||||||
|
|
||||||
|
getOffsets: -> [@xOffset, @yOffset]
|
@ -0,0 +1,155 @@
|
|||||||
|
# A text in the whiteboard
|
||||||
|
class @WhiteboardTextModel extends WhiteboardToolModel
|
||||||
|
|
||||||
|
constructor: (@paper) ->
|
||||||
|
super @paper
|
||||||
|
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
# format: x, y, width, height, colour, fontSize, calcFontSize, text
|
||||||
|
@definition = [0, 0, 0, 0, "#000", 0, 0, ""]
|
||||||
|
|
||||||
|
# @textX = null
|
||||||
|
# @textY = null
|
||||||
|
|
||||||
|
# Make a text on the whiteboard
|
||||||
|
make: (x, y, width, height, colour, fontSize, calcFontSize, text) ->
|
||||||
|
@definition =
|
||||||
|
shape: "text"
|
||||||
|
data: [x, y, width, height, colour, fontSize, calcFontSize, text]
|
||||||
|
|
||||||
|
calcFontSize = (calcFontSize/100 * @gh)
|
||||||
|
x = (x * @gw) + @xOffset
|
||||||
|
y = (y * @gh) + @yOffset + calcFontSize
|
||||||
|
width = width/100 * @gw
|
||||||
|
colour = Utils.strokeAndThickness(colour)["stroke"]
|
||||||
|
|
||||||
|
@obj = @paper.text(x, y, "")
|
||||||
|
@obj.attr
|
||||||
|
fill: colour
|
||||||
|
"font-family": "Arial" # TODO: make dynamic
|
||||||
|
"font-size": calcFontSize
|
||||||
|
@obj.node.style["text-anchor"] = "start" # force left align
|
||||||
|
@obj.node.style["textAnchor"] = "start" # for firefox, 'cause they like to be different
|
||||||
|
@obj
|
||||||
|
|
||||||
|
# Update triangle drawn
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
update: (x, y, width, height, colour, fontSize, calcFontSize, text) ->
|
||||||
|
if @obj?
|
||||||
|
@definition.data = [x, y, width, height, colour, fontSize, calcFontSize, text]
|
||||||
|
|
||||||
|
calcFontSize = (calcFontSize/100 * @gh)
|
||||||
|
x = (x * @gw) + @xOffset
|
||||||
|
width = width/100 * @gw
|
||||||
|
colour = Utils.strokeAndThickness(colour)["stroke"]
|
||||||
|
|
||||||
|
@obj.attr
|
||||||
|
fill: colour
|
||||||
|
"font-size": calcFontSize
|
||||||
|
cell = @obj.node
|
||||||
|
while cell? and cell.hasChildNodes()
|
||||||
|
cell.removeChild(cell.firstChild)
|
||||||
|
textFlow(text, cell, width, x, calcFontSize, false)
|
||||||
|
|
||||||
|
|
||||||
|
# Draw a text on the whiteboard
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
draw: (x, y, width, height, colour, fontSize, calcFontSize, text) ->
|
||||||
|
calcFontSize = (calcFontSize/100 * @gh)
|
||||||
|
x = x * @gw + @xOffset
|
||||||
|
y = (y * @gh) + @yOffset + calcFontSize
|
||||||
|
width = width/100 * @gw
|
||||||
|
colour = Utils.strokeAndThickness(colour)["stroke"]
|
||||||
|
|
||||||
|
el = @paper.text(x, y, "")
|
||||||
|
el.attr
|
||||||
|
fill: colour
|
||||||
|
"font-family": "Arial" # TODO: make dynamic
|
||||||
|
"font-size": calcFontSize
|
||||||
|
el.node.style["text-anchor"] = "start" # force left align
|
||||||
|
el.node.style["textAnchor"] = "start" # for firefox, 'cause they like to be different
|
||||||
|
textFlow(text, el.node, width, x, calcFontSize, false)
|
||||||
|
el
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# TODO: moved here but not finished nor tested
|
||||||
|
# _textStart: (x, y) ->
|
||||||
|
# [sw, sh] = @_currentSlideDimensions()
|
||||||
|
# [cx, cy] = @_currentSlideOffsets()
|
||||||
|
# if @currentText?
|
||||||
|
# globals.connection.emitPublishShape "text",
|
||||||
|
# [ @textbox.value, @currentText.attrs.x / @gw, @currentText.attrs.y / @gh,
|
||||||
|
# @textbox.clientWidth, 16, @currentColour, "Arial", 14 ]
|
||||||
|
# globals.connection.emitTextDone()
|
||||||
|
# @textbox.value = ""
|
||||||
|
# @textbox.style.visibility = "hidden"
|
||||||
|
# @textX = x
|
||||||
|
# @textY = y
|
||||||
|
# sx = (@containerWidth - @gw) / 2
|
||||||
|
# sy = (@containerHeight - @gh) / 2
|
||||||
|
# @cx2 = (x - @containerOffsetLeft - sx + cx) / sw
|
||||||
|
# @cy2 = (y - @containerOffsetTop - sy + cy) / sh
|
||||||
|
# @_makeRect @cx2, @cy2, "#000", 1
|
||||||
|
# globals.connection.emitMakeShape "rect", [ @cx2, @cy2, "#000", 1 ]
|
||||||
|
|
||||||
|
# Finished drawing the rectangle that the text will fit into
|
||||||
|
# @param {Event} e the mouse event
|
||||||
|
# TODO: moved here but not finished nor tested
|
||||||
|
# _textStop: (e) ->
|
||||||
|
# @currentRect.hide() if @currentRect?
|
||||||
|
# [sw, sh] = @_currentSlideDimensions()
|
||||||
|
# [cx, cy] = @_currentSlideOffsets()
|
||||||
|
# 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 = @currentColour
|
||||||
|
# @textbox.value = ""
|
||||||
|
# sx = (@containerWidth - @gw) / 2
|
||||||
|
# sy = (@containerHeight - @gh) / 2
|
||||||
|
# x = @textX - @containerOffsetLeft - sx + cx + 1 # 1px random padding
|
||||||
|
# y = @textY - @containerOffsetTop - sy + cy
|
||||||
|
# @textbox.focus()
|
||||||
|
|
||||||
|
# # if you click outside, it will automatically sumbit
|
||||||
|
# @textbox.onblur = (e) =>
|
||||||
|
# if @currentText
|
||||||
|
# globals.connection.emitPublishShape "text",
|
||||||
|
# [ @value, @currentText.attrs.x / @gw, @currentText.attrs.y / @gh,
|
||||||
|
# @textbox.clientWidth, 16, @currentColour, "Arial", 14 ]
|
||||||
|
# globals.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
|
||||||
|
# _paper = @
|
||||||
|
# @textbox.onkeyup = (e) ->
|
||||||
|
# @style.color = _paper.currentColour
|
||||||
|
# @value = @value.replace(/\n{1,}/g, " ").replace(/\s{2,}/g, " ")
|
||||||
|
# globals.connection.emitUpdateShape "text",
|
||||||
|
# [ @value, x / _paper.sw, (y + (14 * (_paper.sh / _paper.gh))) / _paper.sh,
|
||||||
|
# tboxw * (_paper.gw / _paper.sw), 16, _paper.currentColour, "Arial", 14 ]
|
||||||
|
|
||||||
|
# The server has said the text is finished,
|
||||||
|
# so set it to null for the next text object
|
||||||
|
# TODO: moved here but not finished nor tested
|
||||||
|
# textDone: ->
|
||||||
|
# if @currentText?
|
||||||
|
# @currentText = null
|
||||||
|
# @currentRect.hide() if @currentRect?
|
@ -0,0 +1,105 @@
|
|||||||
|
# A triangle in the whiteboard
|
||||||
|
class @WhiteboardTriangleModel extends WhiteboardToolModel
|
||||||
|
|
||||||
|
constructor: (@paper) ->
|
||||||
|
super @paper
|
||||||
|
|
||||||
|
# the defintion of this shape, kept so we can redraw the shape whenever needed
|
||||||
|
# format: x1, y1, x2, y2, stroke color, thickness
|
||||||
|
@definition = [0, 0, 0, 0, "#000", "0px"]
|
||||||
|
|
||||||
|
# Make a triangle on the whiteboard
|
||||||
|
# @param {[type]} x the x value of the top left corner
|
||||||
|
# @param {[type]} y the y value of the top left corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
make: (info) ->
|
||||||
|
|
||||||
|
x = info.payload.data.coordinate.first_x
|
||||||
|
y = info.payload.data.coordinate.first_y
|
||||||
|
color = info.payload.data.line.color
|
||||||
|
thickness = info.payload.data.line.weight
|
||||||
|
|
||||||
|
path = @_buildPath(x, y, x, y, x, y)
|
||||||
|
@obj = @paper.path(path)
|
||||||
|
@obj.attr Utils.strokeAndThickness(color, thickness)
|
||||||
|
@obj.attr({"stroke-linejoin": "round"})
|
||||||
|
|
||||||
|
@definition =
|
||||||
|
shape: "triangle"
|
||||||
|
data: [x, y, x, y, @obj.attrs["stroke"], @obj.attrs["stroke-width"]]
|
||||||
|
|
||||||
|
@obj
|
||||||
|
|
||||||
|
# Update triangle drawn
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
update: (info) ->
|
||||||
|
|
||||||
|
x1 = info.payload.data.coordinate.first_x
|
||||||
|
y1 = info.payload.data.coordinate.first_y
|
||||||
|
x2 = info.payload.data.coordinate.last_x
|
||||||
|
y2 = info.payload.data.coordinate.last_y
|
||||||
|
|
||||||
|
if @obj?
|
||||||
|
[xTop, yTop, xBottomLeft, yBottomLeft, xBottomRight, yBottomRight] = @_getCornersFromPoints(x1, y1, x2, y2)
|
||||||
|
|
||||||
|
path = @_buildPath(xTop * @gw + @xOffset, yTop * @gh + @yOffset,
|
||||||
|
xBottomLeft * @gw + @xOffset, yBottomLeft * @gh + @yOffset,
|
||||||
|
xBottomRight * @gw + @xOffset, yBottomRight * @gh + @yOffset)
|
||||||
|
@obj.attr path: path
|
||||||
|
|
||||||
|
@definition.data[0] = x1
|
||||||
|
@definition.data[1] = y1
|
||||||
|
@definition.data[2] = x2
|
||||||
|
@definition.data[3] = y2
|
||||||
|
|
||||||
|
# Draw a triangle on the whiteboard
|
||||||
|
# @param {number} x1 the x value of the top left corner
|
||||||
|
# @param {number} y1 the y value of the top left corner
|
||||||
|
# @param {number} x2 the x value of the bottom right corner
|
||||||
|
# @param {number} y2 the y value of the bottom right corner
|
||||||
|
# @param {string} colour the colour of the object
|
||||||
|
# @param {number} thickness the thickness of the object's line(s)
|
||||||
|
draw: (x1, y1, x2, y2, colour, thickness) ->
|
||||||
|
[xTop, yTop, xBottomLeft, yBottomLeft, xBottomRight, yBottomRight] = @_getCornersFromPoints(x1, y1, x2, y2)
|
||||||
|
path = @_buildPath(xTop, yTop, xBottomLeft, yBottomLeft, xBottomRight, yBottomRight)
|
||||||
|
path = @_scaleTrianglePath(path, @gw, @gh, @xOffset, @yOffset)
|
||||||
|
triangle = @paper.path(path)
|
||||||
|
triangle.attr Utils.strokeAndThickness(colour, thickness)
|
||||||
|
triangle.attr({"stroke-linejoin": "round"})
|
||||||
|
triangle
|
||||||
|
|
||||||
|
_getCornersFromPoints: (x1, y1, x2, y2) ->
|
||||||
|
xTop = (((x2 - x1) / 2) + x1)
|
||||||
|
yTop = y1
|
||||||
|
xBottomLeft = x1
|
||||||
|
yBottomLeft = y2
|
||||||
|
xBottomRight = x2
|
||||||
|
yBottomRight = y2
|
||||||
|
[xTop, yTop, xBottomLeft, yBottomLeft, xBottomRight, yBottomRight]
|
||||||
|
|
||||||
|
_buildPath: (xTop, yTop, xBottomLeft, yBottomLeft, xBottomRight, yBottomRight) ->
|
||||||
|
"M#{xTop},#{yTop},#{xBottomLeft},#{yBottomLeft},#{xBottomRight},#{yBottomRight}z"
|
||||||
|
|
||||||
|
# Scales a triangle 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
|
||||||
|
_scaleTrianglePath: (string, w, h, xOffset=0, yOffset=0) ->
|
||||||
|
path = null
|
||||||
|
points = string.match(/(\d+[.]?\d*)/g)
|
||||||
|
len = points.length
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
# go through each point and multiply it by the new height and width
|
||||||
|
path = "M"
|
||||||
|
while j < len
|
||||||
|
path += "," unless j is 0
|
||||||
|
path += "" + (points[j] * w + xOffset) + "," + (points[j + 1] * h + yOffset)
|
||||||
|
j += 2
|
||||||
|
path + "z"
|
||||||
|
|
||||||
|
WhiteboardTriangleModel
|
@ -22,3 +22,4 @@ Meteor.methods
|
|||||||
|
|
||||||
id = Meteor.Shapes.insert(entry)
|
id = Meteor.Shapes.insert(entry)
|
||||||
console.log "added shape id =[#{id}]:#{shapeObject.id} in #{meetingId}"
|
console.log "added shape id =[#{id}]:#{shapeObject.id} in #{meetingId}"
|
||||||
|
|
||||||
|
1
labs/meteor-client/packages/.gitignore
vendored
1
labs/meteor-client/packages/.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
/iron-router
|
/iron-router
|
||||||
/blaze-layout
|
/blaze-layout
|
||||||
/npm
|
/npm
|
||||||
|
/raphaeljs-package
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
"redis": {},
|
"redis": {},
|
||||||
"npm": {},
|
"npm": {},
|
||||||
"bootstrap-3": {},
|
"bootstrap-3": {},
|
||||||
"iron-router": {}
|
"iron-router": {},
|
||||||
|
"raphaeljs-package": {
|
||||||
|
"git": "https://github.com/tomconnors/raphaeljs-package.git"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@
|
|||||||
"redis": {},
|
"redis": {},
|
||||||
"npm": {},
|
"npm": {},
|
||||||
"bootstrap-3": {},
|
"bootstrap-3": {},
|
||||||
"iron-router": {}
|
"iron-router": {},
|
||||||
|
"raphaeljs-package": {
|
||||||
|
"git": "https://github.com/tomconnors/raphaeljs-package.git",
|
||||||
|
"branch": "master"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"redis": {
|
"redis": {
|
||||||
@ -28,6 +32,11 @@
|
|||||||
"tag": "v0.7.1",
|
"tag": "v0.7.1",
|
||||||
"commit": "d1ffb3f06ea4c112132b030f2eb1a70b81675ecb"
|
"commit": "d1ffb3f06ea4c112132b030f2eb1a70b81675ecb"
|
||||||
},
|
},
|
||||||
|
"raphaeljs-package": {
|
||||||
|
"git": "https://github.com/tomconnors/raphaeljs-package.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "85eaef3664ec063e4bcb81be1226d126d4d20539"
|
||||||
|
},
|
||||||
"blaze-layout": {
|
"blaze-layout": {
|
||||||
"git": "https://github.com/EventedMind/blaze-layout.git",
|
"git": "https://github.com/EventedMind/blaze-layout.git",
|
||||||
"tag": "v0.2.4",
|
"tag": "v0.2.4",
|
||||||
|
Loading…
Reference in New Issue
Block a user