diff --git a/record-and-playback/core/scripts/cleanup.rb b/record-and-playback/core/scripts/cleanup.rb
index 120b305be33a7c5a8e7cbcef373cd2823df44206..1db8720473b2151f2ee32b05f0c20e032211afc8 100755
--- a/record-and-playback/core/scripts/cleanup.rb
+++ b/record-and-playback/core/scripts/cleanup.rb
@@ -27,54 +27,54 @@ RECORDING_DIR="/var/bigbluebutton/recording"
 
 
 def cleanProcessedFiles(path)
-	redis=Redis.new
-	# get the pastday
-	pastday=Time.new-(3600*24)
-	puts "Cleaning out conferences before #{pastday}"
-	
-	(Dir.entries(path) - ['.','..']).each do
-	|playback|
-		(Dir.entries(path+"/#{playback}") - ['.','..']).each do
-		|meeting|	
-			
-			modtime = File.mtime(path+"/#{playback}/#{meeting}")
-			dif=modtime <=> pastday
-			# 1 represents all the recording upto the pastday
-			if dif != 1
-				puts "Meeting: #{meeting}"
-				#Deleting Processed Dir
-				puts "Checking record and ingest directories..."
-				
-				puts "Deleting *.done files..."
-				FileUtils.rm_rf RECORDING_DIR+"/status/recorded/#{meeting}.done"
-				FileUtils.rm_rf RECORDING_DIR+"/status/archived/#{meeting}.done"
-				FileUtils.rm_rf RECORDING_DIR+"/status/processed/#{meeting}-#{playback}.done"
-				
-				puts "Deleting processed directories..."
-				if(File.directory?(RECORDING_DIR+"/process/#{playback}/#{meeting}"))
-					FileUtils.rm_rf RECORDING_DIR+"/process/#{playback}/#{meeting}"
-				end
-				if(File.directory?(RECORDING_DIR+"/publish/#{playback}/#{meeting}"))
-					FileUtils.rm_rf RECORDING_DIR+"/publish/#{playback}/#{meeting}"
-				end
-				
-				#Deleting Redis Keys
-				puts "Checking redis keys..."
-				if( redis.exists("meeting:#{meeting}:recordings"))
-					len= redis.llen("meeting:#{meeting}:recordings")
-					range= redis.lrange("meeting:#{meeting}:recordings",0,len)
-					range.each do
-					|msgid|
-						redis.del("recording:#{meeting}:#{msgid}")
-					end
-					puts "Deleting Redis Keys for #{meeting}"
-					redis.del("meeting:#{meeting}:recordings")
-				end
-			end
-			
-		end
-		
-	end
+  redis=Redis.new
+  # get the pastday
+  pastday=Time.new-(3600*24)
+  puts "Cleaning out conferences before #{pastday}"
+
+  (Dir.entries(path) - ['.','..']).each do
+  |playback|
+    (Dir.entries(path+"/#{playback}") - ['.','..']).each do
+    |meeting|
+
+      modtime = File.mtime(path+"/#{playback}/#{meeting}")
+      dif=modtime <=> pastday
+      # 1 represents all the recording upto the pastday
+      if dif != 1
+        puts "Meeting: #{meeting}"
+        #Deleting Processed Dir
+        puts "Checking record and ingest directories..."
+
+        puts "Deleting *.done files..."
+        FileUtils.rm_rf RECORDING_DIR+"/status/recorded/#{meeting}.done"
+        FileUtils.rm_rf RECORDING_DIR+"/status/archived/#{meeting}.done"
+        FileUtils.rm_rf RECORDING_DIR+"/status/processed/#{meeting}-#{playback}.done"
+
+        puts "Deleting processed directories..."
+        if(File.directory?(RECORDING_DIR+"/process/#{playback}/#{meeting}"))
+          FileUtils.rm_rf RECORDING_DIR+"/process/#{playback}/#{meeting}"
+        end
+        if(File.directory?(RECORDING_DIR+"/publish/#{playback}/#{meeting}"))
+          FileUtils.rm_rf RECORDING_DIR+"/publish/#{playback}/#{meeting}"
+        end
+
+        #Deleting Redis Keys
+        puts "Checking redis keys..."
+        if( redis.exists("meeting:#{meeting}:recordings"))
+          len= redis.llen("meeting:#{meeting}:recordings")
+          range= redis.lrange("meeting:#{meeting}:recordings",0,len)
+          range.each do
+          |msgid|
+            redis.del("recording:#{meeting}:#{msgid}")
+          end
+          puts "Deleting Redis Keys for #{meeting}"
+          redis.del("meeting:#{meeting}:recordings")
+        end
+      end
+
+    end
+
+  end
 
 end
 
diff --git a/record-and-playback/core/scripts/rap-worker.rb b/record-and-playback/core/scripts/rap-worker.rb
index c299fad126d5035a78a9799c64f612211f11b931..5e995d31c6d42382466c5550341799dec05a61c2 100755
--- a/record-and-playback/core/scripts/rap-worker.rb
+++ b/record-and-playback/core/scripts/rap-worker.rb
@@ -36,7 +36,7 @@ def archive_recorded_meeting(recording_dir)
   recorded_done_files.each do |recorded_done|
     match = /([^\/]*).done$/.match(recorded_done)
     meeting_id = match[1]
-    
+
     if File.mtime(recorded_done) + ARCHIVE_DELAY_SECONDS > Time.now
       BigBlueButton.logger.info("Temporarily skipping #{meeting_id} for Red5 race workaround")
       next
@@ -359,4 +359,4 @@ rescue Exception => e
   e.backtrace.each do |traceline|
     BigBlueButton.logger.error(traceline)
   end
-end	
+end
diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb
index ed81c3f7e4f1675a4081604b85afeed00aaa8821..10e1634709842aca6647550ef4f62dba342050c8 100755
--- a/record-and-playback/presentation/scripts/process/presentation.rb
+++ b/record-and-playback/presentation/scripts/process/presentation.rb
@@ -54,32 +54,32 @@ if not FileTest.directory?(target_dir)
   BigBlueButton.logger = logger
   BigBlueButton.logger.info("Processing script presentation.rb")
   FileUtils.mkdir_p target_dir
-  
+
  begin
 
   # Create a copy of the raw archives
   temp_dir = "#{target_dir}/temp"
   FileUtils.mkdir_p temp_dir
   FileUtils.cp_r(raw_archive_dir, temp_dir)
-  
+
   BigBlueButton::AudioProcessor.process("#{temp_dir}/#{meeting_id}", "#{target_dir}/audio")
   events_xml = "#{temp_dir}/#{meeting_id}/events.xml"
   FileUtils.cp(events_xml, target_dir)
-  
+
   presentation_dir = "#{temp_dir}/#{meeting_id}/presentation"
   presentations = BigBlueButton::Presentation.get_presentations(events_xml)
-  
+
   processed_pres_dir = "#{target_dir}/presentation"
   FileUtils.mkdir_p processed_pres_dir
-  
+
   presentations.each do |pres|
     pres_dir = "#{presentation_dir}/#{pres}"
     num_pages = BigBlueButton::Presentation.get_number_of_pages_for(pres_dir)
-    
+
     target_pres_dir = "#{processed_pres_dir}/#{pres}"
     FileUtils.mkdir_p target_pres_dir
     FileUtils.mkdir_p "#{target_pres_dir}/textfiles"
-    
+
     images=Dir.glob("#{pres_dir}/#{pres}.{jpg,jpeg,png,gif,JPG,JPEG,PNG,GIF}")
     if images.empty?
       pres_name = "#{pres_dir}/#{pres}"
@@ -113,7 +113,7 @@ if not FileTest.directory?(target_dir)
       BigBlueButton.execute(command)
     end
   end
-  
+
   if !Dir["#{raw_archive_dir}/video/*"].empty? or (presentation_props['include_deskshare'] and !Dir["#{raw_archive_dir}/deskshare/*"].empty?)
     width = presentation_props['video_output_width']
     height = presentation_props['video_output_height']
@@ -128,13 +128,13 @@ if not FileTest.directory?(target_dir)
   process_done.write("Processed #{meeting_id}")
   process_done.close
 #else
-#	BigBlueButton.logger.debug("Skipping #{meeting_id} as it has already been processed.")  
+#	BigBlueButton.logger.debug("Skipping #{meeting_id} as it has already been processed.")
  rescue Exception => e
         BigBlueButton.logger.error(e.message)
-	e.backtrace.each do |traceline|
-		BigBlueButton.logger.error(traceline)
-	end
-	exit 1
+  e.backtrace.each do |traceline|
+    BigBlueButton.logger.error(traceline)
+  end
+  exit 1
  end
 end
-    
+
diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb
index e26a894c798b84ea914afd638416270f9c051bbf..e0fbfa0c4d75703c0bc7dd687bc00dd2fb83a538 100755
--- a/record-and-playback/presentation/scripts/publish/presentation.rb
+++ b/record-and-playback/presentation/scripts/publish/presentation.rb
@@ -36,92 +36,92 @@ class String
 end
 
 def processPanAndZooms
