diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties
index 360f6d7177c3bab3bd1034baef2bd6256e72eb9f..467349955e342b2abb36ca7d4581ba8ea9b43441 100755
--- a/bigbluebutton-client/locale/en_US/bbbResources.properties
+++ b/bigbluebutton-client/locale/en_US/bbbResources.properties
@@ -436,6 +436,8 @@ bbb.caption.window.minimizeBtn.accessibilityName = Minimize the Closed Caption W
 bbb.caption.window.maximizeRestoreBtn.accessibilityName = Maximize the Closed Caption Window
 bbb.caption.transcript.noowner = None
 bbb.caption.transcript.youowner = You
+bbb.caption.transcript.pastewarning.title = Caption Paste Warning
+bbb.caption.transcript.pastewarning.text = Cannot paste text longer than {0} characters. You pasted {1} characters.
 bbb.caption.option.label = Options
 bbb.caption.option.language = Language:
 bbb.caption.option.language.tooltip = Select Caption Language
diff --git a/bigbluebutton-client/resources/config.xml.template b/bigbluebutton-client/resources/config.xml.template
index 5504336f5ec78410b3865c06bec0748c3c251019..093d326a93d5c438ed29845d70313b479a8bd075 100755
--- a/bigbluebutton-client/resources/config.xml.template
+++ b/bigbluebutton-client/resources/config.xml.template
@@ -118,6 +118,7 @@
 		<module name="CaptionModule" url="http://HOST/client/CaptionModule.swf?v=VERSION" 
 			uri="rtmp://HOST/bigbluebutton" 
 			dependsOn="UsersModule"
+			maxPasteLength="1024"
 			baseTabIndex="701"
 		/>
 
diff --git a/bigbluebutton-client/resources/prod/layout.xml b/bigbluebutton-client/resources/prod/layout.xml
index f2079f0e11641d0dec9abfde185a7c4cb6adf2e4..5ef4afede5bd0eb81a15701245c8fe52b0375b06 100755
--- a/bigbluebutton-client/resources/prod/layout.xml
+++ b/bigbluebutton-client/resources/prod/layout.xml
@@ -25,6 +25,7 @@
     <window name="ChatWindow" width="0.303125" height="0.9955703211517165" x="0.3229166666666667" y="0.9656699889258029" order="4" hidden="true" />
     <window name="PresentationWindow" minimized="true" order="1" hidden="true" />
     <window name="UsersWindow" minimized="true" hidden="true" order="2"/>
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>
   <layout name="bbb.layout.name.webcamsfocus">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -33,6 +34,7 @@
     <window name="ChatWindow" width="0.3393632416787265" height="0.5305851063829787" x="0.658465991316932" y="0" />
     <window name="UsersWindow" hidden="true" />
     <window name="PresentationWindow" width="0.34008683068017365" height="0.4601063829787234" x="0.658465991316932" y="0.535904255319149" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>
   <layout name="bbb.layout.name.presentfocus">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -41,6 +43,7 @@
     <window name="VideoDock" width="0.2923611111111111" height="0.4640957446808511" x="0.7048611111111112" y="0.535904255319149" />
     <window name="PresentationWindow" width="0.7027777777777777" height="0.9986702127659575" x="0" y="0" />
     <window name="ChatWindow" width="0.2923611111111111" height="0.5305851063829787" x="0.7048611111111112" y="0" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>
   <layout name="bbb.layout.name.lectureassistant">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -49,6 +52,7 @@
     <window name="UsersWindow" width="0.22152777777777777" height="0.9958677685950413" x="0" y="0" minWidth="280" />
     <window name="PresentationWindow" width="0.3104166666666667" height="0.5537190082644629" x="0.6895833333333333" y="0" />
     <window name="VideoDock" width="0.30972222222222223" height="0.4357198347107438" x="0.6902777777777778" y="0.558870523415978" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>	
   <layout name="bbb.layout.name.lecture">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -57,6 +61,7 @@
     <window name="VideoDock" width="0.2923611111111111" height="0.4640957446808511" x="0.7048611111111112" y="0.535904255319149" />
     <window name="PresentationWindow" width="0.7027777777777777" height="0.9986702127659575" x="0" y="0" />
     <window name="ChatWindow" width="0.2923611111111111" height="0.5305851063829787" x="0.7048611111111112" y="0" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>	
   <layout name="bbb.layout.name.lecture" role="presenter">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -65,6 +70,7 @@
     <window name="UsersWindow" hidden="true" />
     <window name="PresentationWindow" maximized="true" />
     <window name="VideoDock" hidden="true" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>	
   <layout name="bbb.layout.name.lecture" role="moderator">
     <window name="NotesWindow" hidden="true" width="0.7" height="1" x="0" y="0" draggable="false" resizable="false"/>
