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 = ""; } } }