-	#Create panzooms.xml
-	BigBlueButton.logger.info("Creating panzooms.xml")
-	$panzooms_xml = Nokogiri::XML::Builder.new do |xml|
-		$xml = xml
-		$xml.recording('id' => 'panzoom_events') do
-			h_ratio_prev = nil
-			w_ratio_prev = nil
-			x_prev = nil
-			y_prev = nil
-			timestamp_orig_prev = nil
-			timestamp_prev = nil
-			last_time = nil
-			if $panzoom_events.empty?
-				BigBlueButton.logger.info("No panzoom events; old recording?")
-				BigBlueButton.logger.info("Synthesizing a panzoom event")
-				if !$slides_events.empty?
-					timestamp_orig = $slides_events.first[:timestamp].to_f
-					# make sure this is scheduled *after* the slide is shown. Dunno if needed.
-					timestamp_orig += 1000
-					timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
-					$xml.event(:timestamp => timestamp, :orig => timestamp_orig) do
-						$xml.viewBox "0 0 #{$vbox_width} #{$vbox_height}"
-					end
-					timestamp_orig_prev = timestamp_orig
-					timestamp_prev = timestamp
-					h_ratio_prev = 100
-					w_ratio_prev = 100
-					x_prev = 0
-					y_prev = 0
-				else
-					BigBlueButton.logger.info("Couldn't find any slides! panzooms will be empty.")
-				end
-			else
-				last_time = $panzoom_events.last[:timestamp].to_f
-			end
-			$panzoom_events.each do |panZoomEvent|
-				# Get variables
-				timestamp_orig = panZoomEvent[:timestamp].to_f
-
-				timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
-				h_ratio = panZoomEvent.xpath(".//heightRatio")[0].text()
-				w_ratio = panZoomEvent.xpath(".//widthRatio")[0].text()
-				x = panZoomEvent.xpath(".//xOffset")[0].text()
-				y = panZoomEvent.xpath(".//yOffset")[0].text()
-				
-				if(timestamp_prev == timestamp)
-					if(timestamp_orig == last_time)
-						if(h_ratio && w_ratio && x && y)
-							$xml.event(:timestamp => timestamp, :orig => timestamp_orig) do
-								$ss.each do |key,val|
-									$val = val
-									if key === timestamp
-										$vbox_width = $val[0]
-										$vbox_height = $val[1]
-									end
-								end
-								$xml.viewBox "#{($vbox_width-((1-((x.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio.to_f/100.0)*$vbox_height).round(1)}"
-							end
-						end
-					end
-					# do nothing because playback can't react that fast
-				else
-					if(h_ratio_prev && w_ratio_prev && x_prev && y_prev)
-						$xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
-							$ss.each do |key,val|
-								$val = val
-								if key === timestamp_prev
-									$vbox_width = $val[0]
-									$vbox_height = $val[1]
-								end
-							end
-							$xml.viewBox "#{($vbox_width-((1-((x_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio_prev.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio_prev.to_f/100.0)*$vbox_height).round(1)}"
-						end
-					end
-				end
-				timestamp_prev = timestamp
-				timestamp_orig_prev = timestamp_orig
-				h_ratio_prev = h_ratio
-				w_ratio_prev = w_ratio
-				x_prev = x
-				y_prev = y
-			end
-			$xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
-	                        $ss.each do |key,val|
-        		                $val = val
-                        		if key === timestamp_prev
+  #Create panzooms.xml
+  BigBlueButton.logger.info("Creating panzooms.xml")
+  $panzooms_xml = Nokogiri::XML::Builder.new do |xml|
+    $xml = xml
+    $xml.recording('id' => 'panzoom_events') do
+      h_ratio_prev = nil
+      w_ratio_prev = nil
+      x_prev = nil
+      y_prev = nil
+      timestamp_orig_prev = nil
+      timestamp_prev = nil
+      last_time = nil
+      if $panzoom_events.empty?
+        BigBlueButton.logger.info("No panzoom events; old recording?")
+        BigBlueButton.logger.info("Synthesizing a panzoom event")
+        if !$slides_events.empty?
+          timestamp_orig = $slides_events.first[:timestamp].to_f
+          # make sure this is scheduled *after* the slide is shown. Dunno if needed.
+          timestamp_orig += 1000
+          timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
+          $xml.event(:timestamp => timestamp, :orig => timestamp_orig) do
+            $xml.viewBox "0 0 #{$vbox_width} #{$vbox_height}"
+          end
+          timestamp_orig_prev = timestamp_orig
+          timestamp_prev = timestamp
+          h_ratio_prev = 100
+          w_ratio_prev = 100
+          x_prev = 0
+          y_prev = 0
+        else
+          BigBlueButton.logger.info("Couldn't find any slides! panzooms will be empty.")
+        end
+      else
+        last_time = $panzoom_events.last[:timestamp].to_f
+      end
+      $panzoom_events.each do |panZoomEvent|
+        # Get variables
+        timestamp_orig = panZoomEvent[:timestamp].to_f
+
+        timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
+        h_ratio = panZoomEvent.xpath(".//heightRatio")[0].text()
+        w_ratio = panZoomEvent.xpath(".//widthRatio")[0].text()
+        x = panZoomEvent.xpath(".//xOffset")[0].text()
+        y = panZoomEvent.xpath(".//yOffset")[0].text()
+
+        if(timestamp_prev == timestamp)
+          if(timestamp_orig == last_time)
+            if(h_ratio && w_ratio && x && y)
+              $xml.event(:timestamp => timestamp, :orig => timestamp_orig) do
+                $ss.each do |key,val|
+                  $val = val
+                  if key === timestamp
+                    $vbox_width = $val[0]
+                    $vbox_height = $val[1]
+                  end
+                end
+                $xml.viewBox "#{($vbox_width-((1-((x.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio.to_f/100.0)*$vbox_height).round(1)}"
+              end
+            end
+          end
+          # do nothing because playback can't react that fast
+        else
+          if(h_ratio_prev && w_ratio_prev && x_prev && y_prev)
+            $xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
+              $ss.each do |key,val|
+                $val = val
+                if key === timestamp_prev
+                  $vbox_width = $val[0]
+                  $vbox_height = $val[1]
+                end
+              end
+              $xml.viewBox "#{($vbox_width-((1-((x_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio_prev.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio_prev.to_f/100.0)*$vbox_height).round(1)}"
+            end
+          end
+        end
+        timestamp_prev = timestamp
+        timestamp_orig_prev = timestamp_orig
+        h_ratio_prev = h_ratio
+        w_ratio_prev = w_ratio
+        x_prev = x
+        y_prev = y
+      end
+      $xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
+                          $ss.each do |key,val|
+                            $val = val
+                            if key === timestamp_prev
                                         $vbox_width = $val[0]
                                         $vbox_height = $val[1]
                                 end
@@ -129,312 +129,312 @@ def processPanAndZooms
                           $xml.viewBox "#{($vbox_width-((1-((x_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio_prev.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio_prev.to_f/100.0)*$vbox_height).round(1)}"
                         end
 
-		end
-	end
-	BigBlueButton.logger.info("Finished creating panzooms.xml")
+    end
+  end
+  BigBlueButton.logger.info("Finished creating panzooms.xml")
 end
 
 def processCursorEvents
-	BigBlueButton.logger.info("Processing cursor events")
-	$cursor_xml = Nokogiri::XML::Builder.new do |xml|
-		$xml = xml
-		$xml.recording('id' => 'cursor_events') do
-			x_prev = nil
-			y_prev = nil
-			timestamp_orig_prev = nil
-			timestamp_prev = nil
-			if(!$cursor_events.empty?)
-				last_time = $cursor_events.last[:timestamp].to_f
-				$cursor_events.each do |cursorEvent|
-					timestamp_orig = cursorEvent[:timestamp].to_f
-					timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
-				
-					x = cursorEvent.xpath(".//xOffset")[0].text()
-					y = cursorEvent.xpath(".//yOffset")[0].text()
-					if(timestamp_prev == timestamp)
-				
-					else
-						if(x_prev && y_prev)							
-							$ss.each do |key,val|
-								$val = val
-								if key === timestamp_prev
-									$vbox_width = $val[0]/2 # because the image size is twice as big as the viewbox
-									$vbox_height = $val[1]/2 # because the image size is twice as big as the viewbox
-								end
-							end
-							xPoint = ($vbox_width.to_f*x.to_f).round(1)
-							yPoint = ($vbox_height.to_f*y.to_f).round(1)								
-							if xPoint < 800 and yPoint < 600 and xPoint > 0 and yPoint > 0
-								$xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
-									$xml.cursor "#{xPoint} #{yPoint}"
-								end
-							end
-						end
-					end
-					timestamp_prev = timestamp
-					timestamp_orig_prev = timestamp_orig
-					x_prev = x
-					y_prev = y
-				end
-			end
-		end
-	end
-	BigBlueButton.logger.info("Finished processing cursor events")
+  BigBlueButton.logger.info("Processing cursor events")
+  $cursor_xml = Nokogiri::XML::Builder.new do |xml|
+    $xml = xml
+    $xml.recording('id' => 'cursor_events') do
+      x_prev = nil
+      y_prev = nil
+      timestamp_orig_prev = nil
+      timestamp_prev = nil
+      if(!$cursor_events.empty?)
+        last_time = $cursor_events.last[:timestamp].to_f
+        $cursor_events.each do |cursorEvent|
+          timestamp_orig = cursorEvent[:timestamp].to_f
+          timestamp = ( translateTimestamp(timestamp_orig) / 1000 ).round(1)
+
+          x = cursorEvent.xpath(".//xOffset")[0].text()
+          y = cursorEvent.xpath(".//yOffset")[0].text()
+          if(timestamp_prev == timestamp)
+
+          else
+            if(x_prev && y_prev)
+              $ss.each do |key,val|
+                $val = val
+                if key === timestamp_prev
+                  $vbox_width = $val[0]/2 # because the image size is twice as big as the viewbox
+                  $vbox_height = $val[1]/2 # because the image size is twice as big as the viewbox
+                end
+              end
+              xPoint = ($vbox_width.to_f*x.to_f).round(1)
+              yPoint = ($vbox_height.to_f*y.to_f).round(1)
+              if xPoint < 800 and yPoint < 600 and xPoint > 0 and yPoint > 0
+                $xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
+                  $xml.cursor "#{xPoint} #{yPoint}"
+                end
+              end
+            end
+          end
+          timestamp_prev = timestamp
+          timestamp_orig_prev = timestamp_orig
+          x_prev = x
+          y_prev = y
+        end
+      end
+    end
+  end
+  BigBlueButton.logger.info("Finished processing cursor events")
 end
 
 def processClearEvents