@@ -73,6 +79,7 @@
     <window name="UsersWindow" width="0.22152777777777777" height="0.9944903581267218" x="0" y="0" minWidth="280" />
     <window name="PresentationWindow" width="0.3104166666666667" height="0.5537190082644629" x="0.6895833333333333" y="0" />
     <window name="VideoDock" width="0.30972222222222223" height="0.4256198347107438" x="0.6902777777777778" y="0.568870523415978" />
+    <window name="CaptionWindow" hidden="true" width="0.513" height="0.308" x="0.180" y="0.692" />
   </layout>	
 <!--
   <layout name="Users">
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml
index 941642b056abc095eb2637e585f728f73e039dfc..ecaddd20eb7d3f9a4f25908f2e978ef0bf7d7705 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/ShortcutHelpWindow.mxml
@@ -85,6 +85,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 			private function init():void {
 				modifier = ExternalInterface.call("determineModifier");
 				globalModifier = ExternalInterface.call("determineGlobalModifier");
+				
+				ShortcutOptions.initialize();
+				
 				populateModules();
 			}
 			
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/CaptionOptions.as b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/CaptionOptions.as
index c6a98343db5da5b2d6e392bcfa442782b1150e5f..76f50a9bfecae7fb60cb2060c5cea5115978c01a 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/CaptionOptions.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/CaptionOptions.as
@@ -3,12 +3,19 @@ package org.bigbluebutton.modules.caption.model {
 	
 	public class CaptionOptions {
 		
+		[Bindable] 
+		public var maxPasteLength:int = 1024;
+		
 		[Bindable]
 		public var baseTabIndex:int = 701;
 		
 		public function CaptionOptions() {
 			var cxml:XML = BBB.getConfigForModule("CaptionModule");
 			if (cxml != null) {
+				if (cxml.@maxPasteLength != undefined) {
+					maxPasteLength = cxml.@maxPasteLength;
+				}
+				
 				if (cxml.@baseTabIndex != undefined) {
 					baseTabIndex = cxml.@baseTabIndex;
 				}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/Transcripts.as b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/Transcripts.as
index ff64a8f911c553ba4fdcc233afadd71f2db7c66b..9b57b2b1bc264caeaf46cc020d3dfbf03e862adb 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/Transcripts.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/model/Transcripts.as
@@ -61,7 +61,9 @@ package org.bigbluebutton.modules.caption.model {
 		}
 		
 		public function editCaptionHistory(locale:String, startIndex:int, endIndex:int, text:String):void {
-			findLocale(locale).editHistory(startIndex, endIndex, text);
+			if (_historyInited) { // ignore updates until after history has been loaded
+				findLocale(locale).editHistory(startIndex, endIndex, text);
+			}
 		}
 		
 		public function findLocale(locale:String):Transcript {
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/CaptionWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/CaptionWindow.mxml
index 1ba87c799e1d34326664ccccf29617a16493af43..cd2ce68273374c35bb30b4e6f38156fe4297aecd 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/CaptionWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/CaptionWindow.mxml
@@ -76,7 +76,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 				optionsTab.setChangeCallback(optionChange);
 				captionTabs.addChild(optionsTab);
 				
-				textTab = new TextTab(captionOptions.baseTabIndex+5);
+				textTab = new TextTab(captionOptions.baseTabIndex+5, captionOptions);
 			}
 			
 			private function onCreationComplete():void {
@@ -162,7 +162,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 			private function onTranscriptOwnerIDChange(o:Object):void {
 				var ownerId:String = o as String;
 				
-				optionsTab.transcriptOwnerIDChange(ownerId);
 				textTab.transcriptOwnerIDChange(ownerId);
 				
 				setTextTabLabel(ownerId);
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/OptionsTab.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/OptionsTab.mxml
index 0cad6a84fb4d746c469912d180aa83c6960d1e50..7670abd055e629ef37e98fd5947ce34259564c8a 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/OptionsTab.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/OptionsTab.mxml
@@ -53,11 +53,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 				currentTranscript = t;
 			}
 			
-			public function transcriptOwnerIDChange(ownerID:String):void {
-				claimOwnershipButton.visible = UserManager.getInstance().getConference().amIModerator() && 
-												ownerID != UserManager.getInstance().getConference().getMyUserId();
-			}
-			
 			private function onLocaleComboChange():void {
 				if (transcriptsCollection && localeCombo.selectedIndex != -1) {
 					if (changeCallback != null) changeCallback(OptionENUM.LOCALE, localeCombo.selectedItem.locale);
@@ -80,24 +75,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 				if (changeCallback != null) changeCallback(OptionENUM.TEXTCOLOR, textColorPicker.selectedColor);
 			}
 			
-			private function onClaimButtonClick():void {
-				claimTranscript(currentTranscript.locale, true);
-				localeCombo.setFocus();
-			}
-			
-			private function claimTranscript(locale:String, claim:Boolean):void {
-				var updateCaptionOwnerEvent:SendUpdateCaptionOwnerEvent = new SendUpdateCaptionOwnerEvent(SendUpdateCaptionOwnerEvent.SEND_UPDATE_CAPTION_OWNER_EVENT);
-				updateCaptionOwnerEvent.locale = locale;
-				updateCaptionOwnerEvent.claim = claim;
-				
-				var dispatcher:Dispatcher = new Dispatcher();
-				dispatcher.dispatchEvent(updateCaptionOwnerEvent);
-			}
-			
 		]]>
 	</mx:Script>
 	
-	<common:TabIndexer id="tabIndexer" tabIndices="{[localeCombo, claimOwnershipButton, fontFamilyCombo, fontSizeCombo, backgroundColorPicker, textColorPicker]}"/>
+	<common:TabIndexer id="tabIndexer" tabIndices="{[localeCombo, fontFamilyCombo, fontSizeCombo, backgroundColorPicker, textColorPicker]}"/>
 	
 	<mx:VBox>
 		<mx:HBox>
@@ -111,12 +92,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 						 change="onLocaleComboChange()"
 						 toolTip="{ResourceUtil.getInstance().getString('bbb.caption.option.language.tooltip')}"/>
 		</mx:HBox>
-		<mx:HBox>
-			<mx:Button id="claimOwnershipButton" 
-					   label="{ResourceUtil.getInstance().getString('bbb.caption.option.takeowner')}"
-					   toolTip="{ResourceUtil.getInstance().getString('bbb.caption.option.takeowner.tooltip')}"
-					   height="22" visible="false" click="onClaimButtonClick()" />
-		</mx:HBox>
 	</mx:VBox>
 	<mx:VBox>
 		<mx:HBox>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/TextTab.as b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/TextTab.as
index 2f720fff6eaae49f6958391fed4a0ad4d01f764a..c8a003ebe8eee9d81ba2b5864621430a3149bc40 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/TextTab.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/caption/views/TextTab.as
@@ -22,20 +22,31 @@ package org.bigbluebutton.modules.caption.views {
 	
 	import flash.events.Event;
 	import flash.events.KeyboardEvent;
+	import flash.events.MouseEvent;
 	import flash.events.TextEvent;
+	import flash.events.TimerEvent;
 	import flash.text.TextFieldType;
 	import flash.ui.Keyboard;
+	import flash.utils.Timer;
 	
 	import mx.binding.utils.BindingUtils;
 	import mx.binding.utils.ChangeWatcher;
-	import mx.containers.Box;
+	import mx.containers.VBox;
+	import mx.controls.Alert;
+	import mx.controls.Button;
 	import mx.events.FlexEvent;
 	
 	import org.bigbluebutton.core.managers.UserManager;
 	import org.bigbluebutton.modules.caption.events.SendEditCaptionHistoryEvent;
+	import org.bigbluebutton.modules.caption.events.SendUpdateCaptionOwnerEvent;
+	import org.bigbluebutton.modules.caption.model.CaptionOptions;
 	import org.bigbluebutton.modules.caption.model.Transcript;
+	import org.bigbluebutton.util.i18n.ResourceUtil;
+	import org.osmf.events.TimeEvent;
 
-	public class TextTab extends Box {
+	public class TextTab extends VBox {
+		
+		private var _captionOptions:CaptionOptions;
 		
 		[Bindable]
 		private var currentTranscript:Transcript;
@@ -51,10 +62,15 @@ package org.bigbluebutton.modules.caption.views {
 		
 		private var inputArea:TextArea2;
 		private var outputArea:TextArea2;
+		private var claimButton:Button;
 		
-		public function TextTab(startIndex:int) {
+		public function TextTab(startIndex:int, captionOptions:CaptionOptions) {
 			super();
 			
+			_captionOptions = captionOptions;
+			
+			setStyle("horizontalAlign", "center");
+			
 			inputArea = new TextArea2();
 			inputArea.percentWidth = 100;
 			inputArea.percentHeight = 100;
@@ -70,6 +86,19 @@ package org.bigbluebutton.modules.caption.views {
 			outputArea.tabIndex = startIndex+1;
 			addChild(outputArea);
 			
+			claimButton = new Button();
+			claimButton.label = ResourceUtil.getInstance().getString('bbb.caption.option.takeowner');
+			claimButton.toolTip = ResourceUtil.getInstance().getString('bbb.caption.option.takeowner.tooltip');
+			claimButton.height = 22; 
+			claimButton.visible = false;
+			claimButton.includeInLayout = false;
+			claimButton.tabIndex = startIndex+1;
+			claimButton.addEventListener(MouseEvent.CLICK, onClaimButtonClick);
+			addChild(claimButton);
+			
+			_sendTimer = new Timer(TIME_TO_SEND, 1);
+			_sendTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onSendTimerComplete);
+			
 			addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
 		}
 		
@@ -94,23 +123,30 @@ package org.bigbluebutton.modules.caption.views {
 		}
 		
 		public function transcriptOwnerIDChange(ownerID:String):void {
-			if (ownerID == "") {
-				//unclaimed text
-				inputArea.visible = inputArea.includeInLayout = false;
-				outputArea.visible = outputArea.includeInLayout = true;
-				inputArea.getInternalTextField().type = TextFieldType.DYNAMIC;
-			} else if (ownerID == UserManager.getInstance().getConference().getMyUserId()) {
+			if (ownerID == UserManager.getInstance().getConference().getMyUserId()) {
+				claimButton.visible = claimButton.includeInLayout = false;
+				
 				//release text
 				inputArea.visible = inputArea.includeInLayout = true;
 				outputArea.visible = outputArea.includeInLayout = false;
 				inputArea.getInternalTextField().type = TextFieldType.INPUT;
 				inputArea.text = currentTranscript.transcript;
 			} else {
-				//claimed by other
-				inputArea.visible = inputArea.includeInLayout = false;
-				outputArea.visible = outputArea.includeInLayout = true;
-				inputArea.getInternalTextField().type = TextFieldType.DYNAMIC;
+				claimButton.visible = claimButton.includeInLayout = UserManager.getInstance().getConference().amIModerator();
+				
+				if (ownerID == "") {
+					//unclaimed text
+					inputArea.visible = inputArea.includeInLayout = false;
+					outputArea.visible = outputArea.includeInLayout = true;
+					inputArea.getInternalTextField().type = TextFieldType.DYNAMIC;
+				} else {
+					//claimed by other
+					inputArea.visible = inputArea.includeInLayout = false;
+					outputArea.visible = outputArea.includeInLayout = true;
+					inputArea.getInternalTextField().type = TextFieldType.DYNAMIC;
+				}
 			}
+			
 		}
 		
 		public function setFontSize(fontSize:int):void {
@@ -133,8 +169,27 @@ package org.bigbluebutton.modules.caption.views {
 			outputArea.setStyle("backgroundColor", color);
 		}
 		
+		private function onClaimButtonClick(e:MouseEvent):void {
+			claimTranscript(currentTranscript.locale, true);
+		}
+		
+		private function claimTranscript(locale:String, claim:Boolean):void {
+			var updateCaptionOwnerEvent:SendUpdateCaptionOwnerEvent = new SendUpdateCaptionOwnerEvent(SendUpdateCaptionOwnerEvent.SEND_UPDATE_CAPTION_OWNER_EVENT);
+			updateCaptionOwnerEvent.locale = locale;
+			updateCaptionOwnerEvent.claim = claim;
+			
+			var dispatcher:Dispatcher = new Dispatcher();
+			dispatcher.dispatchEvent(updateCaptionOwnerEvent);
+		}
+		
 		private function onTranscriptTextInput(e:TextEvent):void {
-			trace("Text entered: " + e.text + ", carat begin:" + inputArea.selectionBeginIndex + ", end: " + inputArea.selectionEndIndex);
+			//trace("Text entered: " + e.text + ", carat begin:" + inputArea.selectionBeginIndex + ", end: " + inputArea.selectionEndIndex);
+			
+			if (e.text.length > _captionOptions.maxPasteLength) {
+				e.preventDefault();
+				Alert.show(ResourceUtil.getInstance().getString("bbb.caption.transcript.pastewarning.text", [_captionOptions.maxPasteLength, e.text.length]), ResourceUtil.getInstance().getString("bbb.caption.transcript.pastewarning.title"), Alert.OK);
+				return;
+			}
 			
 			// There is no surefire way to detect whether the internal TextField is in overwrite mode or not. We need to 
 			// delay sending the message until after the text changes and then check length. This extra check is only 
@@ -149,7 +204,7 @@ package org.bigbluebutton.modules.caption.views {
 		}
 		
 		private function onTranscriptTextChange(e:Event):void {
-			trace("transcript change: " + inputArea.text);
+			//trace("transcript change: " + inputArea.text);
 			
 			if (_checkForOverwrite) {
 				_checkForOverwrite = false;
@@ -222,15 +277,67 @@ package org.bigbluebutton.modules.caption.views {
 			}
 		}
 		
+		private const LEN_TO_SEND:int = 7;
+		private const REP_TO_SEND:int = 3;
+		private const TIME_TO_SEND:int = 1000;
+		
+		private var _startIndex:int = -1;
+		private var _endIndex:int = -1;
+		private var _accText:String = "";
+		
+		private var _sendTimer:Timer;
+		
 		private function respondToTextChange(t:String, si:int, ei:int):void {
+			if (_startIndex == -1) {
+				_startIndex = si;
+				_endIndex = ei;
+				_accText = t;
+			} else if (ei < _startIndex || si > _startIndex + _accText.length) {
+				// edited away from current spot
+				sendTextToServer();
+			
+				_startIndex = si;
+				_endIndex = ei;
+				_accText = t;
+			} else {
+				var tempText:String = _accText;
+				var subStart:int = si - _startIndex;
+				
+				tempText = _accText.substr(0, Math.max(subStart, 0)) + t + _accText.substr(subStart+(ei-si));
+				
+				if (ei - _startIndex > _accText.length) _endIndex += ei - _accText.length - _startIndex;
+				if (si < _startIndex) _startIndex = si;
+				_accText = tempText;
+			}
+			
+			// start/restart the timer
+			if (_sendTimer.running) _sendTimer.stop();
+			_sendTimer.start();
+			
+			// check length
+			if (_accText.length >= LEN_TO_SEND || _endIndex - _startIndex >= REP_TO_SEND) {
+				sendTextToServer();
+			}
+		}
+		
+		private function onSendTimerComplete(e:TimerEvent):void {
+			sendTextToServer();
+		}
+		
+		private function sendTextToServer():void {
 			var editHistoryEvent:SendEditCaptionHistoryEvent = new SendEditCaptionHistoryEvent(SendEditCaptionHistoryEvent.SEND_EDIT_CAPTION_HISTORY);
 			editHistoryEvent.locale = currentTranscript.locale;
-			editHistoryEvent.startIndex = si;
-			editHistoryEvent.endIndex = ei;
-			editHistoryEvent.text = t;
+			editHistoryEvent.startIndex = _startIndex;
+			editHistoryEvent.endIndex = _endIndex;
+			editHistoryEvent.text = _accText;
 			
 			var dispatcher:Dispatcher = new Dispatcher();
 			dispatcher.dispatchEvent(editHistoryEvent);
+			
+			// reset variables after sending
+			_startIndex = -1;
+			_endIndex = -1;
+			_accText = "";
 		}
 	}
 }