define [ 'jquery', 'underscore', 'backbone', 'raphael', 'globals', 'cs!models/whiteboard_paper', 'cs!views/session_whiteboard_controls', 'text!templates/preupload_image.html', 'text!templates/session_whiteboard.html', 'colorwheel' ], ($, _, Backbone, Raphael, globals, WhiteboardPaperModel, SessionWhiteboardControlsView, preuploadImageTemplate, sessionWhiteboardTemplate) -> # TODO: this is being used for presentation and whiteboard, maybe they could be separated DEFAULT_COLOUR = "#FF0000" DEFAULT_THICKNESS = 1 # The whiteboard components in a session # The contents are rendered by SessionView, this class is Used to # manage the events in the users. SessionWhiteboardView = Backbone.View.extend events: "click #colour-view": "_toggleColorPicker" initialize: -> @paper = null @controlsView = new SessionWhiteboardControlsView() # Resize the paper when the window is resize to make it always 100%x100% resizePaper = => @_setPaperSize() $(window).resize(resizePaper) # Bind to the event triggered when the client connects to the server globals.connection.bind "connection:connected", @_registerConnectionEvents, @ # Override the close() method so we can close the sub-views. close: -> @controlsView.close() @paper?.unbindEvents() Backbone.View.prototype.close.call(@) render: -> compiledTemplate = _.template(sessionWhiteboardTemplate) @$el.html compiledTemplate # subview with whiteboard controls @assign(@controlsView, "#slide-controls") @colorView = @$("#colour-view") @colourViewCtx = @colorView[0].getContext("2d") @colourText = @$("#colour-text") @thicknessView = @$("#thickness-view") @thicknessViewCtx = @thicknessView[0].getContext("2d") @$("#thickness-slider").slider value: 1 min: 1 max: 20 @$("#thickness-slider").on "slide", (event, ui) => @_drawThicknessView ui.value, @currentColour @$("#slide-current-text-area").autosize() @_createColourPicker() @_createPaper() @_drawThicknessView(DEFAULT_THICKNESS, DEFAULT_COLOUR) @_drawColourView(DEFAULT_COLOUR) _createColourPicker: -> unless @colourPicker @$("#colour-picker").hide() @colourPicker = Raphael.colorwheel(@$("#colour-picker")[0], 75) @colourPicker.input(@$("#colour-text")[0]) @colourPicker.onchange (color) => @_drawThicknessView(null, color.hex) @_drawColourView(color.hex) @colourPicker.color DEFAULT_COLOUR _createPaper: -> # have to create the paper here, in the initializer #slide doesn't exist yet container = @$("#slide")[0] textbox = @$("#slide-current-text-area")[0] @paper ?= new WhiteboardPaperModel(container, textbox) @paper.create() # events triggered when an image is added or removed from the paper @paper.bind "paper:image:added", @_addPreloadImage, this @paper.bind "paper:image:removed", @_removePreloadImage, this # Registers listeners for events in the application socket. _registerConnectionEvents: -> socket = globals.connection.socket # Received event to update all the slide images # @param {Array} urls list of URLs to be added to the paper (after old images are removed) socket.on "all_slides", (urls) => console.log "received all_slides", urls @controlsView.clearUploadStatus() @paper?.removeAllImagesFromPaper() for url in urls @paper?.addImageToPaper(url[0], url[1], url[2]) # to make sure the paper will ocuupy all available area @_setPaperSize() # Received event to clear the whiteboard shapes socket.on "clrPaper", => console.log "received clrPaper" @paper?.clearShapes() # Received event to update all the shapes in the whiteboard # @param {Array} shapes Array of shapes to be drawn socket.on "all_shapes", (shapes) => console.log "received all_shapes" # TODO: a hackish trick for making compatible the shapes from redis with the node.js 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) @paper?.clearShapes() @paper?.drawListOfShapes shapes # Received event to update a shape being created # @param {string} shape type of shape being updated # @param {Array} data all information to update the shape socket.on "updShape", (shape, data) => @paper?.updateShape shape, data # Received event to create a shape on the whiteboard # @param {string} shape type of shape being made # @param {Array} data all information to make the shape socket.on "makeShape", (shape, data) => @paper?.makeShape shape, data socket.on "shapePoints", (type,color,thickness,points) => if type == "line" for i in [0..points.length] by 2 if i == 0 data = [(points[i]/100),(points[i+1]/100),color,thickness] @paper?.makeShape type, data else data = [(points[i]/100),(points[i+1]/100),true] @paper?.updateShape type, data # Received event to update the cursor coordinates # @param {number} x x-coord of the cursor as a percentage of page width # @param {number} y y-coord of the cursor as a percentage of page height socket.on "mvCur", (x, y) => @paper?.moveCursor x, y # Received event to update the slide image # @param {string} url URL of image to show socket.on "changeslide", (url) => console.log "received changeslide", url @paper?.showImageFromPaper url # Received event to update the viewBox value # @param {string} xperc Percentage of x-offset from top left corner # @param {string} yperc Percentage of y-offset from top left corner # @param {string} wperc Percentage of full width of image to be displayed # @param {string} hperc Percentage of full height of image to be displayed # TODO: not tested yet socket.on "viewBox", (xperc, yperc, wperc, hperc) => console.log "received viewBox", xperc, yperc, wperc, hperc xperc = parseFloat(xperc, 10) yperc = parseFloat(yperc, 10) wperc = parseFloat(wperc, 10) hperc = parseFloat(hperc, 10) @paper?.updatePaperFromServer xperc, yperc, wperc, hperc # Received event to update the whiteboard between fit to width and fit to page # @param {boolean} value choice of fit: true for fit to page, false for fit to width socket.on "fitToPage", (value) -> @paper?.setFitToPage value # Received event to update the zoom level of the whiteboard. # @param {number} delta amount of change in scroll wheel socket.on "zoom", (delta) -> @paper?.setZoom delta # Received event to update the whiteboard size and position # @param {number} cx x-offset from top left corner as percentage of original width of paper # @param {number} cy y-offset from top left corner as percentage of original height of paper # @param {number} sw slide width as percentage of original width of paper # @param {number} sh slide height as a percentage of original height of paper socket.on "paper", (cx, cy, sw, sh) -> @paper?.updatePaperFromServer cx, cy, sw, sh # Received event when the panning action finishes socket.on "panStop", -> @paper?.stopPanning() # Received event to change the current tool # @param {string} tool The tool to be turned on socket.on "toolChanged", (tool) -> console.log "received toolChanged", tool @paper?.setCurrentTool tool # Received event to denote when the text has been created socket.on "textDone", -> @paper?.textDone() # 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. _toggleColorPicker: -> if @$("#colour-picker").is(":visible") @$("#colour-picker").hide() else @$("#colour-picker").show() # TODO: to use the event to test other things # @controlsView.setUploadStatus "msg", true _addPreloadImage: (img) -> customSrc = img.attr("src") customSrc = customSrc.replace(":3000", "") # TODO: temporary console.log "adding preload image", customSrc data = img: id: img.id url: customSrc compiledTemplate = _.template(preuploadImageTemplate, data) @$("#slide").append compiledTemplate _removePreloadImage: (imgID) -> console.log "removing preload image", imgID $("#preload-" + imgID).remove() # 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) -> @currentThickness = thickness if thickness? @thicknessViewCtx.fillStyle = "#FFFFFF" @thicknessViewCtx.fillRect 0, 0, 20, 20 center = Math.round((20 - @currentThickness + 1) / 2) @thicknessViewCtx.fillStyle = colour @thicknessViewCtx.fillRect center, center, @currentThickness + 1, @currentThickness + 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) -> @currentColour = colour @colourViewCtx.fillStyle = colour @colourText.value = colour @colourViewCtx.fillRect 0, 0, 12, 12 _setPaperSize: () -> @paper.changeSize(@$el.width(), @$el.height(), true, false) SessionWhiteboardView