-	# process all the cleared pages events.
-	$clear_page_events.each do |clearEvent|
-		#Retrieve time, page and presentation.
-		clearTime = clearEvent[:timestamp].to_f
-		#clearTime = ( clearEvent[:timestamp].to_f / 1000 ).round(1)
-		$pageCleared = clearEvent.xpath(".//pageNumber")[0].text()
-		slideFolder = clearEvent.xpath(".//presentation")[0].text()
-		if $version_atleast_0_9_0
-			$clearPageTimes[($prev_clear_time..clearTime)] =
-				[$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared.to_i + 1}.png", nil]
-		else
-			$clearPageTimes[($prev_clear_time..clearTime)] =
-				[$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared}.png", nil]
-		end
-		$prev_clear_time = clearTime
-		$canvas_number+=1
-	end
+  # process all the cleared pages events.
+  $clear_page_events.each do |clearEvent|
+    #Retrieve time, page and presentation.
+    clearTime = clearEvent[:timestamp].to_f
+    #clearTime = ( clearEvent[:timestamp].to_f / 1000 ).round(1)
+    $pageCleared = clearEvent.xpath(".//pageNumber")[0].text()
+    slideFolder = clearEvent.xpath(".//presentation")[0].text()
+    if $version_atleast_0_9_0
+      $clearPageTimes[($prev_clear_time..clearTime)] =
+        [$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared.to_i + 1}.png", nil]
+    else
+      $clearPageTimes[($prev_clear_time..clearTime)] =
+        [$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared}.png", nil]
+    end
+    $prev_clear_time = clearTime
+    $canvas_number+=1
+  end
 end
 
 def processUndoEvents
-	# Processing the undo events, creating/filling a hashmap called "undos".
-	BigBlueButton.logger.info("Process undo events.")
-	$undo_events.each do |undo|
-		closest_shape = nil # Initialize as nil to prime the loop.
-		t = undo[:timestamp].to_f
-		$shape_events.each do |shape|
-			# The undo cannot be for a shape that hasn't been drawn yet.
-			if shape[:timestamp].to_f < t
-				# It must be the closest shape drawn that hasn't already been undone.
-				if (closest_shape == nil) || (shape[:timestamp].to_f > closest_shape[:timestamp].to_f)
-					# It cannot be an undo for another shape already.
-					if !($undos.has_key? shape)
-						# Must be part of this presentation of course
-						if shape.xpath(".//pageNumber")[0].text() == undo.xpath(".//pageNumber")[0].text()
-							# Must be a shape in this page too.
-							if shape.xpath(".//presentation")[0].text() == undo.xpath(".//presentation")[0].text()
-								if ((shape.xpath(".//type")[0].text() == "rectangle") || (shape.xpath(".//type")[0].text() == "ellipse"))
-									shape_already_processed = false
-									if($undos.length == 0)
-										shape_already_processed = false
-									else
-										$undos.each do |u, v|
-											if shape.xpath(".//dataPoints")[0].text().split(",")[0] == u.xpath(".//dataPoints")[0].text().split(",")[0]
-												if shape.xpath(".//dataPoints")[0].text().split(",")[1] == u.xpath(".//dataPoints")[0].text().split(",")[1]
-													shape_already_processed = true
-												end
-											end
-										end
-									end
-									if !(shape_already_processed)
-										closest_shape = shape
-									end
-								else
-									closest_shape = shape
-								end
-							end
-						end
-					end
-				end
-			end
-		end
-		if(closest_shape != nil)
-			$undos[closest_shape] = undo[:timestamp]
-		end
-	end
-
-	$undos_temp = {}
-	$undos.each do |un, val|
-		$undos_temp[ un[:timestamp] ] = val
-	end
-	$undos = $undos_temp
-	BigBlueButton.logger.info("Undos: #{$undos}")
+  # Processing the undo events, creating/filling a hashmap called "undos".
+  BigBlueButton.logger.info("Process undo events.")
+  $undo_events.each do |undo|
+    closest_shape = nil # Initialize as nil to prime the loop.
+    t = undo[:timestamp].to_f
+    $shape_events.each do |shape|
+      # The undo cannot be for a shape that hasn't been drawn yet.
+      if shape[:timestamp].to_f < t
+        # It must be the closest shape drawn that hasn't already been undone.
+        if (closest_shape == nil) || (shape[:timestamp].to_f > closest_shape[:timestamp].to_f)
+          # It cannot be an undo for another shape already.
+          if !($undos.has_key? shape)
+            # Must be part of this presentation of course
+            if shape.xpath(".//pageNumber")[0].text() == undo.xpath(".//pageNumber")[0].text()
+              # Must be a shape in this page too.
+              if shape.xpath(".//presentation")[0].text() == undo.xpath(".//presentation")[0].text()
+                if ((shape.xpath(".//type")[0].text() == "rectangle") || (shape.xpath(".//type")[0].text() == "ellipse"))
+                  shape_already_processed = false
+                  if($undos.length == 0)
+                    shape_already_processed = false
+                  else
+                    $undos.each do |u, v|
+                      if shape.xpath(".//dataPoints")[0].text().split(",")[0] == u.xpath(".//dataPoints")[0].text().split(",")[0]
+                        if shape.xpath(".//dataPoints")[0].text().split(",")[1] == u.xpath(".//dataPoints")[0].text().split(",")[1]
+                          shape_already_processed = true
+                        end
+                      end
+                    end
+                  end
+                  if !(shape_already_processed)
+                    closest_shape = shape
+                  end
+                else
+                  closest_shape = shape
+                end
+              end
+            end
+          end
+        end
+      end
+    end
+    if(closest_shape != nil)
+      $undos[closest_shape] = undo[:timestamp]
+    end
+  end
+
+  $undos_temp = {}
+  $undos.each do |un, val|
+    $undos_temp[ un[:timestamp] ] = val
+  end
+  $undos = $undos_temp
+  BigBlueButton.logger.info("Undos: #{$undos}")
 end
 
 def processClearImages
-	BigBlueButton.logger.info("Put image numbers in clearPageTimes")
-	$slides_compiled.each do |key, val|
-		$clearPageTimes.each do |cpt, pgCanvasUrl|
-			# check if the src of the slide matches the url of the clear event
-			if key[0] == pgCanvasUrl[2]
-				# put the image number into the $clearPageTimes
-				pgCanvasUrl[3] = "image#{val[2].to_i}"
-			end
-		end
-	end
+  BigBlueButton.logger.info("Put image numbers in clearPageTimes")
+  $slides_compiled.each do |key, val|
+    $clearPageTimes.each do |cpt, pgCanvasUrl|
+      # check if the src of the slide matches the url of the clear event
+      if key[0] == pgCanvasUrl[2]
+        # put the image number into the $clearPageTimes
+        pgCanvasUrl[3] = "image#{val[2].to_i}"
+      end
+    end
+  end
 end
 
 def storePencilShape
-	$pencil_count = $pencil_count + 1 # always update the line count!
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id=>"draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape =>"line#{$pencil_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; stroke-linecap: round; ") do
-		for i in (0...($shapeDataPoints.length/2)-1) do
-			$xml.line(:x1 => (($shapeDataPoints[i*2].to_f)/100)*$vbox_width, :y1 => (($shapeDataPoints[(i*2)+1].to_f)/100)*$vbox_height, :x2 => (($shapeDataPoints[(i*2)+2].to_f)/100)*$vbox_width, :y2 => (($shapeDataPoints[(i*2)+3].to_f)/100)*$vbox_height)
-		end
-	end
+  $pencil_count = $pencil_count + 1 # always update the line count!
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id=>"draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape =>"line#{$pencil_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; stroke-linecap: round; ") do
+    for i in (0...($shapeDataPoints.length/2)-1) do
+      $xml.line(:x1 => (($shapeDataPoints[i*2].to_f)/100)*$vbox_width, :y1 => (($shapeDataPoints[(i*2)+1].to_f)/100)*$vbox_height, :x2 => (($shapeDataPoints[(i*2)+2].to_f)/100)*$vbox_width, :y2 => (($shapeDataPoints[(i*2)+3].to_f)/100)*$vbox_height)
+    end
+  end
 end
 
 def storeLineShape
-	if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
-		# do not update the line count
-	else
-		$line_count = $line_count + 1
-	end
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "line#{$line_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
-
-		$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
-		$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
-		endPointX = (($shapeDataPoints[2].to_f)/100)*$vbox_width
-		endPointY = (($shapeDataPoints[3].to_f)/100)*$vbox_height
-
-		$originalOriginX = $originX
-		$originalOriginY = $originY
-
-		$xml.line(:x1 => $originX, :y1 => $originY, :x2 => endPointX, :y2 => endPointY )
-		$prev_time = $shapeCreationTime
-	end
+  if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
+    # do not update the line count
+  else
+    $line_count = $line_count + 1
+  end
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "line#{$line_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
+
+    $originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
+    $originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
+    endPointX = (($shapeDataPoints[2].to_f)/100)*$vbox_width
+    endPointY = (($shapeDataPoints[3].to_f)/100)*$vbox_height
+
+    $originalOriginX = $originX
+    $originalOriginY = $originY
+
+    $xml.line(:x1 => $originX, :y1 => $originY, :x2 => endPointX, :y2 => endPointY )
+    $prev_time = $shapeCreationTime
+  end
 end
 
 def storeRectShape
-	if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
-		# do not update the rectangle count
-	else
-		$rectangle_count = $rectangle_count + 1
-	end
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "rect#{$rectangle_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
-		$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
-		$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
-		$originalOriginX = $originX
-		$originalOriginY = $originY
-		rectWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
-		rectHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
-
-		# Cannot have a negative height or width so we adjust
-		if(rectHeight < 0)
-			$originY = $originY + rectHeight
-			rectHeight = rectHeight.abs
-		end
-		if(rectWidth < 0)
-			$originX = $originX + rectWidth
-			rectWidth = rectWidth.abs
-		end
-		if $is_square == "true"
-			#width of the square as reference
-			$xml.rect(:x => $originX, :y => $originY, :width => rectWidth, :height => rectWidth)
-		else
-			$xml.rect(:x => $originX, :y => $originY, :width => rectWidth, :height => rectHeight)
-		end
-		$prev_time = $shapeCreationTime
-	end
+  if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
+    # do not update the rectangle count
+  else
+    $rectangle_count = $rectangle_count + 1
+  end
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "rect#{$rectangle_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
+    $originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
+    $originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
+    $originalOriginX = $originX
+    $originalOriginY = $originY
+    rectWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
+    rectHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
+
+    # Cannot have a negative height or width so we adjust
+    if(rectHeight < 0)
+      $originY = $originY + rectHeight
+      rectHeight = rectHeight.abs
+    end
+    if(rectWidth < 0)
+      $originX = $originX + rectWidth
+      rectWidth = rectWidth.abs
+    end
+    if $is_square == "true"
+      #width of the square as reference
+      $xml.rect(:x => $originX, :y => $originY, :width => rectWidth, :height => rectWidth)
+    else
+      $xml.rect(:x => $originX, :y => $originY, :width => rectWidth, :height => rectHeight)
+    end
+    $prev_time = $shapeCreationTime
+  end
 end
 
 def storeTriangleShape
