text: // Documented
  
  // Object references
  var chcount = document.getElementById('charcount');
  var msgbox = document.getElementById("chat_messages");
  var chatbox = document.getElementById('chat_input_box');
  
  var PORT = 3000; //port that SocketIO will connect on
  var SERVER_IP = window.location.hostname;
  
  // Connect to the websocket via SocketIO
  var socket = io.connect('http://'+SERVER_IP+':'+PORT);
  
  /**
   * If the socket is connected
   * @return {undefined}
   */
  socket.on('connect', function () {
  
    // Immediately say we are connected
    socket.emit('user connect');
  
    /**
     * Received event for a new public chat message
     * @param  {string} name name of user
     * @param  {string} msg  message to be displayed
     * @return {undefined}
     */
    socket.on('msg', function (name, msg) {
      msgbox.innerHTML += '<div>' + name + ': ' + msg + '</div>';
      msgbox.scrollTop = msgbox.scrollHeight;
    });
  
    /**
     * Received event to logout yourself
     * @return {undefined}
     */
    socket.on('logout', function () {
        post_to_url('logout');
        window.location.replace("./");
    });
  
    /**
     * Received event to update the user list
     * @param  {Array} names Array of names and publicIDs of connected users
     * @return {undefined}
     */
    socket.on('user list change', function (names) {
      var clickFunc = '$(\'.selected\').removeClass(\'selected\');$(this).addClass(\'selected\');';
      var currusers = document.getElementById('current_users');
      currusers.innerHTML = ''; //clear it first
      for (var i = names.length - 1; i >= 0; i--) {
        currusers.innerHTML += '<div class="user clickable" onclick="'+clickFunc+'" id= "'+names[i].id+'"><b>' + names[i].name + '</b></div>';
      }
    });
  
    /**
     * Received event to update all the messages in the chat box
     * @param  {Array} messages Array of messages in public chat box
     * @return {undefined}
     */
    socket.on('all_messages', function (messages) {
      //msgbox.innerHTML = '';
      for (var i = messages.length - 1; i >= 0; i--){
      msgbox.innerHTML += '<div>' + messages[i].username + ": " + messages[i].message + '</div>';
      };
      msgbox.scrollTop = msgbox.scrollHeight;
    });
  
    /**
     * Received event to update all the shapes in the whiteboard
     * @param  {Array} shapes Array of shapes to be drawn
     * @return {undefined}
     */
    socket.on('all_shapes', function (shapes) {
      clearPaper();
      drawListOfShapes(shapes);
    });
  
    /**
     * If the server is reconnected to the client
     * @return {undefined}
     */
    socket.on('reconnect', function () {
      msgbox.innerHTML += '<div><b> RECONNECTED! </b></div>';
    });
  
    /**
     * If the client is attempting to reconnect to the server
     * @return {undefined}
     */
    socket.on('reconnecting', function () {
      msgbox.innerHTML += '<div><b> Reconnecting... </b></div>';
    });
  
    /**
     * If the client cannot reconnect to the server
     * @return {undefined}
     */
    socket.on('reconnect_failed', function () {
      msgbox.innerHTML += '<div><b> Reconnect FAILED! </b></div>';
    });
  
    /**
     * If the server disconnects from the client or vice-versa
     * @return {undefined}
     */
    socket.on('disconnect', function() {
        window.location.replace("./");
    });
  
    /**
     * Received event to clear the whiteboard shapes
     * @return {undefined}
     */
    socket.on('clrPaper', function () {
      clearPaper();
    });
  
    /**
     * 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
     * @return {undefined}
     */
    socket.on('viewBox', function(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);
    });
  
    /**
     * 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
     * @return {undefined}
     */
    socket.on('mvCur', function(x, y) {
      mvCur(x, y);
    });
  
    /**
     * Received event to update the slide image
     * @param  {string} url URL of image to show
     * @return {undefined}
     */
    socket.on('changeslide', function(url) {
      showImageFromPaper(url);
    });
  
    /**
     * Received event to update the whiteboard between fit to width and fit to page
     * @param  {boolean} fit choice of fit: true for fit to page, false for fit to width
     * @return {undefined}
     */
    socket.on('fitToPage', function(fit) {
      setFitToPage(fit);
    });
  
    /**
     * Received event to update the zoom level of the whiteboard.
     * @param  {number} delta amount of change in scroll wheel
     * @return {undefined}
     */
    socket.on('zoom', function(delta) {
      setZoom(delta);
    });
  
    /**
     * Received event when the panning action finishes
     * @return {undefined}
     */
    socket.on('panStop', function() {
      panDone();
    });
  
    /**
     * 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
     * @return {undefined}
     */
    socket.on('makeShape', function(shape, data) {
      switch(shape) {
      case 'line':
        makeLine.apply(makeLine, data);
      break;
  
      case 'rect':
        makeRect.apply(makeRect, data);
      break;
  
      case 'ellipse':
        makeEllipse.apply(makeEllipse, data);
      break;
  
      default:
        //no other shapes allowed
      break;
      }
    });
  
    /**
     * 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
     * @return {undefined}
     */
    socket.on('updShape', function(shape, data) {
      switch(shape) {
      case 'line':
        updateLine.apply(updateLine, data);
      break;
  
      case 'rect':
        updateRect.apply(updateRect, data);
      break;
  
      case 'ellipse':
        updateEllipse.apply(updateEllipse, data);
      break;
  
      case 'text':
        updateText.apply(updateText, data);
      break;
  
      default:
        console.log('shape not recognized');
      break;
    }
    });
  
    /**
     * Received event to denote when the text has been created
     * @return {undefined}
     */
    socket.on('textDone', function() {
      textDone();
    });
  
    /**
     * Received event to change the current tool
     * @param  {string} tool tool to be turned on
     * @return {undefined}
     */
    socket.on('toolChanged', function(tool) {
      turnOn(tool);
    });
  
    /**
     * 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
     * @return {undefined}
     */
    socket.on('paper', function(cx, cy, sw, sh) {
    updatePaperFromServer(cx, cy, sw, sh);
    });
  
    /**
     * Received event to set the presenter to a user
     * @param  {string} publicID publicID of the user that is being set as the current presenter
     * @return {undefined}
     */
    socket.on('setPresenter', function(publicID) {
      $('.presenter').removeClass('presenter');
      $('#' + publicID).addClass('presenter');
    });
  
    /**
     * Received event to update the status of the upload progress
     * @param  {string} message  update message of status of upload progress
     * @param  {boolean} fade    true if you wish the message to automatically disappear after 3 seconds
     * @return {undefined}
     */
    socket.on('uploadStatus', function(message, fade) {
      $('#uploadStatus').text(message);
      // if a true is passed for fade, it will only stay for
      // a period of time before disappearing automatically
      if(fade) {
        setTimeout(function() {
          $('#uploadStatus').text('');
        }, 3000);
      }
    });
  
    /**
     * 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)
     * @return {undefined}
     */
    socket.on('all_slides', function(urls) {
      $('#uploadStatus').text(""); //upload finished
      removeAllImagesFromPaper();
      var count = 0;
      var numOfSlides = urls.length;
      for (var i = 0; i < numOfSlides; i++) {
        var array = urls[i];
        var img = addImageToPaper(array[0], array[1], array[2]);
        //TODO: temporary solution for remove :3000
        var custom_src = img.attr('src');
        custom_src = custom_src.replace(':3000', ""); 
        console.log(custom_src);
        $('#slide').append('<img id="preload'+img.id+'"src="'+custom_src+'" style="display:none;" alt=""/>'); //preload images
      };
    });
  });
  
  /**
   * If an error occurs while not connected
   * @param  {string} reason Reason for the error.
   * @return {undefined}
   */
  socket.on('error', function (reason) {
    console.error('Unable to connect Socket.IO', reason);
  });
  
  /**
   * 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}
   */
  function post_to_url(path, params, method) {
    method = method || "post"; // Set method to post by default, if not specified.
    // The rest of this code assumes you are not using a library.
    // It can be made less wordy if you use one.
    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);
    for(var key in params) {
      if(params.hasOwnProperty(key)) {
        var hiddenField = document.createElement("input");
        hiddenField.setAttribute("type", "hidden");
        hiddenField.setAttribute("name", key);
        hiddenField.setAttribute("value", params[key]);
        form.appendChild(hiddenField);
      }
    }
    document.body.appendChild(form);
    form.submit();
  }
  
  /**
   * Sending a public chat message to users
   * @return {undefined}
   */
  function sendMessage() {
    var msg = chatbox.value;
      if (msg != '') {
          socket.emit('msg', msg);
          chatbox.value = '';
      }
      chatbox.focus();
  }
  
  /**
   * Clearing the canvas drawings
   * @return {undefined}
   */
  function clearCanvas() {
    socket.emit("clrPaper");
  }
  
  /**
   * Requests the shapes from the server.
   * @return {undefined}
   */
  function getShapesFromServer() {
    socket.emit('all_shapes');
  }
  
  /**
   * Emit an update in a fit of the whiteboard
   * @param  {boolean} true for fitToPage, false for fitToWidth
   * @return {undefined}
   */
  function sendFitToPage(fit) {
    socket.emit('fitToPage', fit);
  }
  
  /**
   * Emit the finish of a text shape
   * @return {undefined}
   */
  function emitDoneText() {
    socket.emit('textDone');
  }
  
  /**
   * Emit the creation of a shape
   * @param  {string} shape type of shape
   * @param  {Array} data  all the data required to draw the shape on the client whiteboard
   * @return {undefined}
   */
  function emitMakeShape(shape, data) {
    socket.emit('makeShape', shape, data);
  }
  
  /**
   * Emit the update of a shape
   * @param  {string} shape type of shape
   * @param  {Array} data  all the data required to update the shape on the client whiteboard
   * @return {undefined}
   */
  function emitUpdateShape(shape, data) {
    socket.emit('updShape', shape, data);
  }
  
  /**
   * Emit an update in the whiteboard position/size values
   * @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
   * @return {undefined}
   */
  function sendPaperUpdate(cx, cy, sw, sh) {
    socket.emit('paper', cx, cy, sw, sh);
  }
  
  /**
   * Emit an update to move the cursor around the canvas
   * @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
   * @return {undefined}
   */
  function emMvCur(x, y) {
    socket.emit('mvCur', x, y);
  }
  
  /**
   * Update the zoom level for the clients
   * @param  {number} delta amount of change in scroll wheel
   * @return {undefined}
   */
  function emZoom(delta) {
    socket.emit('zoom', delta);
  }
  
  /**
   * Request the next slide
   * @return {undefined}
   */
  function nextImg() {
    socket.emit('nextslide');
  }
  
  /**
   * Request the previous slide
   * @return {undefined}
   */
  function prevImg() {
    socket.emit('prevslide');
  }
  
  /**
   * Logout of the meeting
   * @return {undefined}
   */
  function logout() {
    socket.emit('logout');
  }
  
  /**
   * Emit panning has stopped
   * @return {undefined}
   */
  function emPanStop() {
    socket.emit('panStop');
  }
  
  /**
   * Publish a shape to the server to be saved
   * @param  {string} shape type of shape to be saved
   * @param  {Array} data   information about shape so that it can be recreated later
   * @return {undefined}
   */
  function emitPublishShape(shape, data) {
    socket.emit('saveShape', shape, JSON.stringify(data));
  }
  
  /**
   * Emit a change in the current tool
   * @param  {string} tool [description]
   * @return {undefined}
   */
  function changeTool(tool) {
    socket.emit('changeTool', tool);
  }
  
  /**
   * Tell the server to undo the last shape
   * @return {undefined}
   */
  function undoShape() {
    socket.emit('undo');
  }
  
  /**
   * Emit a change in the presenter
   * @return {undefined}
   */
  function switchPresenter() {
    socket.emit('setPresenter', $('.selected').attr('id'));
  }
  
  /**
   * Update the character count remaining in the chat box
   * @param  {number} max maximum number of allowed characters
   * @return {undefined}
   */
  function countchars(max) {
    chcount.innerHTML = max - chatbox.value.length;
  }
  