-	if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
-		# do not update the triangle count
-	else
-		$triangle_count = $triangle_count + 1
-	end
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "triangle#{$triangle_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
+  if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
+    # do not update the triangle count
+  else
+    $triangle_count = $triangle_count + 1
+  end
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "triangle#{$triangle_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
 
-		$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
-		$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
+    $originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
+    $originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
 
-		#3 points (p0, p1 and p2) to draw a triangle
+    #3 points (p0, p1 and p2) to draw a triangle
 
-		base = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
+    base = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
 
-		x0 = $originX + (base.to_f / 2.0)
-		x1 = $originX
-		x2 = $originX + base.to_f
+    x0 = $originX + (base.to_f / 2.0)
+    x1 = $originX
+    x2 = $originX + base.to_f
 
-		height = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
+    height = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
 
-		y0 = $originY
-		y1 = $originY + height
-		y2 = y1
+    y0 = $originY
+    y1 = $originY + height
+    y2 = y1
 
-		p0 = "#{x0},#{y0}"
-		p1 = "#{x1},#{y1}"
-		p2 = "#{x2},#{y2}"
+    p0 = "#{x0},#{y0}"
+    p1 = "#{x1},#{y1}"
+    p2 = "#{x2},#{y2}"
 
-		$originalOriginX = $originX
-		$originalOriginY = $originY
+    $originalOriginX = $originX
+    $originalOriginY = $originY
 
-		$xml.polyline(:points => "#{p0} #{p1} #{p2} #{p0}")
-		$prev_time = $shapeCreationTime
-	end
+    $xml.polyline(:points => "#{p0} #{p1} #{p2} #{p0}")
+    $prev_time = $shapeCreationTime
+  end
 end
 
 def storeEllipseShape
-	if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
-		# do not update the rectangle count
-	else
-		$ellipse_count = $ellipse_count + 1
-	end # end (($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "ellipse#{$ellipse_count}", :style =>"stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
-		$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
-		$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
-		$originalOriginX = $originX
-		$originalOriginY = $originY
-		ellipseWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
-		ellipseHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
-		if(ellipseHeight < 0)
-			$originY = $originY + ellipseHeight
-			ellipseHeight = ellipseHeight.abs
-		end
-		if(ellipseWidth < 0)
-			$originX = $originX + ellipseWidth
-			ellipseWidth = ellipseWidth.abs
-		end
-		if $is_circle == "true"
-			#Use width as reference
-			$xml.circle(:cx => $originX+(ellipseWidth/2), :cy => $originY+(ellipseWidth/2), :r => ellipseWidth/2)
-		else
-			$xml.ellipse(:cx => $originX+(ellipseWidth/2), :cy => $originY+(ellipseHeight/2), :rx => ellipseWidth/2, :ry => ellipseHeight/2)
-		end
-		$prev_time = $shapeCreationTime
-	end # end xml.g
+  if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
+    # do not update the rectangle count
+  else
+    $ellipse_count = $ellipse_count + 1
+  end # end (($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "ellipse#{$ellipse_count}", :style =>"stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
+    $originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
+    $originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
+    $originalOriginX = $originX
+    $originalOriginY = $originY
+    ellipseWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
+    ellipseHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
+    if(ellipseHeight < 0)
+      $originY = $originY + ellipseHeight
+      ellipseHeight = ellipseHeight.abs
+    end
+    if(ellipseWidth < 0)
+      $originX = $originX + ellipseWidth
+      ellipseWidth = ellipseWidth.abs
+    end
+    if $is_circle == "true"
+      #Use width as reference
+      $xml.circle(:cx => $originX+(ellipseWidth/2), :cy => $originY+(ellipseWidth/2), :r => ellipseWidth/2)
+    else
+      $xml.ellipse(:cx => $originX+(ellipseWidth/2), :cy => $originY+(ellipseHeight/2), :rx => ellipseWidth/2, :ry => ellipseHeight/2)
+    end
+    $prev_time = $shapeCreationTime
+  end # end xml.g
 end
 
 def storeTextShape
-	$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
-	$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
-	if(($originalOriginX == $originX) && ($originalOriginY == $originY))
-		# do not update the text count
-	else
-		$text_count = $text_count + 1
-	end
-	font_size_factor = 1.7
-	width_extra_percent = -0.7
-	height_extra_percent = 1
-	width = ( ($textBoxWidth.to_f + width_extra_percent) / 100.0) * $vbox_width
-	height = ( ($textBoxHeight.to_f + height_extra_percent ) / 100.0) * $vbox_height
-	y_gap = -30.0
-	x_gap = 5.0
-	$textFontSize_pixels = $textFontSize.to_f * font_size_factor
-	$global_shape_count += 1
-	$xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "text#{$text_count}", :style => "word-wrap: break-word; visibility:hidden; font-family: #{$textFontType}; font-size: #{$textFontSize_pixels}px;") do
-		$xml.switch do
-			$xml.foreignObject(  :color => "##{$colour_hex}", :width => width, :height => height, :x => "#{((($shapeDataPoints[0].to_f)/100)*$vbox_width) + x_gap}", :y => "#{((($shapeDataPoints[1].to_f)/100) *$vbox_height )  + y_gap.to_f }") do
-				$xml.p( :xmlns => "http://www.w3.org/1999/xhtml" ) do
-					$xml.text($textValue)
-				end
-			end
-		end
-		$prev_time = $shapeCreationTime
-	end # end xml.g
-	$originalOriginX = $originX
-	$originalOriginY = $originY
+  $originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
+  $originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
+  if(($originalOriginX == $originX) && ($originalOriginY == $originY))
+    # do not update the text count
+  else
+    $text_count = $text_count + 1
+  end
+  font_size_factor = 1.7
+  width_extra_percent = -0.7
+  height_extra_percent = 1
+  width = ( ($textBoxWidth.to_f + width_extra_percent) / 100.0) * $vbox_width
+  height = ( ($textBoxHeight.to_f + height_extra_percent ) / 100.0) * $vbox_height
+  y_gap = -30.0
+  x_gap = 5.0
+  $textFontSize_pixels = $textFontSize.to_f * font_size_factor
+  $global_shape_count += 1
+  $xml.g(:class => :shape, :id => "draw#{$global_shape_count}", :timestamp => $shapeCreationTime, :undo => $shapeUndoTime, :shape => "text#{$text_count}", :style => "word-wrap: break-word; visibility:hidden; font-family: #{$textFontType}; font-size: #{$textFontSize_pixels}px;") do
+    $xml.switch do
+      $xml.foreignObject(  :color => "##{$colour_hex}", :width => width, :height => height, :x => "#{((($shapeDataPoints[0].to_f)/100)*$vbox_width) + x_gap}", :y => "#{((($shapeDataPoints[1].to_f)/100) *$vbox_height )  + y_gap.to_f }") do
+        $xml.p( :xmlns => "http://www.w3.org/1999/xhtml" ) do
+          $xml.text($textValue)
+        end
+      end
+    end
+    $prev_time = $shapeCreationTime
+  end # end xml.g
+  $originalOriginX = $originX
+  $originalOriginY = $originY
 end
 
 def storePollResultShape(xml, shape)
@@ -522,212 +522,212 @@ end
 # to translate the timestamps later based on these offsets
 #
 def calculateRecordEventsOffset
-	accumulated_duration = 0
-	previous_stop_recording = $meeting_start.to_f
-	$rec_events.each do |event|
-		event[:offset] = event[:start_timestamp] - accumulated_duration
-		event[:duration] = event[:stop_timestamp] - event[:start_timestamp]
-		event[:accumulated_duration] = accumulated_duration
-
-		previous_stop_recording = event[:stop_timestamp]
-		accumulated_duration += event[:duration]
-	end
+  accumulated_duration = 0
+  previous_stop_recording = $meeting_start.to_f
+  $rec_events.each do |event|
+    event[:offset] = event[:start_timestamp] - accumulated_duration
+    event[:duration] = event[:stop_timestamp] - event[:start_timestamp]
+    event[:accumulated_duration] = accumulated_duration
+
+    previous_stop_recording = event[:stop_timestamp]
+    accumulated_duration += event[:duration]
+  end
 end
 
 #
-# Translated an arbitrary Unix timestamp to the recording timestamp. This is the 
+# Translated an arbitrary Unix timestamp to the recording timestamp. This is the
 # function that others will call
 #
 def translateTimestamp(timestamp)
-	new_timestamp = translateTimestamp_helper(timestamp.to_f).to_f
+  new_timestamp = translateTimestamp_helper(timestamp.to_f).to_f
 #	BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-$meeting_start.to_f}, new value=#{new_timestamp}")
-	new_timestamp
+  new_timestamp
 end
 
 #
 # Translated an arbitrary Unix timestamp to the recording timestamp
 #
 def translateTimestamp_helper(timestamp)
-	$rec_events.each do |event|
-		# if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording
-		if timestamp <= event[:start_timestamp]
-			return event[:start_timestamp] - event[:offset]
-		# if the timestamp is during the recording period, it is just translated to the new one using the offset
-		elsif timestamp > event[:start_timestamp] and timestamp <= event[:stop_timestamp]
-			return timestamp - event[:offset]
-		end
-	end
-	# if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp
-	return timestamp - $rec_events.last()[:offset] + $rec_events.last()[:duration]
+  $rec_events.each do |event|
+    # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording
+    if timestamp <= event[:start_timestamp]
+      return event[:start_timestamp] - event[:offset]
+    # if the timestamp is during the recording period, it is just translated to the new one using the offset
+    elsif timestamp > event[:start_timestamp] and timestamp <= event[:stop_timestamp]
+      return timestamp - event[:offset]
+    end
+  end
+  # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp
+  return timestamp - $rec_events.last()[:offset] + $rec_events.last()[:duration]
 end
 #
 # Given an event timestamp, says whether it occurs during a recording period or not.
 #
 def occursDuringRecording(timestamp)
-	$rec_events.each do |event|
-		if timestamp >= event[:start_timestamp] and timestamp <= event[:stop_timestamp]
-			return true
-		end
-	end
-	return false
+  $rec_events.each do |event|
+    if timestamp >= event[:start_timestamp] and timestamp <= event[:stop_timestamp]
+      return true
+    end
+  end
+  return false
 end
 
 #
 # Calculates the length of a recording
 #
 def computeRecordingLength()
-	recordingLength = 0
-	$rec_events.each do |event|
-		recordingLength += event[:stop_timestamp] - event[:start_timestamp]
-	end
-	recordingLength
+  recordingLength = 0
+  $rec_events.each do |event|
+    recordingLength += event[:stop_timestamp] - event[:start_timestamp]
+  end
+  recordingLength
 end
 
 def preprocessSlideEvents
-	new_slides_events = []
-	$slides_events.each do |slide_event|
-		new_slide_event = slide_event.clone
-		$rec_events.each do |rec_event|
-			if new_slide_event[:timestamp] <= rec_event[:start_timestamp]
-				new_slide_event[:timestamp] = rec_event[:start_timestamp]
-				if not new_slides_events.empty? and new_slides_events.last()[:timestamp] == rec_event[:start_timestamp]
-					new_slides_events.pop()
-				end
-				new_slides_events << new_slide_event
-				break
-			elsif new_slide_event[:timestamp] > rec_event[:start_timestamp] and new_slide_event[:timestamp] <= rec_event[:stop_timestamp]
-				new_slides_events << new_slide_event
-			end
-		end
-	end
-	return new_slides_events
+  new_slides_events = []
+  $slides_events.each do |slide_event|
+    new_slide_event = slide_event.clone
+    $rec_events.each do |rec_event|
+      if new_slide_event[:timestamp] <= rec_event[:start_timestamp]
+        new_slide_event[:timestamp] = rec_event[:start_timestamp]
+        if not new_slides_events.empty? and new_slides_events.last()[:timestamp] == rec_event[:start_timestamp]
+          new_slides_events.pop()
+        end
+        new_slides_events << new_slide_event
+        break
+      elsif new_slide_event[:timestamp] > rec_event[:start_timestamp] and new_slide_event[:timestamp] <= rec_event[:stop_timestamp]
+        new_slides_events << new_slide_event
+      end
+    end
+  end
+  return new_slides_events
 end
 
 def processSlideEvents
-	BigBlueButton.logger.info("Slide events processing")
-	# For each slide (there is only one image per slide)
-	$slides_events.each do |node|
-		# Ignore slide events that happened after the last recording period.
-		if(node[:timestamp].to_f > $rec_events.last[:stop_timestamp].to_f)
-			next
-		end
-		eventname = node['eventname']
-		if eventname == "SharePresentationEvent"
-			$presentation_name = node.xpath(".//presentationName")[0].text()
-		else
-			slide_timestamp =  node[:timestamp]
-			slide_start = ( translateTimestamp(slide_timestamp) / 1000 ).round(1)
-			orig_slide_start = ( slide_timestamp.to_f / 1000 ).round(1)
-			slide_number = node.xpath(".//slide")[0].text().to_i
+  BigBlueButton.logger.info("Slide events processing")
+  # For each slide (there is only one image per slide)
+  $slides_events.each do |node|
+    # Ignore slide events that happened after the last recording period.
+    if(node[:timestamp].to_f > $rec_events.last[:stop_timestamp].to_f)
+      next
+    end
+    eventname = node['eventname']
+    if eventname == "SharePresentationEvent"
+      $presentation_name = node.xpath(".//presentationName")[0].text()
+    else
+      slide_timestamp =  node[:timestamp]
+      slide_start = ( translateTimestamp(slide_timestamp) / 1000 ).round(1)
+      orig_slide_start = ( slide_timestamp.to_f / 1000 ).round(1)
+      slide_number = node.xpath(".//slide")[0].text().to_i
                         slide_number = slide_number < 0 ? 0 : slide_number
-			slide_src = "presentation/#{$presentation_name}/slide-#{slide_number + 1}.png"
+      slide_src = "presentation/#{$presentation_name}/slide-#{slide_number + 1}.png"
                         txt_file_path = "presentation/#{$presentation_name}/textfiles/slide-#{slide_number + 1}.txt"
                         slide_text = File.exist?("#{$process_dir}/#{txt_file_path}") ? txt_file_path : nil
-			image_url = "#{$process_dir}/#{slide_src}"
-
-			if !File.exist?(image_url)
-				BigBlueButton.logger.warn("Missing image file #{slide_src}!")
-				# Emergency last-ditch blank image creation
-				FileUtils.mkdir_p("#{$process_dir}/presentation/#{$presentation_name}")
-				command = "convert -size 1600x1200 xc:white -quality 90 +dither -depth 8 -colors 256 #{image_url}"
-				BigBlueButton.execute(command)
-			end
-
-			slide_size = FastImage.size(image_url)
-			current_index = $slides_events.index(node)
-			if(current_index + 1 < $slides_events.length)
-				slide_end = ( translateTimestamp($slides_events[current_index + 1][:timestamp]) / 1000 ).round(1)
-				orig_slide_end = ( $slides_events[current_index + 1][:timestamp].to_f / 1000 ).round(1)
-			else
-				slide_end = ( translateTimestamp($meeting_end) / 1000 ).round(1)
-				orig_slide_end = ( $meeting_end.to_f / 1000 ).round(1)
-			end
-
-			if slide_start == slide_end
-				BigBlueButton.logger.info("#{slide_src} is never displayed (slide_start = slide_end), so it won't be included in the svg")
-				next
-			end
-
-			BigBlueButton.logger.info("Processing slide image")
-			# Is this a new image or one previously viewed?
-			if($slides_compiled[[slide_src, slide_size[1], slide_size[0]]] == nil)
-				# If it is, add it to the list with all the data.
-				$slides_compiled[[slide_src, slide_size[1], slide_size[0]]] = [[slide_start], [slide_end], $global_slide_count, slide_text, [orig_slide_start], [orig_slide_end]]
-				$global_slide_count = $global_slide_count + 1
-			else
-				# If not, append new in and out times to the old entry
-				# But if the previous slide_end is equal to the current slide_start, we just pop the previous slide_end and push the current one
-				# It will avoid the duplication of the thumbnails on the playback
-				if($slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1].last == slide_start)
-					$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1].pop
-					$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1] << slide_end
-				else
-					$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][0] << slide_start
-					$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1] << slide_end
-				end
-				$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][4] << orig_slide_start
-				$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][5] << orig_slide_end
-			end
-
-			$ss[(slide_start..slide_end)] = slide_size # store the size of the slide at that range of time
-			puts "#{slide_src} : #{slide_start} -> #{slide_end}"
-		end
-	end
+      image_url = "#{$process_dir}/#{slide_src}"
+
+      if !File.exist?(image_url)
+        BigBlueButton.logger.warn("Missing image file #{slide_src}!")
+        # Emergency last-ditch blank image creation
+        FileUtils.mkdir_p("#{$process_dir}/presentation/#{$presentation_name}")
+        command = "convert -size 1600x1200 xc:white -quality 90 +dither -depth 8 -colors 256 #{image_url}"
+        BigBlueButton.execute(command)
+      end
+
+      slide_size = FastImage.size(image_url)
+      current_index = $slides_events.index(node)
+      if(current_index + 1 < $slides_events.length)
+        slide_end = ( translateTimestamp($slides_events[current_index + 1][:timestamp]) / 1000 ).round(1)
+        orig_slide_end = ( $slides_events[current_index + 1][:timestamp].to_f / 1000 ).round(1)
+      else
+        slide_end = ( translateTimestamp($meeting_end) / 1000 ).round(1)
+        orig_slide_end = ( $meeting_end.to_f / 1000 ).round(1)
+      end
+
+      if slide_start == slide_end
+        BigBlueButton.logger.info("#{slide_src} is never displayed (slide_start = slide_end), so it won't be included in the svg")
+        next
+      end
+
+      BigBlueButton.logger.info("Processing slide image")
+      # Is this a new image or one previously viewed?
+      if($slides_compiled[[slide_src, slide_size[1], slide_size[0]]] == nil)
+        # If it is, add it to the list with all the data.
+        $slides_compiled[[slide_src, slide_size[1], slide_size[0]]] = [[slide_start], [slide_end], $global_slide_count, slide_text, [orig_slide_start], [orig_slide_end]]
+        $global_slide_count = $global_slide_count + 1
+      else
+        # If not, append new in and out times to the old entry
+        # But if the previous slide_end is equal to the current slide_start, we just pop the previous slide_end and push the current one
+        # It will avoid the duplication of the thumbnails on the playback
+        if($slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1].last == slide_start)
+          $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1].pop
+          $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1] << slide_end
+        else
+          $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][0] << slide_start
+          $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1] << slide_end
+        end
+        $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][4] << orig_slide_start
+        $slides_compiled[[slide_src, slide_size[1], slide_size[0]]][5] << orig_slide_end
+      end
+
+      $ss[(slide_start..slide_end)] = slide_size # store the size of the slide at that range of time
+      puts "#{slide_src} : #{slide_start} -> #{slide_end}"
+    end
+  end
 end
 
 def processShapesAndClears
-	# Create shapes.svg file from the events.xml
-	BigBlueButton.logger.info("Creating shapes.svg")
-	$shapes_svg = Nokogiri::XML::Builder.new do |xml|
-		$xml = xml
-		
-		processClearEvents()
-		processUndoEvents()
-		
-		# Put in the last clear events numbers (previous clear to the end of the slideshow)
-		#endPresentationTime = ( $end_time.to_f / 1000 ).round(1)
-		endPresentationTime = $end_time.to_f
-		$clearPageTimes[($prev_clear_time..endPresentationTime)] = [$pageCleared, $canvas_number, nil, nil]
-		
-		# Put the headers on the svg xml file.
-		$xml.doc.create_internal_subset('svg', "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd")
-		$xml.svg(:id => :svgfile, :style => 'position:absolute; height:600px; width:800px;', :xmlns => 'http://www.w3.org/2000/svg', 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', :version => '1.1', :viewBox => :'0 0 800 600') do
-
-			# This is for the first image. It is a placeholder for an image that doesn't exist.
-			$xml.image(:id => :image0, :class => 'slide', :in => 0, :out => $first_slide_start, :src => "logo.png", :width => 800)
-			$xml.g(:class => :canvas, :id => :canvas0, :image => :image0, :display => :none)
-			$presentation_name = ""
-			
-			processSlideEvents()
-			processClearImages()
-
-			BigBlueButton.logger.info("Printing out the gathered images")
-			# Print out the gathered/detected images. 
-			$slides_compiled.each do |key, val|
-				$val = val
-				$xml.image(:id => "image#{$val[2].to_i}", :class => 'slide', :in => $val[0].join(' '), :out => $val[1].join(' '), 'xlink:href' => key[0], :height => key[1], :width => key[2], :visibility => :hidden, :text => $val[3], :x => 0)
-				$canvas_number+=1
-				$xml.g(:class => :canvas, :id => "canvas#{$val[2].to_i}", :image => "image#{$val[2].to_i}", :display => :none) do
-					
-					BigBlueButton.logger.info("Processing shapes within the image #{$val[2].to_i}")
-					# Select and print the shapes within the current image
-					$shape_events.each do |shape|
-						$shapeTimestamp = shape[:timestamp].to_f
-						$shapeCreationTime = ( translateTimestamp($shapeTimestamp) / 1000 ).round(1)
-						orig_shapeCreationTime = ( $shapeTimestamp.to_f / 1000 ).round(1)
-						in_this_image = false
-						index = 0
-						numOfTimes = $val[0].length
-
-						# Check if the current shape is to be drawn in this particular image
-						while((in_this_image == false) && (index < numOfTimes)) do
-							if((($val[4][index].to_f)..($val[5][index].to_f)) === orig_shapeCreationTime) # is the shape within the certain time of the image
-								in_this_image = true
-							end
-							index+=1
-						end
-
-						if(in_this_image)
+  # Create shapes.svg file from the events.xml
+  BigBlueButton.logger.info("Creating shapes.svg")
+  $shapes_svg = Nokogiri::XML::Builder.new do |xml|
+    $xml = xml
+
+    processClearEvents()
+    processUndoEvents()
+
+    # Put in the last clear events numbers (previous clear to the end of the slideshow)
+    #endPresentationTime = ( $end_time.to_f / 1000 ).round(1)
+    endPresentationTime = $end_time.to_f
+    $clearPageTimes[($prev_clear_time..endPresentationTime)] = [$pageCleared, $canvas_number, nil, nil]
+
+    # Put the headers on the svg xml file.
+    $xml.doc.create_internal_subset('svg', "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd")
+    $xml.svg(:id => :svgfile, :style => 'position:absolute; height:600px; width:800px;', :xmlns => 'http://www.w3.org/2000/svg', 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', :version => '1.1', :viewBox => :'0 0 800 600') do
+
+      # This is for the first image. It is a placeholder for an image that doesn't exist.
+      $xml.image(:id => :image0, :class => 'slide', :in => 0, :out => $first_slide_start, :src => "logo.png", :width => 800)
+      $xml.g(:class => :canvas, :id => :canvas0, :image => :image0, :display => :none)
+      $presentation_name = ""
+
+      processSlideEvents()
+      processClearImages()
+
+      BigBlueButton.logger.info("Printing out the gathered images")
+      # Print out the gathered/detected images.
+      $slides_compiled.each do |key, val|
+        $val = val
+        $xml.image(:id => "image#{$val[2].to_i}", :class => 'slide', :in => $val[0].join(' '), :out => $val[1].join(' '), 'xlink:href' => key[0], :height => key[1], :width => key[2], :visibility => :hidden, :text => $val[3], :x => 0)
+        $canvas_number+=1
+        $xml.g(:class => :canvas, :id => "canvas#{$val[2].to_i}", :image => "image#{$val[2].to_i}", :display => :none) do
+
+          BigBlueButton.logger.info("Processing shapes within the image #{$val[2].to_i}")
+          # Select and print the shapes within the current image
+          $shape_events.each do |shape|
+            $shapeTimestamp = shape[:timestamp].to_f
+            $shapeCreationTime = ( translateTimestamp($shapeTimestamp) / 1000 ).round(1)
+            orig_shapeCreationTime = ( $shapeTimestamp.to_f / 1000 ).round(1)
+            in_this_image = false
+            index = 0
+            numOfTimes = $val[0].length
+
+            # Check if the current shape is to be drawn in this particular image
+            while((in_this_image == false) && (index < numOfTimes)) do
+              if((($val[4][index].to_f)..($val[5][index].to_f)) === orig_shapeCreationTime) # is the shape within the certain time of the image
+                in_this_image = true
+              end
+              index+=1
+            end
+
+            if(in_this_image)
                                                         # Get variables
                                                         BigBlueButton.logger.info shape.to_xml(:indent => 2)
                                                         $shapeType = shape.xpath(".//type")[0].text()
@@ -746,116 +746,116 @@ def processShapesAndClears
                                                         when 'poll_result'
                                                           # Just hand the 'shape' xml object to the poll rendering code.
                                                         end
-							
-							# figure out undo time
-							BigBlueButton.logger.info("Figuring out undo time")
-							if($undos.has_key? ( shape[:timestamp] ))
-								$shapeUndoTime = ( translateTimestamp( $undos[ shape[:timestamp] ] ) / 1000).round(1)
-							else						
-								$shapeUndoTime = -1
-							end
-							
-							clear_time = -1
-							$clearPageTimes.each do |clearTimeInstance, pageAndCanvasNumbers|
-								$clearTimeInstance = clearTimeInstance
-								$pageAndCanvasNumbers = pageAndCanvasNumbers
-								if(($clearTimeInstance.last > $shapeTimestamp) && ($pageAndCanvasNumbers[3] == "image#{$val[2].to_i}"))
-									if((clear_time > ( translateTimestamp($clearTimeInstance.last) / 1000 ).round(1)) || (clear_time == -1))
-										clear_time = ( translateTimestamp($clearTimeInstance.last) / 1000 ).round(1)
-									end
-								end
-							end
-							
-							if($shapeUndoTime == -1)
-								if(clear_time == -1)
-									$shapeUndoTime = -1 # nothing changes
-								elsif(clear_time != -1)
-									$shapeUndoTime = clear_time
-								end
-							elsif($shapeUndoTime != -1)
-								if(clear_time == -1)
-									$shapeUndoTime = $shapeUndoTime #nothing changes
-								elsif (clear_time != -1)
-									if(clear_time < $shapeUndoTime)
-										$shapeUndoTime = clear_time
-									else
-										$shapeUndoTime = $shapeUndoTime # nothing changes
-									end
-								end
-							end
-							
-							# Process colours
-							$colour_hex = colour.to_i.to_s(16) # convert from base 10 to base 16 (hex)
-							$colour_hex='0'*(6-$colour_hex.length) + $colour_hex # pad the number with 0's to give it a length of 6
-								
-							# resolve the current image height and width
-							$ss.each do |t,size|
-								if t === $shapeCreationTime
-									$vbox_width = size[0]
-									$vbox_height = size[1]
-								end
-							end
-							
+
+              # figure out undo time
+              BigBlueButton.logger.info("Figuring out undo time")
+              if($undos.has_key? ( shape[:timestamp] ))
+                $shapeUndoTime = ( translateTimestamp( $undos[ shape[:timestamp] ] ) / 1000).round(1)
+              else
+                $shapeUndoTime = -1
+              end
+
+              clear_time = -1
+              $clearPageTimes.each do |clearTimeInstance, pageAndCanvasNumbers|
+                $clearTimeInstance = clearTimeInstance
+                $pageAndCanvasNumbers = pageAndCanvasNumbers
+                if(($clearTimeInstance.last > $shapeTimestamp) && ($pageAndCanvasNumbers[3] == "image#{$val[2].to_i}"))
+                  if((clear_time > ( translateTimestamp($clearTimeInstance.last) / 1000 ).round(1)) || (clear_time == -1))
+                    clear_time = ( translateTimestamp($clearTimeInstance.last) / 1000 ).round(1)
+                  end
+                end
+              end
+
+              if($shapeUndoTime == -1)
+                if(clear_time == -1)
+                  $shapeUndoTime = -1 # nothing changes
+                elsif(clear_time != -1)
+                  $shapeUndoTime = clear_time
+                end
+              elsif($shapeUndoTime != -1)
+                if(clear_time == -1)
+                  $shapeUndoTime = $shapeUndoTime #nothing changes
+                elsif (clear_time != -1)
+                  if(clear_time < $shapeUndoTime)
+                    $shapeUndoTime = clear_time
+                  else
+                    $shapeUndoTime = $shapeUndoTime # nothing changes
+                  end
+                end
+              end
+
+              # Process colours
+              $colour_hex = colour.to_i.to_s(16) # convert from base 10 to base 16 (hex)
+              $colour_hex='0'*(6-$colour_hex.length) + $colour_hex # pad the number with 0's to give it a length of 6
+
+              # resolve the current image height and width
+              $ss.each do |t,size|
+                if t === $shapeCreationTime
+                  $vbox_width = size[0]
+                  $vbox_height = size[1]
+                end
+              end
+
                                                         case $shapeType
                                                         when 'pencil'
-								storePencilShape()
+                storePencilShape()
                                                         when 'line'
-								storeLineShape()
+                storeLineShape()
                                                         when 'rectangle'
-								square = shape.xpath(".//square")
-								if square.length > 0
-									$is_square = square[0].text()
-								else
-									$is_square = 'false'
-								end
-								storeRectShape()
+                square = shape.xpath(".//square")
+                if square.length > 0
+                  $is_square = square[0].text()
+                else
+                  $is_square = 'false'
+                end
+                storeRectShape()
                                                         when 'triangle'
-								storeTriangleShape()
+                storeTriangleShape()
                                                         when 'ellipse'
-								circle = shape.xpath(".//circle")
-								if circle.length > 0
-									$is_circle = circle[0].text()
-								else
-									$is_circle = 'false'
-								end
-								storeEllipseShape()
+                circle = shape.xpath(".//circle")
+                if circle.length > 0
+                  $is_circle = circle[0].text()
+                else
+                  $is_circle = 'false'
+                end
+                storeEllipseShape()
                                                         when 'text'
-								$textBoxWidth = shape.xpath(".//textBoxWidth")[0].text()
-								$textBoxHeight = shape.xpath(".//textBoxHeight")[0].text()
-								storeTextShape()
+                $textBoxWidth = shape.xpath(".//textBoxWidth")[0].text()
+                $textBoxHeight = shape.xpath(".//textBoxHeight")[0].text()
+                storeTextShape()
                                                         when 'poll_result'
                                                           storePollResultShape($xml, shape)
-							end
-						end # end if(in_this_image)
-					end # end shape_events.each do |shape|
-				end
-			end
-		end
-	end
+              end
+            end # end if(in_this_image)
+          end # end shape_events.each do |shape|
+        end
+      end
+    end
+  end
 end
 
 def processChatMessages
-	BigBlueButton.logger.info("Processing chat events")
-	# Create slides.xml and chat.
-	$slides_doc = Nokogiri::XML::Builder.new do |xml|
-		$xml = xml
-		$xml.popcorn {
-			# Process chat events.
-			current_time = 0
-			$rec_events.each do |re|
-				$chat_events.each do |node|
-					if (node[:timestamp].to_i >= re[:start_timestamp] and node[:timestamp].to_i <= re[:stop_timestamp])
-						chat_timestamp =  node[:timestamp]
-						chat_sender = node.xpath(".//sender")[0].text()
-						chat_message =  BigBlueButton::Events.linkify(node.xpath(".//message")[0].text())
-						chat_start = ( translateTimestamp(chat_timestamp) / 1000).to_i
-						$xml.chattimeline(:in => chat_start, :direction => :down,  :name => chat_sender, :message => chat_message, :target => :chat )
-					end
-				end
-				current_time += re[:stop_timestamp] - re[:start_timestamp]
-			end
-		}
-	end
+  BigBlueButton.logger.info("Processing chat events")
+  # Create slides.xml and chat.
+  $slides_doc = Nokogiri::XML::Builder.new do |xml|
+    $xml = xml
+    $xml.popcorn {
+      # Process chat events.
+      current_time = 0
+      $rec_events.each do |re|
+        $chat_events.each do |node|
+          if (node[:timestamp].to_i >= re[:start_timestamp] and node[:timestamp].to_i <= re[:stop_timestamp])
+            chat_timestamp =  node[:timestamp]
+            chat_sender = node.xpath(".//sender")[0].text()
+            chat_message =  BigBlueButton::Events.linkify(node.xpath(".//message")[0].text())
+            chat_start = ( translateTimestamp(chat_timestamp) / 1000).to_i
+            $xml.chattimeline(:in => chat_start, :direction => :down,  :name => chat_sender, :message => chat_message, :target => :chat )
+          end
+        end
+        current_time += re[:stop_timestamp] - re[:start_timestamp]
+      end
+    }
+  end
 end
 
 $vbox_width = 1600
@@ -912,175 +912,175 @@ begin
 
 if ($playback == "presentation")
 
-	# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
-	bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
-	simple_props = YAML::load(File.open('presentation.yml'))
+  # This script lives in scripts/archive/steps while properties.yaml lives in scripts/
+  bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
+  simple_props = YAML::load(File.open('presentation.yml'))
 
     log_dir = bbb_props['log_dir']
 
-	logger = Logger.new("#{log_dir}/presentation/publish-#{$meeting_id}.log", 'daily' )
-	BigBlueButton.logger = logger
+  logger = Logger.new("#{log_dir}/presentation/publish-#{$meeting_id}.log", 'daily' )
+  BigBlueButton.logger = logger
 
-	BigBlueButton.logger.info("Setting recording dir")
-	recording_dir = bbb_props['recording_dir']
-	BigBlueButton.logger.info("Setting process dir")
-	$process_dir = "#{recording_dir}/process/presentation/#{$meeting_id}"
-	BigBlueButton.logger.info("setting publish dir")
-	publish_dir = simple_props['publish_dir']
-	BigBlueButton.logger.info("setting playback url info")
+  BigBlueButton.logger.info("Setting recording dir")
+  recording_dir = bbb_props['recording_dir']
+  BigBlueButton.logger.info("Setting process dir")
+  $process_dir = "#{recording_dir}/process/presentation/#{$meeting_id}"
+  BigBlueButton.logger.info("setting publish dir")
+  publish_dir = simple_props['publish_dir']
+  BigBlueButton.logger.info("setting playback url info")
         playback_protocol = bbb_props['playback_protocol']
-	playback_host = bbb_props['playback_host']
-	BigBlueButton.logger.info("setting target dir")
-	target_dir = "#{recording_dir}/publish/presentation/#{$meeting_id}"
-	if not FileTest.directory?(target_dir)
-		BigBlueButton.logger.info("Making dir target_dir")
-		FileUtils.mkdir_p target_dir
-
-		package_dir = "#{target_dir}/#{$meeting_id}"
-		BigBlueButton.logger.info("Making dir package_dir")
-		FileUtils.mkdir_p package_dir
-
-		begin	
-		
-		if File.exist?("#{$process_dir}/webcams.webm")
-			BigBlueButton.logger.info("Making video dir")
-			video_dir = "#{package_dir}/video"
-			FileUtils.mkdir_p video_dir
-			BigBlueButton.logger.info("Made video dir - copying: #{$process_dir}/webcams.webm to -> #{video_dir}")
-			FileUtils.cp("#{$process_dir}/webcams.webm", video_dir)
-			BigBlueButton.logger.info("Copied .webm file")
-		else
-			audio_dir = "#{package_dir}/audio"
-			BigBlueButton.logger.info("Making audio dir")
-			FileUtils.mkdir_p audio_dir
-			BigBlueButton.logger.info("Made audio dir - copying: #{$process_dir}/audio.webm to -> #{audio_dir}")
-			FileUtils.cp("#{$process_dir}/audio.webm", audio_dir)
-			BigBlueButton.logger.info("Copied audio.webm file - copying: #{$process_dir}/audio.ogg to -> #{audio_dir}")
-			FileUtils.cp("#{$process_dir}/audio.ogg", audio_dir)
-			BigBlueButton.logger.info("Copied audio.ogg file")
-		end
-
-
-		processing_time = File.read("#{$process_dir}/processing_time")
-
-		# Retrieve record events and calculate total recording duration.
-		$rec_events = BigBlueButton::Events.match_start_and_stop_rec_events(
-				BigBlueButton::Events.get_start_and_stop_rec_events("#{$process_dir}/events.xml"))
-
-		recording_time = computeRecordingLength()
-
-		# presentation_url = "/slides/" + $meeting_id + "/presentation"
-		@doc = Nokogiri::XML(File.open("#{$process_dir}/events.xml"))
-
-		$meeting_start = @doc.xpath("//event")[0][:timestamp]
-		$meeting_end = @doc.xpath("//event").last()[:timestamp]
-		
-		$version = BigBlueButton::Events.bbb_version("#{$process_dir}/events.xml")
-		$version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare("#{$process_dir}/events.xml", 0, 9, 0)
-		BigBlueButton.logger.info("Creating metadata.xml")
+  playback_host = bbb_props['playback_host']
+  BigBlueButton.logger.info("setting target dir")
+  target_dir = "#{recording_dir}/publish/presentation/#{$meeting_id}"
+  if not FileTest.directory?(target_dir)
+    BigBlueButton.logger.info("Making dir target_dir")
+    FileUtils.mkdir_p target_dir
+
+    package_dir = "#{target_dir}/#{$meeting_id}"
+    BigBlueButton.logger.info("Making dir package_dir")
+    FileUtils.mkdir_p package_dir
+
+    begin
+
+    if File.exist?("#{$process_dir}/webcams.webm")
+      BigBlueButton.logger.info("Making video dir")
+      video_dir = "#{package_dir}/video"
+      FileUtils.mkdir_p video_dir
+      BigBlueButton.logger.info("Made video dir - copying: #{$process_dir}/webcams.webm to -> #{video_dir}")
+      FileUtils.cp("#{$process_dir}/webcams.webm", video_dir)
+      BigBlueButton.logger.info("Copied .webm file")
+    else
+      audio_dir = "#{package_dir}/audio"
+      BigBlueButton.logger.info("Making audio dir")
+      FileUtils.mkdir_p audio_dir
+      BigBlueButton.logger.info("Made audio dir - copying: #{$process_dir}/audio.webm to -> #{audio_dir}")
+      FileUtils.cp("#{$process_dir}/audio.webm", audio_dir)
+      BigBlueButton.logger.info("Copied audio.webm file - copying: #{$process_dir}/audio.ogg to -> #{audio_dir}")
+      FileUtils.cp("#{$process_dir}/audio.ogg", audio_dir)
+      BigBlueButton.logger.info("Copied audio.ogg file")
+    end
+
+
+    processing_time = File.read("#{$process_dir}/processing_time")
+
+    # Retrieve record events and calculate total recording duration.
+    $rec_events = BigBlueButton::Events.match_start_and_stop_rec_events(
+        BigBlueButton::Events.get_start_and_stop_rec_events("#{$process_dir}/events.xml"))
+
+    recording_time = computeRecordingLength()
+
+    # presentation_url = "/slides/" + $meeting_id + "/presentation"
+    @doc = Nokogiri::XML(File.open("#{$process_dir}/events.xml"))
+
+    $meeting_start = @doc.xpath("//event")[0][:timestamp]
+    $meeting_end = @doc.xpath("//event").last()[:timestamp]
+
+    $version = BigBlueButton::Events.bbb_version("#{$process_dir}/events.xml")
+    $version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare("#{$process_dir}/events.xml", 0, 9, 0)
+    BigBlueButton.logger.info("Creating metadata.xml")
 
                 # Get the real-time start and end timestamp
                 match = /.*-(\d+)$/.match($meeting_id)
                 real_start_time = match[1]
                 real_end_time = (real_start_time.to_i + ($meeting_end.to_i - $meeting_start.to_i)).to_s
 
-		# Create metadata.xml
-		b = Builder::XmlMarkup.new(:indent => 2)
-
-		metaxml = b.recording {
-			b.id($meeting_id)
-			b.state("available")
-			b.published(true)
-			# Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
-			b.start_time(real_start_time)
-			b.end_time(real_end_time)
-			b.playback {
-				b.format("presentation")
-				b.link("#{playback_protocol}://#{playback_host}/playback/presentation/0.9.0/playback.html?meetingId=#{$meeting_id}")
-				b.processing_time("#{processing_time}")
-				b.duration("#{recording_time}")
-			}
-			b.meta {
-				BigBlueButton::Events.get_meeting_metadata("#{$process_dir}/events.xml").each { |k,v| b.method_missing(k,v) }
-			}
-		}
-		metadata_xml = File.new("#{package_dir}/metadata.xml","w")
-		metadata_xml.write(metaxml)
-		metadata_xml.close
-		BigBlueButton.logger.info("Generating xml for slides and chat")
-	
-		#Create slides.xml
-
-		# Gathering all the events from the events.xml
-		$slides_events = @doc.xpath("//event[@eventname='GotoSlideEvent' or @eventname='SharePresentationEvent']")
-		$chat_events = @doc.xpath("//event[@eventname='PublicChatEvent']")
-		$shape_events = @doc.xpath("//event[@eventname='AddShapeEvent' or @eventname='ModifyTextEvent']") # for the creation of shapes
-		$panzoom_events = @doc.xpath("//event[@eventname='ResizeAndMoveSlideEvent']") # for the action of panning and/or zooming
-		$cursor_events = @doc.xpath("//event[@eventname='CursorMoveEvent']")
-		$clear_page_events = @doc.xpath("//event[@eventname='ClearPageEvent']") # for clearing the svg image
-		$undo_events = @doc.xpath("//event[@eventname='UndoShapeEvent']") # for undoing shapes.
-		$join_time = $meeting_start.to_f
-		$end_time = $meeting_end.to_f
-
-		calculateRecordEventsOffset()
-		
-		first_presentation_start_node = @doc.xpath("//event[@eventname='SharePresentationEvent']")
-		first_presentation_start = $meeting_end
-		if not first_presentation_start_node.empty?
-			first_presentation_start = first_presentation_start_node[0][:timestamp]
-		end
-		$first_slide_start = ( translateTimestamp(first_presentation_start) / 1000 ).round(1)
-		
-		processChatMessages()
-		
-		processShapesAndClears()
-		
-		processPanAndZooms()
-		
-		processCursorEvents()
-		
-		# Write slides.xml to file
-		File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts $slides_doc.to_xml }
-		# Write shapes.svg to file
-		File.open("#{package_dir}/#{$shapes_svg_filename}", 'w') { |f| f.puts $shapes_svg.to_xml.gsub(%r"\s*\<g.*/\>", "") } #.gsub(%r"\s*\<g.*\>\s*\</g\>", "") }
-		
-		# Write panzooms.xml to file
-		File.open("#{package_dir}/#{$panzooms_xml_filename}", 'w') { |f| f.puts $panzooms_xml.to_xml }
-	
-		# Write panzooms.xml to file
-		File.open("#{package_dir}/#{$cursor_xml_filename}", 'w') { |f| f.puts $cursor_xml.to_xml }
-
-		BigBlueButton.logger.info("Copying files to package dir")
-		FileUtils.cp_r("#{$process_dir}/presentation", package_dir)
-		BigBlueButton.logger.info("Copied files to package dir")
-
-	        BigBlueButton.logger.info("Publishing slides")
-		# Now publish this recording files by copying them into the publish folder.
-		if not FileTest.directory?(publish_dir)
-			FileUtils.mkdir_p publish_dir
-		end
-		FileUtils.cp_r(package_dir, publish_dir) # Copy all the files.
-		BigBlueButton.logger.info("Finished publishing script presentation.rb successfully.")
+    # Create metadata.xml
+    b = Builder::XmlMarkup.new(:indent => 2)
+
+    metaxml = b.recording {
+      b.id($meeting_id)
+      b.state("available")
+      b.published(true)
+      # Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
+      b.start_time(real_start_time)
+      b.end_time(real_end_time)
+      b.playback {
+        b.format("presentation")
+        b.link("#{playback_protocol}://#{playback_host}/playback/presentation/0.9.0/playback.html?meetingId=#{$meeting_id}")
+        b.processing_time("#{processing_time}")
+        b.duration("#{recording_time}")
+      }
+      b.meta {
+        BigBlueButton::Events.get_meeting_metadata("#{$process_dir}/events.xml").each { |k,v| b.method_missing(k,v) }
+      }
+    }
+    metadata_xml = File.new("#{package_dir}/metadata.xml","w")
+    metadata_xml.write(metaxml)
+    metadata_xml.close
+    BigBlueButton.logger.info("Generating xml for slides and chat")
+
+    #Create slides.xml
+
+    # Gathering all the events from the events.xml
+    $slides_events = @doc.xpath("//event[@eventname='GotoSlideEvent' or @eventname='SharePresentationEvent']")
+    $chat_events = @doc.xpath("//event[@eventname='PublicChatEvent']")
+    $shape_events = @doc.xpath("//event[@eventname='AddShapeEvent' or @eventname='ModifyTextEvent']") # for the creation of shapes
+    $panzoom_events = @doc.xpath("//event[@eventname='ResizeAndMoveSlideEvent']") # for the action of panning and/or zooming
+    $cursor_events = @doc.xpath("//event[@eventname='CursorMoveEvent']")
+    $clear_page_events = @doc.xpath("//event[@eventname='ClearPageEvent']") # for clearing the svg image
+    $undo_events = @doc.xpath("//event[@eventname='UndoShapeEvent']") # for undoing shapes.
+    $join_time = $meeting_start.to_f
+    $end_time = $meeting_end.to_f
+
+    calculateRecordEventsOffset()
+
+    first_presentation_start_node = @doc.xpath("//event[@eventname='SharePresentationEvent']")
+    first_presentation_start = $meeting_end
+    if not first_presentation_start_node.empty?
+      first_presentation_start = first_presentation_start_node[0][:timestamp]
+    end
+    $first_slide_start = ( translateTimestamp(first_presentation_start) / 1000 ).round(1)
+
+    processChatMessages()
+
+    processShapesAndClears()
+
+    processPanAndZooms()
+
+    processCursorEvents()
+
+    # Write slides.xml to file
+    File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts $slides_doc.to_xml }
+    # Write shapes.svg to file
+    File.open("#{package_dir}/#{$shapes_svg_filename}", 'w') { |f| f.puts $shapes_svg.to_xml.gsub(%r"\s*\<g.*/\>", "") } #.gsub(%r"\s*\<g.*\>\s*\</g\>", "") }
+
+    # Write panzooms.xml to file
+    File.open("#{package_dir}/#{$panzooms_xml_filename}", 'w') { |f| f.puts $panzooms_xml.to_xml }
+
+    # Write panzooms.xml to file
+    File.open("#{package_dir}/#{$cursor_xml_filename}", 'w') { |f| f.puts $cursor_xml.to_xml }
+
+    BigBlueButton.logger.info("Copying files to package dir")
+    FileUtils.cp_r("#{$process_dir}/presentation", package_dir)
+    BigBlueButton.logger.info("Copied files to package dir")
+
+          BigBlueButton.logger.info("Publishing slides")
+    # Now publish this recording files by copying them into the publish folder.
+    if not FileTest.directory?(publish_dir)
+      FileUtils.mkdir_p publish_dir
+    end
+    FileUtils.cp_r(package_dir, publish_dir) # Copy all the files.
+    BigBlueButton.logger.info("Finished publishing script presentation.rb successfully.")
 
                 BigBlueButton.logger.info("Removing processed files.")
-		FileUtils.rm_r(Dir.glob("#{$process_dir}/*"))
+    FileUtils.rm_r(Dir.glob("#{$process_dir}/*"))
 
-		BigBlueButton.logger.info("Removing published files.")
-		FileUtils.rm_r(Dir.glob("#{target_dir}/*"))
+    BigBlueButton.logger.info("Removing published files.")
+    FileUtils.rm_r(Dir.glob("#{target_dir}/*"))
                 rescue  Exception => e
                         BigBlueButton.logger.error(e.message)
-			e.backtrace.each do |traceline|
-				BigBlueButton.logger.error(traceline)
-			end
-			exit 1
-		end
+      e.backtrace.each do |traceline|
+        BigBlueButton.logger.error(traceline)
+      end
+      exit 1
+    end
         publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.done", "w")
         publish_done.write("Published #{$meeting_id}")
         publish_done.close
 
-	else
-		BigBlueButton.logger.info("#{target_dir} is already there")
-	end
+  else
+    BigBlueButton.logger.info("#{target_dir} is already there")
+  end
 end