Merge branch 'development' into as3-logging
Conflicts: bigbluebutton-client/src/BigBlueButtonMainContainer.mxml bigbluebutton-client/src/org/bigbluebutton/core/services/UsersService.as bigbluebutton-client/src/org/bigbluebutton/main/maps/ApplicationEventMap.mxml bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml bigbluebutton-client/src/org/bigbluebutton/main/views/WebRTCEchoTest.mxml bigbluebutton-client/src/org/bigbluebutton/modules/chat/services/MessageReceiver.as bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/PublishWindowManager.as bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/services/red5/Connection.as bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopPublishWindow.mxml bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopViewWindow.mxml bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/WebRTCCallManager.as bigbluebutton-client/src/org/bigbluebutton/modules/polling/service/PollDataProcessor.as bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserGraphicHolder.mxml bigbluebutton-client/src/org/bigbluebutton/modules/whiteboard/business/shapes/PollResultObject.as bigbluebutton-client/src/org/bigbluebutton/modules/whiteboard/models/WhiteboardModel.as
This commit is contained in:
commit
83666a7caa
@ -18,8 +18,8 @@ object PollFactory {
|
||||
|
||||
private def processYesNoPollType(qType: String): Question = {
|
||||
val answers = new Array[Answer](2)
|
||||
answers(0) = new Answer(0, "N", Some("No"))
|
||||
answers(1) = new Answer(1, "Y", Some("Yes"))
|
||||
answers(0) = new Answer(0, "No", Some("No"))
|
||||
answers(1) = new Answer(1, "Yes", Some("Yes"))
|
||||
|
||||
new Question(0, PollType.YesNoPollType, false, None, answers)
|
||||
}
|
||||
@ -27,8 +27,8 @@ object PollFactory {
|
||||
private def processTrueFalsePollType(qType: String): Question = {
|
||||
val answers = new Array[Answer](2)
|
||||
|
||||
answers(0) = new Answer(0, "F", Some("False"))
|
||||
answers(1) = new Answer(1, "T", Some("True"))
|
||||
answers(0) = new Answer(0, "False", Some("False"))
|
||||
answers(1) = new Answer(1, "True", Some("True"))
|
||||
|
||||
new Question(0, PollType.TrueFalsePollType, false, None, answers)
|
||||
}
|
||||
|
@ -68,6 +68,9 @@ trait PollApp {
|
||||
val shape = new scala.collection.mutable.HashMap[String, Object]()
|
||||
shape += "num_respondents" -> new Integer(result.numRespondents)
|
||||
shape += "num_responders" -> new Integer(result.numResponders)
|
||||
shape += "type" -> "poll_result"
|
||||
shape += "id" -> result.id
|
||||
shape += "status" -> "DRAW_END"
|
||||
|
||||
val answers = new ArrayBuffer[java.util.HashMap[String, Object]];
|
||||
result.answers.foreach(ans => {
|
||||
@ -81,12 +84,13 @@ trait PollApp {
|
||||
val gson = new Gson()
|
||||
shape += "result" -> gson.toJson(answers.toArray)
|
||||
|
||||
// Hardcode poll result display location for now.
|
||||
// Hardcode poll result display location for now to display result
|
||||
// in bottom-right corner.
|
||||
val display = new ArrayList[Double]()
|
||||
display.add(21.845575)
|
||||
display.add(23.145401)
|
||||
display.add(46.516006)
|
||||
display.add(61.42433)
|
||||
display.add(66.0)
|
||||
display.add(60.0)
|
||||
display.add(34.0)
|
||||
display.add(40.0)
|
||||
|
||||
shape += "points" -> display
|
||||
shape.toMap
|
||||
|
@ -228,6 +228,7 @@ trait UsersApp {
|
||||
}
|
||||
|
||||
usersModel.removeUser(msg.userId)
|
||||
usersModel.removeRegUser(msg.userId)
|
||||
|
||||
log.info("Ejecting user from meeting: mid=[" + mProps.meetingID + "]uid=[" + msg.userId + "]")
|
||||
outGW.send(new UserEjectedFromMeeting(mProps.meetingID, mProps.recorded, msg.userId, msg.ejectedBy))
|
||||
@ -249,11 +250,14 @@ trait UsersApp {
|
||||
|
||||
def handleUserunshareWebcam(msg: UserUnshareWebcam) {
|
||||
usersModel.getUser(msg.userId) foreach { user =>
|
||||
val streams = user.webcamStreams - msg.stream
|
||||
val uvo = user.copy(hasStream = (!streams.isEmpty), webcamStreams = streams)
|
||||
usersModel.addUser(uvo)
|
||||
log.info("User unshared webcam: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "] unsharedStream=[" + msg.stream + "] streams=[" + streams + "]")
|
||||
outGW.send(new UserUnsharedWebcam(mProps.meetingID, mProps.recorded, uvo.userID, msg.stream))
|
||||
val streamName = user.webcamStreams find (w => w == msg.stream) foreach { streamName =>
|
||||
val streams = user.webcamStreams - streamName
|
||||
val uvo = user.copy(hasStream = (!streams.isEmpty), webcamStreams = streams)
|
||||
usersModel.addUser(uvo)
|
||||
log.info("User unshared webcam: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "] unsharedStream=[" + msg.stream + "] streams=[" + streams + "]")
|
||||
outGW.send(new UserUnsharedWebcam(mProps.meetingID, mProps.recorded, uvo.userID, msg.stream))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,7 +274,20 @@ trait UsersApp {
|
||||
def handleUserJoin(msg: UserJoining): Unit = {
|
||||
val regUser = usersModel.getRegisteredUserWithToken(msg.authToken)
|
||||
regUser foreach { ru =>
|
||||
val vu = new VoiceUser(msg.userID, msg.userID, ru.name, ru.name, false, false, false, false)
|
||||
// if there was a phoneUser with the same userID, reuse the VoiceUser value object
|
||||
val vu = usersModel.getUser(msg.userID) match {
|
||||
case Some(u) => {
|
||||
if (u.voiceUser.joined) {
|
||||
u.voiceUser.copy()
|
||||
} else {
|
||||
new VoiceUser(msg.userID, msg.userID, ru.name, ru.name, false, false, false, false)
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
new VoiceUser(msg.userID, msg.userID, ru.name, ru.name, false, false, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
val uvo = new UserVO(msg.userID, ru.externId, ru.name,
|
||||
ru.role, raiseHand = false, presenter = false,
|
||||
hasStream = false, locked = getInitialLockStatus(ru.role),
|
||||
@ -286,7 +303,7 @@ trait UsersApp {
|
||||
outGW.send(new MeetingState(mProps.meetingID, mProps.recorded, uvo.userID, meetingModel.getPermissions(), meetingModel.isMeetingMuted()))
|
||||
|
||||
// Become presenter if the only moderator
|
||||
if (usersModel.numModerators == 1) {
|
||||
if ((usersModel.numModerators == 1) || (usersModel.noPresenter())) {
|
||||
if (ru.role == Role.MODERATOR) {
|
||||
assignNewPresenter(msg.userID, ru.name, msg.userID)
|
||||
}
|
||||
@ -309,14 +326,18 @@ trait UsersApp {
|
||||
|
||||
/* The current presenter has left the meeting. Find a moderator and make
|
||||
* him presenter. This way, if there is a moderator in the meeting, there
|
||||
* will always be a presenter.
|
||||
* will always be a presenter.
|
||||
*/
|
||||
val moderator = usersModel.findAModerator()
|
||||
moderator.foreach { mod =>
|
||||
log.info("Presenter left meeting: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "]. Making user=[" + mod.userID + "] presenter.")
|
||||
assignNewPresenter(mod.userID, mod.name, mod.userID)
|
||||
}
|
||||
|
||||
}
|
||||
// add VoiceUser again to the list as a phone user since we still didn't get the event from FreeSWITCH
|
||||
val vu = u.voiceUser
|
||||
if (vu.joined) {
|
||||
this.context.self ! (new UserJoinedVoiceConfMessage(mProps.voiceBridge, vu.userId, msg.userID, vu.callerName, vu.callerNum, vu.muted, vu.talking));
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,12 +356,16 @@ trait UsersApp {
|
||||
log.info("Voice user=[" + msg.voiceUserId + "] is already in conf=[" + mProps.voiceBridge + "]. Must be duplicate message.")
|
||||
}
|
||||
case None => {
|
||||
// No current web user. This means that the user called in through
|
||||
// the phone. We need to generate a new user as we are not able
|
||||
// to match with a web user.
|
||||
val webUserId = usersModel.generateWebUserId
|
||||
val webUserId = if (msg.userId != msg.callerIdName) {
|
||||
msg.userId
|
||||
} else {
|
||||
// No current web user. This means that the user called in through
|
||||
// the phone. We need to generate a new user as we are not able
|
||||
// to match with a web user.
|
||||
usersModel.generateWebUserId
|
||||
}
|
||||
val vu = new VoiceUser(msg.voiceUserId, webUserId, msg.callerIdName, msg.callerIdNum,
|
||||
true, false, false, false)
|
||||
true, false, msg.muted, msg.talking)
|
||||
|
||||
val sessionId = "PHONE-" + webUserId;
|
||||
|
||||
|
@ -96,6 +96,10 @@ class UsersModel {
|
||||
uservos.values find (u => u.role == MODERATOR)
|
||||
}
|
||||
|
||||
def noPresenter(): Boolean = {
|
||||
!getCurrentPresenter().isDefined
|
||||
}
|
||||
|
||||
def getCurrentPresenter(): Option[UserVO] = {
|
||||
uservos.values find (u => u.presenter == true)
|
||||
}
|
||||
@ -127,4 +131,17 @@ class UsersModel {
|
||||
def getViewers(): Array[UserVO] = {
|
||||
uservos.values filter (u => u.role == VIEWER) toArray
|
||||
}
|
||||
|
||||
def getRegisteredUserWithUserID(userID: String): Option[RegisteredUser] = {
|
||||
regUsers.values find (ru => userID contains ru.id)
|
||||
}
|
||||
|
||||
def removeRegUser(userID: String) {
|
||||
getRegisteredUserWithUserID(userID) match {
|
||||
case Some(ru) => {
|
||||
regUsers -= ru.authToken
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
}
|
@ -42,6 +42,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
||||
private ConnectionInvokerService connInvokerService;
|
||||
private MessagePublisher red5InGW;
|
||||
|
||||
private final UserConnectionMapper userConnections = new UserConnectionMapper();
|
||||
|
||||
private final String APP = "BBB";
|
||||
private final String CONN = "RED5-";
|
||||
|
||||
@ -166,6 +168,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
||||
|
||||
log.info("User joining bbb-apps: data={}", logStr);
|
||||
|
||||
userConnections.addUserConnection(userId, connId);
|
||||
|
||||
return super.roomConnect(connection, params);
|
||||
|
||||
}
|
||||
@ -214,10 +218,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
||||
Gson gson = new Gson();
|
||||
String logStr = gson.toJson(logData);
|
||||
|
||||
log.info("User leaving bbb-apps: data={}", logStr);
|
||||
|
||||
red5InGW.userLeft(bbbSession.getRoom(), getBbbSession().getInternalUserID(), sessionId);
|
||||
|
||||
boolean removeUser = userConnections.userDisconnected(userId, connId);
|
||||
|
||||
if (removeUser) {
|
||||
log.info("User leaving bbb-apps: data={}", logStr);
|
||||
red5InGW.userLeft(bbbSession.getRoom(), getBbbSession().getInternalUserID(), sessionId);
|
||||
} else {
|
||||
log.info("User not leaving bbb-apps but just disconnected: data={}", logStr);
|
||||
}
|
||||
|
||||
super.roomDisconnect(conn);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
package org.bigbluebutton.red5;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* This class maintains the connections mapping of a user.
|
||||
* This tracks the connections for a user to manage auto-reconnects.
|
||||
* @author ralam
|
||||
*
|
||||
*/
|
||||
public class UserConnectionMapper {
|
||||
|
||||
private ConcurrentMap<String, UserConnection> users = new ConcurrentHashMap<String, UserConnection>(8, 0.9f, 1);;
|
||||
|
||||
/**
|
||||
* Adds a connection for a user.
|
||||
* @param userId
|
||||
* @param connId
|
||||
*/
|
||||
public synchronized void addUserConnection(String userId, String connId) {
|
||||
if (users.containsKey(userId)) {
|
||||
UserConnection user = users.get(userId);
|
||||
user.add(connId);
|
||||
} else {
|
||||
UserConnection user = new UserConnection();
|
||||
user.add(connId);
|
||||
users.put(userId, user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed a connection for a user. Returns true if the user doesn't have any
|
||||
* connection left and thus can be removed.
|
||||
* @param userId
|
||||
* @param connId
|
||||
* @return boolean - no more connections
|
||||
*/
|
||||
public synchronized boolean userDisconnected(String userId, String connId) {
|
||||
if (users.containsKey(userId)) {
|
||||
UserConnection user = users.get(userId);
|
||||
user.remove(connId);
|
||||
if (user.isEmpty()) {
|
||||
users.remove(userId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class UserConnection {
|
||||
private final Set<String> connections = new HashSet<String>();
|
||||
|
||||
public void add(String connId) {
|
||||
connections.add(connId);
|
||||
}
|
||||
|
||||
public void remove(String connId) {
|
||||
connections.remove(connId);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return connections.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
@ -100,6 +100,12 @@ ToolTip {
|
||||
fontFamily: Arial;
|
||||
}
|
||||
|
||||
.pollResondersLabelStyle {
|
||||
color: #444444;
|
||||
fontFamily: Arial;
|
||||
fontSize: 12;
|
||||
}
|
||||
|
||||
Button, .logoutButtonStyle, .chatSendButtonStyle, .helpLinkButtonStyle, .cameraDisplaySettingsWindowProfileComboStyle, .cameraDisplaySettingsWindowCameraSelector, .languageSelectorStyle, .testJavaLinkButtonStyle, .recordButtonStyleNormal, .recordButtonStyleStart, .recordButtonStyleStop, .micSettingsWindowHelpButtonStyle {
|
||||
textIndent: 0;
|
||||
paddingLeft: 10;
|
||||
@ -408,7 +414,7 @@ DataGrid {
|
||||
icon: Embed('assets/images/webcam-private-chat.png');
|
||||
}
|
||||
|
||||
.presentationFileUploadWindowStyle {
|
||||
TitleWindow {
|
||||
borderColor: #b9babc;
|
||||
borderAlpha: 1;
|
||||
borderThicknessLeft: 10;
|
||||
@ -419,11 +425,8 @@ DataGrid {
|
||||
cornerRadius: 5;
|
||||
headerHeight: 20;
|
||||
backgroundAlpha: 1;
|
||||
headerColors: #b9babc, #b9babc;
|
||||
footerColors: #b9babc, #b9babc;
|
||||
backgroundColor: #EFEFEF;
|
||||
dropShadowEnabled: true;
|
||||
titleStyleName: "presentationFileUploadWindowTitleStyle";
|
||||
}
|
||||
|
||||
.presentationFileUploadWindowTitleStyle, .presentationUploadTitleStyle {
|
||||
@ -511,42 +514,6 @@ DataGrid {
|
||||
imageSource: Embed(source='assets/images/chromePluginBlocked.png');
|
||||
}
|
||||
|
||||
.cameraDisplaySettingsWindowStyle {
|
||||
borderColor: #b9babc;
|
||||
borderAlpha: 1;
|
||||
borderThicknessLeft: 10;
|
||||
borderThicknessTop: 0;
|
||||
borderThicknessBottom: 10;
|
||||
borderThicknessRight: 10;
|
||||
roundedBottomCorners: true;
|
||||
cornerRadius: 5;
|
||||
headerHeight: 20;
|
||||
backgroundAlpha: 1;
|
||||
headerColors: #b9babc, #b9babc;
|
||||
footerColors: #b9babc, #b9babc;
|
||||
backgroundColor: #EFEFEF;
|
||||
dropShadowEnabled: true;
|
||||
titleStyleName: "webcamSettingsWindowTitleStyle";
|
||||
}
|
||||
|
||||
.micSettingsWindowStyle {
|
||||
borderColor: #b9babc;
|
||||
borderAlpha: 1;
|
||||
borderThicknessLeft: 10;
|
||||
borderThicknessTop: 0;
|
||||
borderThicknessBottom: 10;
|
||||
borderThicknessRight: 10;
|
||||
roundedBottomCorners: true;
|
||||
cornerRadius: 5;
|
||||
headerHeight: 20;
|
||||
backgroundAlpha: 1;
|
||||
headerColors: #b9babc, #b9babc;
|
||||
footerColors: #b9babc, #b9babc;
|
||||
backgroundColor: #EFEFEF;
|
||||
dropShadowEnabled: true;
|
||||
titleStyleName: "micSettingsWindowTitleStyle";
|
||||
}
|
||||
|
||||
.webcamSettingsWindowTitleStyle, .micSettingsWindowTitleStyle {
|
||||
fontFamily: Arial;
|
||||
fontSize: 20;
|
||||
@ -866,24 +833,6 @@ Alert {
|
||||
fontWeight: bold;
|
||||
}
|
||||
|
||||
.lockSettingsWindowStyle {
|
||||
borderColor: #b9babc;
|
||||
borderAlpha: 1;
|
||||
borderThicknessLeft: 10;
|
||||
borderThicknessTop: 0;
|
||||
borderThicknessBottom: 10;
|
||||
borderThicknessRight: 10;
|
||||
roundedBottomCorners: true;
|
||||
cornerRadius: 5;
|
||||
headerHeight: 20;
|
||||
backgroundAlpha: 1;
|
||||
headerColors: #b9babc, #b9babc;
|
||||
footerColors: #b9babc, #b9babc;
|
||||
backgroundColor: #EFEFEF;
|
||||
dropShadowEnabled: true;
|
||||
titleStyleName: "micSettingsWindowTitleStyle";
|
||||
}
|
||||
|
||||
.lockSettingsWindowTitleStyle {
|
||||
fontFamily: Arial;
|
||||
fontSize: 20;
|
||||
@ -906,6 +855,7 @@ Alert {
|
||||
successImage: Embed(source='assets/images/status_success.png');
|
||||
warningImage: Embed(source='assets/images/status_warning.png');
|
||||
failImage: Embed(source='assets/images/status_fail.png');
|
||||
refreshImage: Embed(source='assets/images/status_refresh.png');
|
||||
}
|
||||
|
||||
.warningButtonStyle {
|
||||
@ -922,3 +872,8 @@ Alert {
|
||||
fontSize: 12;
|
||||
paddingTop: 0;
|
||||
}
|
||||
|
||||
.statusTimeStyle {
|
||||
fontSize: 10;
|
||||
paddingTop: 0;
|
||||
}
|
||||
|
@ -35,4 +35,7 @@ purchase a royalty-free license.
|
||||
I'm unavailable for custom icon design work. But your
|
||||
suggestions are always welcome!
|
||||
<mailto:p@yusukekamiyamane.com>
|
||||
====================
|
||||
====================
|
||||
Some of the client icons were generated using the following online tool:
|
||||
|
||||
http://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=clipart&foreground.space.trim=1&foreground.space.pad=0.15&foreground.clipart=res%2Fclipart%2Ficons%2Fnavigation_refresh.svg&foreColor=4b4b4b%2C0&crop=0&backgroundShape=none&backColor=ffffff%2C100&effects=none
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -235,6 +235,7 @@ bbb.chat.minimizeBtn.accessibilityName = Minimize the Chat Window
|
||||
bbb.chat.maximizeRestoreBtn.accessibilityName = Maximize the Chat Window
|
||||
bbb.chat.closeBtn.accessibilityName = Close the Chat Window
|
||||
bbb.chat.chatTabs.accessibleNotice = New messages in this tab.
|
||||
bbb.chat.chatMessage.systemMessage = System
|
||||
bbb.publishVideo.changeCameraBtn.labelText = Change Webcam
|
||||
bbb.publishVideo.changeCameraBtn.toolTip = Open the change webcam dialog box
|
||||
bbb.publishVideo.cmbResolution.tooltip = Select a webcam resolution
|
||||
@ -344,6 +345,13 @@ bbb.logout.confirm.title = Confirm Logout
|
||||
bbb.logout.confirm.message = Are you sure you want to log out?
|
||||
bbb.logout.confirm.yes = Yes
|
||||
bbb.logout.confirm.no = No
|
||||
bbb.connection.failure=Network failure
|
||||
bbb.connection.reconnecting=Reconnecting
|
||||
bbb.connection.reestablished=Connection reestablished
|
||||
bbb.connection.bigbluebutton=BigBlueButton
|
||||
bbb.connection.sip=SIP
|
||||
bbb.connection.video=Video
|
||||
bbb.connection.deskshare=Deskshare
|
||||
bbb.notes.title = Notes
|
||||
bbb.notes.cmpColorPicker.toolTip = Text Color
|
||||
bbb.notes.saveBtn = Save
|
||||
@ -493,10 +501,13 @@ bbb.shortcutkey.chat.chatbox.debug.function = Temporary debug hotkey
|
||||
bbb.polling.startButton.tooltip = Start a poll
|
||||
bbb.polling.publishButton.label = Publish
|
||||
bbb.polling.closeButton.label = Close
|
||||
bbb.polling.answer.Y = Yes
|
||||
bbb.polling.answer.N = No
|
||||
bbb.polling.answer.T = True
|
||||
bbb.polling.answer.F = False
|
||||
bbb.polling.pollModal.title = Poll
|
||||
bbb.polling.respondersLabel.novotes = No Users Responded
|
||||
bbb.polling.respondersLabel.text = {0} Users Responded
|
||||
bbb.polling.answer.Yes = Yes
|
||||
bbb.polling.answer.No = No
|
||||
bbb.polling.answer.True = True
|
||||
bbb.polling.answer.False = False
|
||||
bbb.polling.answer.A = A
|
||||
bbb.polling.answer.B = B
|
||||
bbb.polling.answer.C = C
|
||||
@ -504,6 +515,8 @@ bbb.polling.answer.D = D
|
||||
bbb.polling.answer.E = E
|
||||
bbb.polling.answer.F = F
|
||||
bbb.polling.answer.G = G
|
||||
bbb.polling.results.accessible.header = Poll Results.
|
||||
bbb.polling.results.accessible.answer = Answer {0} had {1} votes.
|
||||
|
||||
bbb.publishVideo.startPublishBtn.labelText = Start Sharing
|
||||
bbb.publishVideo.changeCameraBtn.labelText = Change Webcam Settings
|
||||
|
Binary file not shown.
Binary file not shown.
@ -3,6 +3,9 @@ if (!window.console.log) window.console.log = function () { };
|
||||
|
||||
function startApplet(IP, useTLS , roomNumber, fullScreen, useSVC2)
|
||||
{
|
||||
var deskshareElement = document.getElementById("deskshare");
|
||||
if (deskshareElement == null) {
|
||||
|
||||
console.log("Starting deskshare applet.");
|
||||
var div = document.createElement("div");
|
||||
div.id = "deskshare";
|
||||
@ -21,15 +24,40 @@ function startApplet(IP, useTLS , roomNumber, fullScreen, useSVC2)
|
||||
"<param name=\"permissions\" value=\"all-permissions\"/>" +
|
||||
"</applet>";
|
||||
document.body.appendChild(div);
|
||||
} else {
|
||||
console.log("Deskshare applet element already exists.");
|
||||
var div = document.getElementById("deskshare");
|
||||
if (div.parentNode) {
|
||||
// Just rewrite the applet tag to kick off the applet. We don't remove the applet tag
|
||||
// when desktop sharing is stopped to prevent Firefox (38.0.5) from asking for user permissions
|
||||
// again resulting in the page reloading. (ralam june 17, 2015)
|
||||
// https://code.google.com/p/bigbluebutton/issues/detail?id=1953
|
||||
div.innerHTML =
|
||||
"<applet code=\"org.bigbluebutton.deskshare.client.DeskShareApplet.class\"" +
|
||||
"id=\"DeskShareApplet\" width=\"100\" height=\"10\" archive=\"bbb-deskshare-applet-0.9.0.jar\">" +
|
||||
"<param name=\"ROOM\" value=\"" + roomNumber + "\"/>" +
|
||||
"<param name=\"IP\" value=\"" + IP + "\"/>" +
|
||||
"<param name=\"PORT\" value=\"9123\"/>" +
|
||||
"<param name=\"SCALE\" value=\"0.8\"/>" +
|
||||
"<param name=\"FULL_SCREEN\" value=\"" + fullScreen + "\"/>" +
|
||||
"<param name=\"SVC2\" value=\"" + useSVC2 + "\"/>" +
|
||||
"<param name=\"JavaVersion\" value=\"1.7.0_51\"/>" +
|
||||
"<param name=\"permissions\" value=\"all-permissions\"/>" +
|
||||
"</applet>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function removeFrame () {
|
||||
var div = document.getElementById("deskshare");
|
||||
if (div.parentNode) {
|
||||
// Need to set the innerHTML otherwise the applet will restart in IE.
|
||||
// see https://code.google.com/p/bigbluebutton/issues/detail?id=1776
|
||||
div.innerHTML = "";
|
||||
div.parentNode.removeChild(div);
|
||||
// Do NOT remove the applet tag as it makes Firefox (38.0.5) prompt for
|
||||
// permissions again resulting in the page reloading. (ralam june 17, 2015)
|
||||
// div.innerHTML = "";
|
||||
// div.parentNode.removeChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,4 +171,4 @@ function checkJavaVersion(minJavaVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.EventBroadcaster;
|
||||
import org.bigbluebutton.main.api.ExternalApiCallbacks;
|
||||
@ -71,13 +72,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
protected function init():void {
|
||||
setupTooltips();
|
||||
setupAPI();
|
||||
EventBroadcaster.getInstance().addEventListener("configLoadedEvent", configLoadedEventHandler);
|
||||
BBB.initConfigManager();
|
||||
EventBroadcaster.getInstance().addEventListener("configLoadedEvent", configLoadedEventHandler);
|
||||
BBB.initConfigManager();
|
||||
BBB.initVideoProfileManager();
|
||||
globalModifier = ExternalInterface.call("determineGlobalModifier");
|
||||
}
|
||||
|
||||
private function configLoadedEventHandler(e:Event):void {
|
||||
LogUtil.initLogging();
|
||||
LOGGER.debug("***** Config Loaded ****");
|
||||
mainShell.initOptions(null);
|
||||
ShortcutOptions.initialize();
|
||||
|
@ -0,0 +1,199 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.core.managers
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.core.IFlexDisplayObject;
|
||||
import mx.managers.PopUpManager;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.main.events.LogoutEvent;
|
||||
import org.bigbluebutton.main.model.users.AutoReconnect;
|
||||
import org.bigbluebutton.main.views.ReconnectionPopup;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
public class ReconnectionManager
|
||||
{
|
||||
private static const LOGGER:ILogger = getClassLogger(AutoReconnect);
|
||||
|
||||
public static const BIGBLUEBUTTON_CONNECTION:String = "BIGBLUEBUTTON_CONNECTION";
|
||||
public static const SIP_CONNECTION:String = "SIP_CONNECTION";
|
||||
public static const VIDEO_CONNECTION:String = "VIDEO_CONNECTION";
|
||||
public static const DESKSHARE_CONNECTION:String = "DESKSHARE_CONNECTION";
|
||||
|
||||
private var _connections:Dictionary = new Dictionary();
|
||||
private var _reestablished:ArrayCollection = new ArrayCollection();
|
||||
private var _reconnectTimer:Timer = new Timer(10000, 1);
|
||||
private var _reconnectTimeout:Timer = new Timer(5000, 1);
|
||||
private var _dispatcher:Dispatcher = new Dispatcher();
|
||||
private var _popup:IFlexDisplayObject = null;
|
||||
private var _canceled:Boolean = false;
|
||||
|
||||
public function ReconnectionManager() {
|
||||
_reconnectTimer.addEventListener(TimerEvent.TIMER_COMPLETE, reconnect);
|
||||
_reconnectTimeout.addEventListener(TimerEvent.TIMER_COMPLETE, timeout);
|
||||
}
|
||||
|
||||
private function reconnect(e:TimerEvent = null):void {
|
||||
if (_connections.hasOwnProperty(BIGBLUEBUTTON_CONNECTION)) {
|
||||
reconnectHelper(BIGBLUEBUTTON_CONNECTION);
|
||||
} else {
|
||||
for (var type:String in _connections) {
|
||||
reconnectHelper(type);
|
||||
}
|
||||
}
|
||||
if (!_reconnectTimeout.running)
|
||||
_reconnectTimeout.start();
|
||||
}
|
||||
|
||||
private function timeout(e:TimerEvent = null):void {
|
||||
LOGGER.debug("timeout");
|
||||
_dispatcher.dispatchEvent(new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT));
|
||||
_dispatcher.dispatchEvent(new LogoutEvent(LogoutEvent.USER_LOGGED_OUT));
|
||||
}
|
||||
|
||||
private function reconnectHelper(type:String):void {
|
||||
var obj:Object = _connections[type];
|
||||
obj.reconnect = new AutoReconnect();
|
||||
obj.reconnect.onDisconnect(obj.callback, obj.callbackParameters);
|
||||
}
|
||||
|
||||
public function onDisconnected(type:String, callback:Function, parameters:Array):void {
|
||||
if (!_canceled) {
|
||||
LOGGER.warn("onDisconnected, type={0}, parameters={1}" + [type, parameters.toString()]);
|
||||
|
||||
var obj:Object = new Object();
|
||||
obj.callback = callback;
|
||||
obj.callbackParameters = parameters;
|
||||
_connections[type] = obj;
|
||||
|
||||
if (!_reconnectTimer.running) {
|
||||
_popup = PopUpManager.createPopUp(FlexGlobals.topLevelApplication as DisplayObject, ReconnectionPopup, true);
|
||||
PopUpManager.centerPopUp(_popup);
|
||||
|
||||
_reconnectTimer.reset();
|
||||
_reconnectTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onConnectionAttemptFailed(type:String):void {
|
||||
LOGGER.warn("onConnectionAttemptFailed, type={0}", [type]);
|
||||
if (_connections.hasOwnProperty(type)) {
|
||||
_connections[type].reconnect.onConnectionAttemptFailed();
|
||||
}
|
||||
}
|
||||
|
||||
private function get connectionDictEmpty():Boolean {
|
||||
for (var key:Object in _connections) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function dispatchReconnectionSucceededEvent(type:String):void {
|
||||
var map:Object = {
|
||||
BIGBLUEBUTTON_CONNECTION: BBBEvent.RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT,
|
||||
SIP_CONNECTION: BBBEvent.RECONNECT_SIP_SUCCEEDED_EVENT,
|
||||
VIDEO_CONNECTION: BBBEvent.RECONNECT_VIDEO_SUCCEEDED_EVENT,
|
||||
DESKSHARE_CONNECTION: BBBEvent.RECONNECT_DESKSHARE_SUCCEEDED_EVENT
|
||||
};
|
||||
|
||||
if (map.hasOwnProperty(type)) {
|
||||
LOGGER.debug("dispatchReconnectionSucceededEvent, type={0}", [type]);
|
||||
_dispatcher.dispatchEvent(new BBBEvent(map[type]));
|
||||
} else {
|
||||
LOGGER.debug("dispatchReconnectionSucceededEvent, couldn't find a map value for type {0}", [type]);
|
||||
}
|
||||
}
|
||||
|
||||
public function onConnectionAttemptSucceeded(type:String):void {
|
||||
LOGGER.debug("onConnectionAttemptSucceeded, type={0}", [type]);
|
||||
dispatchReconnectionSucceededEvent(type);
|
||||
|
||||
delete _connections[type];
|
||||
if (type == BIGBLUEBUTTON_CONNECTION) {
|
||||
reconnect();
|
||||
}
|
||||
|
||||
_reestablished.addItem(type);
|
||||
if (connectionDictEmpty) {
|
||||
var msg:String = connectionReestablishedMessage();
|
||||
|
||||
_dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.SUCCESS_MESSAGE_EVENT,
|
||||
ResourceUtil.getInstance().getString('bbb.connection.reestablished'),
|
||||
msg));
|
||||
|
||||
_reconnectTimeout.reset();
|
||||
removePopUp();
|
||||
}
|
||||
}
|
||||
|
||||
public function onCancelReconnection():void {
|
||||
_canceled = true;
|
||||
|
||||
for (var type:Object in _connections) delete _connections[type];
|
||||
|
||||
removePopUp();
|
||||
}
|
||||
|
||||
private function removePopUp():void {
|
||||
if (_popup != null) {
|
||||
PopUpManager.removePopUp(_popup);
|
||||
_popup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function connectionReestablishedMessage():String {
|
||||
var msg:String = "";
|
||||
for each (var conn:String in _reestablished) {
|
||||
switch (conn) {
|
||||
case BIGBLUEBUTTON_CONNECTION:
|
||||
msg += ResourceUtil.getInstance().getString('bbb.connection.bigbluebutton');
|
||||
break;
|
||||
case SIP_CONNECTION:
|
||||
msg += ResourceUtil.getInstance().getString('bbb.connection.sip');
|
||||
break;
|
||||
case VIDEO_CONNECTION:
|
||||
msg += ResourceUtil.getInstance().getString('bbb.connection.video');
|
||||
break;
|
||||
case DESKSHARE_CONNECTION:
|
||||
msg += ResourceUtil.getInstance().getString('bbb.connection.deskshare');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
msg += " ";
|
||||
}
|
||||
_reestablished.removeAll();
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ package org.bigbluebutton.core.services
|
||||
// dispatch event
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("*** failed to get voice user name=[{0}] **** \n", [vu.name]);
|
||||
LOGGER.debug("*** failed to get user name=[{0}] **** \n", [vu.name]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,4 +115,4 @@ package org.bigbluebutton.core.services
|
||||
}
|
||||
}
|
||||
|
||||
class UsersServiceSingletonEnforcer{}
|
||||
class UsersServiceSingletonEnforcer{}
|
||||
|
@ -43,6 +43,17 @@ package org.bigbluebutton.main.events {
|
||||
public static const JOIN_VOICE_FOCUS_HEAD:String = "JOIN_VOICE_FOCUS_HEAD";
|
||||
public static const CHANGE_RECORDING_STATUS:String = "CHANGE_RECORDING_STATUS";
|
||||
|
||||
public static const RECONNECT_DISCONNECTED_EVENT:String = "RECONNECT_ON_DISCONNECTED_EVENT";
|
||||
public static const RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT:String = "RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT";
|
||||
public static const RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT:String = "RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT";
|
||||
|
||||
public static const RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT:String = "RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT";
|
||||
public static const RECONNECT_VIDEO_SUCCEEDED_EVENT:String = "RECONNECT_VIDEO_SUCCEEDED_EVENT";
|
||||
public static const RECONNECT_SIP_SUCCEEDED_EVENT:String = "RECONNECT_SIP_SUCCEEDED_EVENT";
|
||||
public static const RECONNECT_DESKSHARE_SUCCEEDED_EVENT:String = "RECONNECT_DESKSHARE_SUCCEEDED_EVENT";
|
||||
|
||||
public static const CANCEL_RECONNECTION_EVENT:String = "CANCEL_RECONNECTION_EVENT";
|
||||
|
||||
public var message:String;
|
||||
public var payload:Object = new Object();
|
||||
|
||||
@ -51,4 +62,4 @@ package org.bigbluebutton.main.events {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<ObjectBuilder generator="{ModulesProxy}" cache="global" />
|
||||
<ObjectBuilder generator="{ConfigManager}" cache="global" />
|
||||
<ObjectBuilder generator="{ReconnectionManager}" cache="global" />
|
||||
<!--
|
||||
Disabling temporarily the stream monitor
|
||||
-->
|
||||
@ -69,6 +70,22 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<MethodInvoker generator="{ModulesProxy}" method="startAllModules" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}">
|
||||
<MethodInvoker generator="{ReconnectionManager}" method="onDisconnected" arguments="{[event.payload.type, event.payload.callback, event.payload.callbackParameters]}"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT}">
|
||||
<MethodInvoker generator="{ReconnectionManager}" method="onConnectionAttemptFailed" arguments="{event.payload.type}"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT}">
|
||||
<MethodInvoker generator="{ReconnectionManager}" method="onConnectionAttemptSucceeded" arguments="{event.payload.type}"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.CANCEL_RECONNECTION_EVENT}">
|
||||
<MethodInvoker generator="{ReconnectionManager}" method="onCancelReconnection" />
|
||||
</EventHandlers>
|
||||
|
||||
|
||||
|
||||
<mx:Script>
|
||||
@ -76,7 +93,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import mx.events.FlexEvent;
|
||||
|
||||
import org.bigbluebutton.core.managers.ConfigManager;
|
||||
import org.bigbluebutton.core.managers.ReconnectionManager;
|
||||
import org.bigbluebutton.core.services.SkinningService;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ConfigEvent;
|
||||
import org.bigbluebutton.main.events.LogoutEvent;
|
||||
import org.bigbluebutton.main.events.ModuleLoadEvent;
|
||||
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.main.model.users
|
||||
{
|
||||
import flash.events.TimerEvent;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
|
||||
public class AutoReconnect
|
||||
{
|
||||
private static const LOGGER:ILogger = getClassLogger(AutoReconnect);
|
||||
|
||||
private var _backoff:Number = 2000;
|
||||
private var _reconnectCallback:Function;
|
||||
private var _reconnectParameters:Array;
|
||||
|
||||
public function onDisconnect(callback:Function, parameters:Array):void {
|
||||
LOGGER.debug("onDisconnect, parameters={0}", [parameters.toString()]);
|
||||
_reconnectCallback = callback;
|
||||
_reconnectParameters = parameters;
|
||||
attemptReconnect(0);
|
||||
}
|
||||
|
||||
public function onConnectionAttemptFailed():void {
|
||||
LOGGER.warn("onConnectionAttemptFailed");
|
||||
attemptReconnect(_backoff);
|
||||
}
|
||||
|
||||
private function attemptReconnect(backoff:Number):void {
|
||||
LOGGER.debug("attemptReconnect backoff={0}", [backoff]);
|
||||
var retryTimer:Timer = new Timer(backoff, 1);
|
||||
retryTimer.addEventListener(TimerEvent.TIMER, function():void {
|
||||
LOGGER.debug("Reconnecting");
|
||||
_reconnectCallback.apply(null, _reconnectParameters);
|
||||
});
|
||||
retryTimer.start();
|
||||
if (_backoff < 16000) _backoff = Math.max(backoff, 500) *2;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,405 +1,435 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.main.model.users
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.AsyncErrorEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.Responder;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.as3commons.logging.util.jsonXify;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.services.BandwidthMonitor;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.model.ConferenceParameters;
|
||||
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.main.model.users
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.AsyncErrorEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.Responder;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.as3commons.logging.util.jsonXify;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.ReconnectionManager;
|
||||
import org.bigbluebutton.core.services.BandwidthMonitor;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.model.ConferenceParameters;
|
||||
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
|
||||
|
||||
public class NetConnectionDelegate
|
||||
{
|
||||
private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate);
|
||||
|
||||
private var _netConnection:NetConnection;
|
||||
private var connectionId:Number;
|
||||
private var connected:Boolean = false;
|
||||
|
||||
private var _userid:Number = -1;
|
||||
private var _role:String = "unknown";
|
||||
private var _applicationURI:String;
|
||||
private var _conferenceParameters:ConferenceParameters;
|
||||
|
||||
// These two are just placeholders. We'll get this from the server later and
|
||||
// then pass to other modules.
|
||||
private var _authToken:String = "AUTHORIZED";
|
||||
private var _room:String;
|
||||
private var tried_tunneling:Boolean = false;
|
||||
private var logoutOnUserCommand:Boolean = false;
|
||||
private var backoff:Number = 2000;
|
||||
|
||||
private var dispatcher:Dispatcher;
|
||||
private var _messageListeners:Array = new Array();
|
||||
|
||||
private var authenticated: Boolean = false;
|
||||
|
||||
public function NetConnectionDelegate():void
|
||||
{
|
||||
dispatcher = new Dispatcher();
|
||||
|
||||
_netConnection = new NetConnection();
|
||||
_netConnection.proxyType = "best";
|
||||
_netConnection.client = this;
|
||||
_netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
|
||||
_netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
|
||||
_netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
|
||||
_netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
|
||||
}
|
||||
|
||||
public function setUri(uri:String):void {
|
||||
_applicationURI = uri;
|
||||
}
|
||||
|
||||
|
||||
public function get connection():NetConnection {
|
||||
return _netConnection;
|
||||
}
|
||||
|
||||
public function addMessageListener(listener:IMessageListener):void {
|
||||
_messageListeners.push(listener);
|
||||
}
|
||||
|
||||
public function removeMessageListener(listener:IMessageListener):void {
|
||||
for (var ob:int=0; ob<_messageListeners.length; ob++) {
|
||||
if (_messageListeners[ob] == listener) {
|
||||
_messageListeners.splice (ob,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function notifyListeners(messageName:String, message:Object):void {
|
||||
if (messageName != null && messageName != "") {
|
||||
for (var notify:String in _messageListeners) {
|
||||
_messageListeners[notify].onMessage(messageName, message);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Message name is undefined");
|
||||
}
|
||||
}
|
||||
|
||||
public function onMessageFromServer(messageName:String, msg:Object):void {
|
||||
LOGGER.debug("Got message from server [{0}] user=[{1}]", [messageName, UsersUtil.getMyUsername()]);
|
||||
if (!authenticated && (messageName == "validateAuthTokenReply")) {
|
||||
handleValidateAuthTokenReply(msg)
|
||||
} else if (messageName == "validateAuthTokenTimedOut") {
|
||||
handleValidateAuthTokenTimedOut(msg)
|
||||
} else if (authenticated) {
|
||||
notifyListeners(messageName, msg);
|
||||
} else {
|
||||
LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateToken():void {
|
||||
var message:Object = new Object();
|
||||
message["userId"] = _conferenceParameters.internalUserID;
|
||||
message["authToken"] = _conferenceParameters.authToken;
|
||||
|
||||
sendMessage(
|
||||
"validateToken",// Remote function name
|
||||
// result - On successful result
|
||||
function(result:Object):void {
|
||||
LOGGER.debug("validating token for [{0}]", [_conferenceParameters.internalUserID]);
|
||||
},
|
||||
// status - On error occurred
|
||||
function(status:Object):void {
|
||||
LOGGER.error("Error occurred:");
|
||||
for (var x:Object in status) {
|
||||
LOGGER.error(x + " : " + status[x]);
|
||||
}
|
||||
},
|
||||
message
|
||||
); //_netConnection.call
|
||||
}
|
||||
|
||||
private function handleValidateAuthTokenTimedOut(msg: Object):void {
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut {0} **** \n", [msg.msg]);
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var tokenValid: Boolean = map.valid as Boolean;
|
||||
var userId: String = map.userId as String;
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.critical("Validate auth token timed out.", logData);
|
||||
|
||||
if (tokenValid) {
|
||||
authenticated = true;
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut. valid=[{0}] **** \n", [tokenValid]);
|
||||
} else {
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut. valid=[{0}] **** \n", [tokenValid]);
|
||||
dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private function handleValidateAuthTokenReply(msg: Object):void {
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply {0} **** \n", [msg.msg]);
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var tokenValid: Boolean = map.valid as Boolean;
|
||||
var userId: String = map.userId as String;
|
||||
|
||||
if (tokenValid) {
|
||||
authenticated = true;
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply. valid=[{0}] **** \n", [tokenValid]);
|
||||
} else {
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply. valid=[{0}] **** \n", [tokenValid]);
|
||||
dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private function sendConnectionSuccessEvent(userid:String):void{
|
||||
var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS);
|
||||
e.userid = userid;
|
||||
dispatcher.dispatchEvent(e);
|
||||
|
||||
}
|
||||
|
||||
public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void {
|
||||
LOGGER.debug("SENDING [{0}]", [service]);
|
||||
var responder:Responder = new Responder(
|
||||
function(result:Object):void { // On successful result
|
||||
onSuccess("Successfully sent [" + service + "].");
|
||||
},
|
||||
function(status:Object):void { // status - On error occurred
|
||||
var errorReason:String = "Failed to send [" + service + "]:\n";
|
||||
for (var x:Object in status) {
|
||||
errorReason += "\t" + x + " : " + status[x];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (message == null) {
|
||||
_netConnection.call(service, responder);
|
||||
} else {
|
||||
_netConnection.call(service, responder, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the server.
|
||||
* uri: The uri to the conference application.
|
||||
* username: Fullname of the participant.
|
||||
* role: MODERATOR/VIEWER
|
||||
* conference: The conference room
|
||||
* mode: LIVE/PLAYBACK - Live:when used to collaborate, Playback:when being used to playback a recorded conference.
|
||||
* room: Need the room number when playing back a recorded conference. When LIVE, the room is taken from the URI.
|
||||
*/
|
||||
public function connect(params:ConferenceParameters, tunnel:Boolean = false):void {
|
||||
_conferenceParameters = params;
|
||||
|
||||
tried_tunneling = tunnel;
|
||||
|
||||
try {
|
||||
var uri:String = _applicationURI + "/" + _conferenceParameters.room;
|
||||
|
||||
LOGGER.debug("::Connecting to {0} [{1}]", [uri, jsonXify(_conferenceParameters)]);
|
||||
_netConnection.connect(uri, _conferenceParameters.username, _conferenceParameters.role,
|
||||
_conferenceParameters.room, _conferenceParameters.voicebridge,
|
||||
_conferenceParameters.record, _conferenceParameters.externUserID,
|
||||
_conferenceParameters.internalUserID, _conferenceParameters.muteOnStart, _conferenceParameters.lockSettings);
|
||||
} catch(e:ArgumentError) {
|
||||
// Invalid parameters.
|
||||
switch (e.errorID) {
|
||||
case 2004 :
|
||||
LOGGER.debug("Error! Invalid server location: {0}", [uri]);
|
||||
break;
|
||||
default :
|
||||
LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect(logoutOnUserCommand:Boolean):void {
|
||||
this.logoutOnUserCommand = logoutOnUserCommand;
|
||||
_netConnection.close();
|
||||
}
|
||||
|
||||
|
||||
public function forceClose():void {
|
||||
_netConnection.close();
|
||||
}
|
||||
|
||||
protected function netStatus(event:NetStatusEvent):void {
|
||||
handleResult( event );
|
||||
}
|
||||
|
||||
private var _bwMon:BandwidthMonitor = new BandwidthMonitor();
|
||||
|
||||
private function startMonitoringBandwidth():void {
|
||||
LOGGER.info("Start monitoring bandwidth.");
|
||||
var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
|
||||
var result:Array = pattern.exec(_applicationURI);
|
||||
_bwMon.serverURL = result.server;
|
||||
_bwMon.serverApplication = "video";
|
||||
_bwMon.start();
|
||||
}
|
||||
|
||||
private var autoReconnectTimer:Timer = new Timer(1000, 1);
|
||||
|
||||
public function handleResult(event:Object):void {
|
||||
var info : Object = event.info;
|
||||
var statusCode : String = info.code;
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.user = UsersUtil.getUserData();
|
||||
|
||||
switch (statusCode) {
|
||||
case "NetConnection.Connect.Success":
|
||||
LOGGER.debug("Connection to viewers application succeeded.");
|
||||
JSLog.debug("Successfully connected to BBB App.", logData);
|
||||
|
||||
validateToken();
|
||||
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Failed":
|
||||
if (tried_tunneling) {
|
||||
LOGGER.error(":Connection to viewers application failed...even when tunneling");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);
|
||||
} else {
|
||||
disconnect(false);
|
||||
LOGGER.error(":Connection to viewers application failed...try tunneling");
|
||||
var rtmptRetryTimer:Timer = new Timer(1000, 1);
|
||||
rtmptRetryTimer.addEventListener("timer", rtmptRetryTimerHandler);
|
||||
rtmptRetryTimer.start();
|
||||
}
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Closed":
|
||||
LOGGER.debug("Connection to viewers application closed");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED);
|
||||
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.InvalidApp":
|
||||
LOGGER.debug(":viewers application not found on server");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.AppShutDown":
|
||||
LOGGER.debug(":viewers application has been shutdown");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Rejected":
|
||||
LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [_applicationURI]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.NetworkChange":
|
||||
JSLog.warn("Detected network change to BBB App", logData);
|
||||
LOGGER.debug("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
|
||||
break;
|
||||
|
||||
default :
|
||||
LOGGER.debug(":Default status to the viewers application" );
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function autoReconnectTimerHandler(event:TimerEvent):void {
|
||||
LOGGER.debug("autoReconnectTimerHandler: {0}", [event]);
|
||||
connect(_conferenceParameters, tried_tunneling);
|
||||
}
|
||||
|
||||
private function rtmptRetryTimerHandler(event:TimerEvent):void {
|
||||
LOGGER.debug("rtmptRetryTimerHandler: {0}", [event]);
|
||||
connect(_conferenceParameters, true);
|
||||
}
|
||||
|
||||
protected function netSecurityError(event: SecurityErrorEvent):void {
|
||||
LOGGER.error("Security error - {0}", [event.text]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
protected function netIOError(event: IOErrorEvent):void {
|
||||
LOGGER.error("Input/output error - {0}", [event.text]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
protected function netASyncError(event: AsyncErrorEvent):void {
|
||||
LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
|
||||
|
||||
LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
private function sendConnectionFailedEvent(reason:String):void{
|
||||
var logData:Object = new Object();
|
||||
|
||||
if (this.logoutOnUserCommand) {
|
||||
logData.reason = "User requested.";
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.debug("User logged out from BBB App.", logData);
|
||||
sendUserLoggedOutEvent();
|
||||
} else {
|
||||
logData.reason = reason;
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.warn("User disconnected from BBB App.", logData);
|
||||
var e:ConnectionFailedEvent = new ConnectionFailedEvent(reason);
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendUserLoggedOutEvent():void{
|
||||
var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT);
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
|
||||
private function attemptReconnect(backoff:Number):void{
|
||||
var retryTimer:Timer = new Timer(backoff, 1);
|
||||
retryTimer.addEventListener(TimerEvent.TIMER, function():void{
|
||||
connect(_conferenceParameters, tried_tunneling);
|
||||
});
|
||||
retryTimer.start();
|
||||
if (this.backoff < 16000) this.backoff = backoff *2;
|
||||
}
|
||||
|
||||
public function onBWCheck(... rest):Number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function onBWDone(... rest):void {
|
||||
var p_bw:Number;
|
||||
if (rest.length > 0) p_bw = rest[0];
|
||||
// your application should do something here
|
||||
// when the bandwidth check is complete
|
||||
LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NetConnectionDelegate
|
||||
{
|
||||
private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate);
|
||||
|
||||
private var _netConnection:NetConnection;
|
||||
private var connectionId:Number;
|
||||
private var connected:Boolean = false;
|
||||
|
||||
private var _userid:Number = -1;
|
||||
private var _role:String = "unknown";
|
||||
private var _applicationURI:String;
|
||||
private var _conferenceParameters:ConferenceParameters;
|
||||
|
||||
// These two are just placeholders. We'll get this from the server later and
|
||||
// then pass to other modules.
|
||||
private var _authToken:String = "AUTHORIZED";
|
||||
private var _room:String;
|
||||
private var tried_tunneling:Boolean = false;
|
||||
private var logoutOnUserCommand:Boolean = false;
|
||||
private var backoff:Number = 2000;
|
||||
|
||||
private var dispatcher:Dispatcher;
|
||||
private var _messageListeners:Array = new Array();
|
||||
|
||||
private var authenticated: Boolean = false;
|
||||
private var reconnecting:Boolean = false;
|
||||
|
||||
public function NetConnectionDelegate():void
|
||||
{
|
||||
dispatcher = new Dispatcher();
|
||||
|
||||
_netConnection = new NetConnection();
|
||||
_netConnection.proxyType = "best";
|
||||
_netConnection.client = this;
|
||||
_netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
|
||||
_netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
|
||||
_netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
|
||||
_netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
|
||||
}
|
||||
|
||||
public function setUri(uri:String):void {
|
||||
_applicationURI = uri;
|
||||
}
|
||||
|
||||
|
||||
public function get connection():NetConnection {
|
||||
return _netConnection;
|
||||
}
|
||||
|
||||
public function addMessageListener(listener:IMessageListener):void {
|
||||
_messageListeners.push(listener);
|
||||
}
|
||||
|
||||
public function removeMessageListener(listener:IMessageListener):void {
|
||||
for (var ob:int=0; ob<_messageListeners.length; ob++) {
|
||||
if (_messageListeners[ob] == listener) {
|
||||
_messageListeners.splice (ob,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function notifyListeners(messageName:String, message:Object):void {
|
||||
if (messageName != null && messageName != "") {
|
||||
for (var notify:String in _messageListeners) {
|
||||
_messageListeners[notify].onMessage(messageName, message);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Message name is undefined");
|
||||
}
|
||||
}
|
||||
|
||||
public function onMessageFromServer(messageName:String, msg:Object):void {
|
||||
LOGGER.debug("Got message from server [{0}] user=[{1}]", [messageName, UsersUtil.getMyUsername()]);
|
||||
if (!authenticated && (messageName == "validateAuthTokenReply")) {
|
||||
handleValidateAuthTokenReply(msg)
|
||||
} else if (messageName == "validateAuthTokenTimedOut") {
|
||||
handleValidateAuthTokenTimedOut(msg)
|
||||
} else if (authenticated) {
|
||||
notifyListeners(messageName, msg);
|
||||
} else {
|
||||
LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateToken():void {
|
||||
var message:Object = new Object();
|
||||
message["userId"] = _conferenceParameters.internalUserID;
|
||||
message["authToken"] = _conferenceParameters.authToken;
|
||||
|
||||
sendMessage(
|
||||
"validateToken",// Remote function name
|
||||
// result - On successful result
|
||||
function(result:Object):void {
|
||||
LOGGER.debug("validating token for [{0}]", [_conferenceParameters.internalUserID]);
|
||||
},
|
||||
// status - On error occurred
|
||||
function(status:Object):void {
|
||||
LOGGER.error("Error occurred:");
|
||||
for (var x:Object in status) {
|
||||
LOGGER.error(x + " : " + status[x]);
|
||||
}
|
||||
},
|
||||
message
|
||||
); //_netConnection.call
|
||||
}
|
||||
|
||||
private function handleValidateAuthTokenTimedOut(msg: Object):void {
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut {0} **** \n", [msg.msg]);
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var tokenValid: Boolean = map.valid as Boolean;
|
||||
var userId: String = map.userId as String;
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.critical("Validate auth token timed out.", logData);
|
||||
|
||||
if (tokenValid) {
|
||||
authenticated = true;
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut. valid=[{0}] **** \n", [tokenValid]);
|
||||
} else {
|
||||
LOGGER.debug("*** handleValidateAuthTokenTimedOut. valid=[{0}] **** \n", [tokenValid]);
|
||||
dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
|
||||
}
|
||||
if (reconnecting) {
|
||||
onReconnect();
|
||||
reconnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleValidateAuthTokenReply(msg: Object):void {
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply {0} **** \n", [msg.msg]);
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var tokenValid: Boolean = map.valid as Boolean;
|
||||
var userId: String = map.userId as String;
|
||||
|
||||
if (tokenValid) {
|
||||
authenticated = true;
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply. valid=[{0}] **** \n", [tokenValid]);
|
||||
} else {
|
||||
LOGGER.debug("*** handleValidateAuthTokenReply. valid=[{0}] **** \n", [tokenValid]);
|
||||
dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
|
||||
}
|
||||
if (reconnecting) {
|
||||
onReconnect();
|
||||
reconnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function onReconnect():void {
|
||||
if (authenticated) {
|
||||
onReconnectSuccess();
|
||||
} else {
|
||||
onReconnectFailed();
|
||||
}
|
||||
}
|
||||
|
||||
private function onReconnectSuccess():void {
|
||||
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
|
||||
attemptSucceeded.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptSucceeded);
|
||||
}
|
||||
|
||||
private function onReconnectFailed():void {
|
||||
sendUserLoggedOutEvent();
|
||||
}
|
||||
|
||||
private function sendConnectionSuccessEvent(userid:String):void{
|
||||
var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS);
|
||||
e.userid = userid;
|
||||
dispatcher.dispatchEvent(e);
|
||||
|
||||
}
|
||||
|
||||
public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void {
|
||||
LOGGER.debug("SENDING [{0}]", [service]);
|
||||
var responder:Responder = new Responder(
|
||||
function(result:Object):void { // On successful result
|
||||
onSuccess("Successfully sent [" + service + "].");
|
||||
},
|
||||
function(status:Object):void { // status - On error occurred
|
||||
var errorReason:String = "Failed to send [" + service + "]:\n";
|
||||
for (var x:Object in status) {
|
||||
errorReason += "\t" + x + " : " + status[x];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (message == null) {
|
||||
_netConnection.call(service, responder);
|
||||
} else {
|
||||
_netConnection.call(service, responder, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the server.
|
||||
* uri: The uri to the conference application.
|
||||
* username: Fullname of the participant.
|
||||
* role: MODERATOR/VIEWER
|
||||
* conference: The conference room
|
||||
* mode: LIVE/PLAYBACK - Live:when used to collaborate, Playback:when being used to playback a recorded conference.
|
||||
* room: Need the room number when playing back a recorded conference. When LIVE, the room is taken from the URI.
|
||||
*/
|
||||
public function connect(params:ConferenceParameters, tunnel:Boolean = false):void {
|
||||
_conferenceParameters = params;
|
||||
|
||||
tried_tunneling = tunnel;
|
||||
|
||||
try {
|
||||
var uri:String = _applicationURI + "/" + _conferenceParameters.room;
|
||||
|
||||
LOGGER.debug("::Connecting to {0} [{1}]", [uri, jsonXify(_conferenceParameters)]);
|
||||
_netConnection.connect(uri, _conferenceParameters.username, _conferenceParameters.role,
|
||||
_conferenceParameters.room, _conferenceParameters.voicebridge,
|
||||
_conferenceParameters.record, _conferenceParameters.externUserID,
|
||||
_conferenceParameters.internalUserID, _conferenceParameters.muteOnStart, _conferenceParameters.lockSettings);
|
||||
} catch(e:ArgumentError) {
|
||||
// Invalid parameters.
|
||||
switch (e.errorID) {
|
||||
case 2004 :
|
||||
LOGGER.debug("Error! Invalid server location: {0}", [uri]);
|
||||
break;
|
||||
default :
|
||||
LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect(logoutOnUserCommand:Boolean):void {
|
||||
this.logoutOnUserCommand = logoutOnUserCommand;
|
||||
_netConnection.close();
|
||||
}
|
||||
|
||||
|
||||
public function forceClose():void {
|
||||
_netConnection.close();
|
||||
}
|
||||
|
||||
protected function netStatus(event:NetStatusEvent):void {
|
||||
handleResult( event );
|
||||
}
|
||||
|
||||
private var _bwMon:BandwidthMonitor = new BandwidthMonitor();
|
||||
|
||||
private function startMonitoringBandwidth():void {
|
||||
LOGGER.info("Start monitoring bandwidth.");
|
||||
var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
|
||||
var result:Array = pattern.exec(_applicationURI);
|
||||
_bwMon.serverURL = result.server;
|
||||
_bwMon.serverApplication = "video";
|
||||
_bwMon.start();
|
||||
}
|
||||
|
||||
public function handleResult(event:Object):void {
|
||||
var info : Object = event.info;
|
||||
var statusCode : String = info.code;
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.user = UsersUtil.getUserData();
|
||||
|
||||
switch (statusCode) {
|
||||
case "NetConnection.Connect.Success":
|
||||
LOGGER.debug("Connection to viewers application succeeded.");
|
||||
JSLog.debug("Successfully connected to BBB App.", logData);
|
||||
|
||||
validateToken();
|
||||
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Failed":
|
||||
if (tried_tunneling) {
|
||||
LOGGER.error(":Connection to viewers application failed...even when tunneling");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);
|
||||
} else {
|
||||
disconnect(false);
|
||||
LOGGER.error(":Connection to viewers application failed...try tunneling");
|
||||
var rtmptRetryTimer:Timer = new Timer(1000, 1);
|
||||
rtmptRetryTimer.addEventListener("timer", rtmptRetryTimerHandler);
|
||||
rtmptRetryTimer.start();
|
||||
}
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Closed":
|
||||
LOGGER.debug("Connection to viewers application closed");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED);
|
||||
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.InvalidApp":
|
||||
LOGGER.debug(":viewers application not found on server");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.AppShutDown":
|
||||
LOGGER.debug(":viewers application has been shutdown");
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Rejected":
|
||||
LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [_applicationURI]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED);
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.NetworkChange":
|
||||
JSLog.warn("Detected network change to BBB App", logData);
|
||||
LOGGER.debug("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
|
||||
break;
|
||||
|
||||
default :
|
||||
LOGGER.debug(":Default status to the viewers application" );
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function rtmptRetryTimerHandler(event:TimerEvent):void {
|
||||
LOGGER.debug("rtmptRetryTimerHandler: {0}", [event]);
|
||||
connect(_conferenceParameters, true);
|
||||
}
|
||||
|
||||
protected function netSecurityError(event: SecurityErrorEvent):void {
|
||||
LOGGER.error("Security error - {0}", [event.text]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
protected function netIOError(event: IOErrorEvent):void {
|
||||
LOGGER.error("Input/output error - {0}", [event.text]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
protected function netASyncError(event: AsyncErrorEvent):void {
|
||||
LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
|
||||
|
||||
LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
|
||||
sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
|
||||
}
|
||||
|
||||
private function sendConnectionFailedEvent(reason:String):void{
|
||||
var logData:Object = new Object();
|
||||
|
||||
if (this.logoutOnUserCommand) {
|
||||
logData.reason = "User requested.";
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.debug("User logged out from BBB App.", logData);
|
||||
sendUserLoggedOutEvent();
|
||||
} else if (reason == ConnectionFailedEvent.CONNECTION_CLOSED) {
|
||||
// do not try to reconnect if the connection failed is different than CONNECTION_CLOSED
|
||||
logData.reason = reason;
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.warn("User disconnected from BBB App.", logData);
|
||||
|
||||
if (reconnecting) {
|
||||
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
|
||||
attemptFailedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptFailedEvent);
|
||||
} else {
|
||||
reconnecting = true;
|
||||
authenticated = false;
|
||||
|
||||
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
|
||||
disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
|
||||
disconnectedEvent.payload.callback = connect;
|
||||
disconnectedEvent.payload.callbackParameters = new Array(_conferenceParameters, tried_tunneling);
|
||||
dispatcher.dispatchEvent(disconnectedEvent);
|
||||
}
|
||||
} else {
|
||||
var e:ConnectionFailedEvent = new ConnectionFailedEvent(reason);
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendUserLoggedOutEvent():void{
|
||||
var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT);
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
|
||||
public function onBWCheck(... rest):Number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function onBWDone(... rest):void {
|
||||
var p_bw:Number;
|
||||
if (rest.length > 0) p_bw = rest[0];
|
||||
// your application should do something here
|
||||
// when the bandwidth check is complete
|
||||
LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,11 @@
|
||||
break
|
||||
}
|
||||
titleLbl.text = value.title;
|
||||
if (value.occurrences > 1) {
|
||||
titleLbl.text = "(" + value.occurrences + ") " + titleLbl.text;
|
||||
}
|
||||
messageTxt.htmlText = value.message;
|
||||
timeLbl.text = value.time;
|
||||
|
||||
validateNow();
|
||||
}
|
||||
@ -31,4 +35,7 @@
|
||||
<mx:Label id="titleLbl" width="100%" styleName="statusTitleStyle"/>
|
||||
<mx:Text id="messageTxt" width="100%" styleName="statusMessageStyle"/>
|
||||
</mx:VBox>
|
||||
<mx:VBox height="100%" verticalAlign="bottom">
|
||||
<mx:Label id="timeLbl" width="100%" styleName="statusTimeStyle"/>
|
||||
</mx:VBox>
|
||||
</mx:HBox>
|
||||
|
@ -72,11 +72,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
//LogUtil.debug("Progress: " + totalProgress);
|
||||
this.setProgress(totalProgress/numModules, 100);
|
||||
}
|
||||
|
||||
|
||||
private function allModulesLoaded(e:ModuleLoadEvent):void{
|
||||
parent.removeChild(this);
|
||||
if (parent != null) parent.removeChild(this);
|
||||
}
|
||||
|
||||
|
||||
private function testRTMP(e:PortTestEvent):void{
|
||||
//- Cannot get locale string this early in loading process
|
||||
portUpdateStr += "."
|
||||
|
@ -23,7 +23,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
|
||||
title="{ResourceUtil.getInstance().getString('bbb.logout.title')}" showCloseButton="false" creationComplete="init()"
|
||||
verticalScrollPolicy="off" horizontalScrollPolicy="off"
|
||||
x="168" y="86" layout="vertical" width="400" height="200" horizontalAlign="center">
|
||||
x="168" y="86" layout="vertical" width="400" height="110" horizontalAlign="center">
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import mx.core.FlexGlobals;
|
||||
@ -52,6 +52,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
request.method = URLRequestMethod.GET;
|
||||
urlLoader = new URLLoader();
|
||||
urlLoader.addEventListener(Event.COMPLETE, handleComplete);
|
||||
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleRedirectError);
|
||||
urlLoader.load(request);
|
||||
}
|
||||
|
||||
@ -77,6 +78,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
PopUpManager.removePopUp(this);
|
||||
}
|
||||
|
||||
private function handleRedirectError(e:IOErrorEvent):void {
|
||||
PopUpManager.removePopUp(this);
|
||||
}
|
||||
|
||||
private function onUserLoggedOutWindowClose(e:Event):void {
|
||||
PopUpManager.removePopUp(this);
|
||||
}
|
||||
@ -110,17 +115,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function reconnect():void {
|
||||
ExternalInterface.call("document.location.reload", true);
|
||||
}
|
||||
]]>
|
||||
</mx:Script>
|
||||
<mx:VBox width="100%" height="100%" horizontalAlign="center">
|
||||
<mx:Text text="{message}"/>
|
||||
<mx:Button id="okBtn" label="{ResourceUtil.getInstance().getString('bbb.logout.button.label')}" click="redirect()"/>
|
||||
<mx:HRule width="100%" />
|
||||
<mx:Text width="380" textAlign="center" text="{ResourceUtil.getInstance().getString('bbb.logout.refresh.message')}" />
|
||||
<mx:Button id="reconnectBtn" label="{ResourceUtil.getInstance().getString('bbb.logout.refresh.label')}" click="reconnect()" />
|
||||
</mx:VBox>
|
||||
</mx:TitleWindow>
|
||||
</mx:TitleWindow>
|
||||
|
@ -83,7 +83,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBbbModuleWindow;
|
||||
import org.bigbluebutton.common.Images;
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.common.events.AddUIComponentToMainCanvas;
|
||||
import org.bigbluebutton.common.events.CloseWindowEvent;
|
||||
import org.bigbluebutton.common.events.OpenWindowEvent;
|
||||
@ -164,7 +163,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
public function initOptions(e:Event):void {
|
||||
LogUtil.initLogging();
|
||||
UserManager.getInstance().getConference().configLockSettings();
|
||||
layoutOptions = new LayoutOptions();
|
||||
layoutOptions.parseOptions();
|
||||
@ -585,6 +583,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
private function handleInvalidAuthToken(event:InvalidAuthTokenEvent):void {
|
||||
showlogoutWindow(ResourceUtil.getInstance().getString('bbb.mainshell.invalidAuthToken'));
|
||||
globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT));
|
||||
}
|
||||
|
||||
private function handleRemoveToolbarComponent(event:ToolbarButtonEvent):void {
|
||||
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Lesser General Public License as published by the Free Software
|
||||
Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-->
|
||||
|
||||
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
|
||||
xmlns:mate="http://mate.asfusion.com/"
|
||||
verticalScrollPolicy="off"
|
||||
horizontalScrollPolicy="off"
|
||||
horizontalAlign="center"
|
||||
width="250"
|
||||
title="{ResourceUtil.getInstance().getString('bbb.connection.failure')}">
|
||||
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
]]>
|
||||
</mx:Script>
|
||||
<mx:HBox width="100%" height="100%" verticalAlign="middle">
|
||||
<mx:Box
|
||||
paddingBottom="10"
|
||||
paddingTop="10"
|
||||
paddingLeft="10"
|
||||
paddingRight="10"
|
||||
>
|
||||
<mx:Image id="typeImg" source="{typeImg.getStyle('refreshImage')}" width="34" height="34" styleName="statusImageStyle" />
|
||||
</mx:Box>
|
||||
<mx:Text
|
||||
selectable="false"
|
||||
text="{ResourceUtil.getInstance().getString('bbb.connection.reconnecting')}"
|
||||
width="100%"/>
|
||||
</mx:HBox>
|
||||
</mx:Panel>
|
@ -33,6 +33,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import flash.globalization.DateTimeFormatter;
|
||||
import flash.globalization.DateTimeStyle;
|
||||
import flash.globalization.LocaleID;
|
||||
|
||||
import mx.controls.ToolTip;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.managers.PopUpManager;
|
||||
@ -58,24 +62,38 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function handleSuccessMessageEvent(e:ClientStatusEvent):void {
|
||||
if(isUniqueMessage("success", e.title, e.message)) {
|
||||
messages.push({type:"success",title:e.title,message:e.message});
|
||||
showNotification();
|
||||
}
|
||||
handleMessageEvent("success", e);
|
||||
}
|
||||
|
||||
private function handleWarningMessageEvent(e:ClientStatusEvent):void {
|
||||
if(isUniqueMessage("warning", e.title, e.message)) {
|
||||
messages.push({type:"warning",title:e.title,message:e.message});
|
||||
showNotification();
|
||||
}
|
||||
handleMessageEvent("warning", e);
|
||||
}
|
||||
|
||||
private function handleFailMessageEvent(e:ClientStatusEvent):void {
|
||||
if(isUniqueMessage("fail", e.title, e.message)) {
|
||||
messages.push({type:"fail",title:e.title,message:e.message});
|
||||
showNotification();
|
||||
handleMessageEvent("fail", e);
|
||||
}
|
||||
|
||||
private function handleMessageEvent(type:String, e:ClientStatusEvent):void {
|
||||
var index:Number = getMessageIndex(type, e.title, e.message);
|
||||
var obj:Object;
|
||||
if (index != -1) {
|
||||
obj = messages.splice(index, 1)[0];
|
||||
obj.occurrences++;
|
||||
} else {
|
||||
obj = {
|
||||
type: type,
|
||||
title: e.title,
|
||||
message: e.message,
|
||||
occurrences: 1
|
||||
};
|
||||
}
|
||||
|
||||
var dtf:DateTimeFormatter = new DateTimeFormatter(LocaleID.DEFAULT, DateTimeStyle.NONE, DateTimeStyle.MEDIUM);
|
||||
var time:String = dtf.format(new Date());
|
||||
obj.time = time;
|
||||
|
||||
messages.push(obj);
|
||||
showNotification();
|
||||
}
|
||||
|
||||
private function showNotification():void {
|
||||
@ -92,12 +110,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
}
|
||||
|
||||
private function isUniqueMessage(type:String, title:String, message:String):Boolean {
|
||||
private function getMessageIndex(type:String, title:String, message:String):int {
|
||||
for (var i:Number=0; i<messages.length; i++) {
|
||||
if (messages[i].type == type && messages[i].title == title && messages[i].message == message)
|
||||
return false;
|
||||
return i;
|
||||
}
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
private function handleButtonClick():void {
|
||||
|
@ -116,12 +116,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function noButtonClicked():void {
|
||||
userClosed = true;
|
||||
LOGGER.warn("Echo test failed.");
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.reason = "User requested.";
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.info("WebRtc Echo test failed.", logData);
|
||||
|
||||
|
||||
var dispatcher:Dispatcher = new Dispatcher();
|
||||
dispatcher.dispatchEvent(new WebRTCEchoTestEvent(WebRTCEchoTestEvent.WEBRTC_ECHO_TEST_NO_AUDIO));
|
||||
onCancelClicked();
|
||||
|
@ -28,6 +28,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import mx.events.FlexEvent;
|
||||
import org.bigbluebutton.common.events.OpenWindowEvent;
|
||||
import org.bigbluebutton.core.EventConstants;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ModuleStartedEvent;
|
||||
import org.bigbluebutton.modules.chat.events.ChatEvent;
|
||||
import org.bigbluebutton.modules.chat.events.SendPrivateChatMessageEvent;
|
||||
@ -79,6 +80,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<EventHandlers type="{TranscriptEvent.TRANSCRIPT_EVENT}" >
|
||||
<MethodInvoker generator="{ChatMessageService}" method="sendWelcomeMessage"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT}" >
|
||||
<EventAnnouncer generator="{TranscriptEvent}" type="{TranscriptEvent.LOAD_TRANSCRIPT}"/>
|
||||
</EventHandlers>
|
||||
|
||||
<Injectors target="{ChatMessageService}">
|
||||
<PropertyInjector targetKey="dispatcher" source="{scope.dispatcher}"/>
|
||||
|
@ -35,6 +35,8 @@ package org.bigbluebutton.modules.chat.services
|
||||
{
|
||||
|
||||
private static const LOGGER:ILogger = getClassLogger(MessageReceiver);
|
||||
|
||||
private var welcomed:Boolean = false;
|
||||
|
||||
public var dispatcher:IEventDispatcher;
|
||||
|
||||
@ -67,9 +69,12 @@ package org.bigbluebutton.modules.chat.services
|
||||
for (var i:int = 0; i < chats.length; i++) {
|
||||
handleChatReceivePublicMessageCommand(chats[i], true);
|
||||
}
|
||||
|
||||
var pcEvent:TranscriptEvent = new TranscriptEvent(TranscriptEvent.TRANSCRIPT_EVENT);
|
||||
dispatcher.dispatchEvent(pcEvent);
|
||||
|
||||
if (!welcomed) {
|
||||
var pcEvent:TranscriptEvent = new TranscriptEvent(TranscriptEvent.TRANSCRIPT_EVENT);
|
||||
dispatcher.dispatchEvent(pcEvent);
|
||||
welcomed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleChatReceivePublicMessageCommand(message:Object, history:Boolean = false):void {
|
||||
|
@ -62,6 +62,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mate:Listener type="{ShortcutEvent.CHANGE_FONT_COLOUR}" method="focusColourPicker" />
|
||||
<mate:Listener type="{ShortcutEvent.SEND_MESSAGE}" method="remoteSendMessage" />
|
||||
<mate:Listener type="{ShortcutEvent.CHAT_DEBUG}" method="chatDebugInfo" />
|
||||
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" receive="refreshChat(event)"/>
|
||||
|
||||
<mate:Listener type="{LockControlEvent.CHANGED_LOCK_SETTINGS}" method="lockSettingsChanged" />
|
||||
|
||||
@ -83,6 +84,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.events.UserJoinedEvent;
|
||||
import org.bigbluebutton.main.events.UserLeftEvent;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.model.users.BBBUser;
|
||||
import org.bigbluebutton.main.model.users.Conference;
|
||||
import org.bigbluebutton.modules.chat.ChatConstants;
|
||||
@ -232,6 +234,13 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
scrollToEndOfMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private function refreshChat(e:BBBEvent):void {
|
||||
if (e.payload.type == "BIGBLUEBUTTON_CONNECTION") {
|
||||
if (publicChat) chatMessages = new ChatConversation();
|
||||
}
|
||||
}
|
||||
|
||||
private function handleUserJoinedEvent(event:UserJoinedEvent):void {
|
||||
// Handle user joining so that the user can start to talk if the person rejoins
|
||||
if (!publicChat && event.userID == chatWithUserID) {
|
||||
|
34
bigbluebutton-client/src/org/bigbluebutton/modules/classyaudio/managers/ConnectionManager.as
Executable file → Normal file
34
bigbluebutton-client/src/org/bigbluebutton/modules/classyaudio/managers/ConnectionManager.as
Executable file → Normal file
@ -27,12 +27,16 @@ package org.bigbluebutton.modules.classyaudio.managers {
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.ObjectEncoding;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.modules.classyaudio.events.CallConnectedEvent;
|
||||
import org.bigbluebutton.modules.classyaudio.events.CallDisconnectedEvent;
|
||||
import org.bigbluebutton.modules.classyaudio.events.ConnectionStatusEvent;
|
||||
import org.bigbluebutton.modules.classyaudio.events.ConnectionStatusEvent;
|
||||
|
||||
public class ConnectionManager {
|
||||
|
||||
private static const LOGGER:ILogger = getClassLogger(ConnectionManager);
|
||||
|
||||
private var netConnection:NetConnection = null;
|
||||
private var username:String;
|
||||
private var uri:String;
|
||||
@ -82,42 +86,42 @@ package org.bigbluebutton.modules.classyaudio.managers {
|
||||
|
||||
switch(evt.info.code) {
|
||||
case "NetConnection.Connect.Success":
|
||||
LogUtil.debug("Successfully connected to SIP application.");
|
||||
LOGGER.debug("Successfully connected to SIP application.");
|
||||
event.status = ConnectionStatusEvent.SUCCESS;
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Failed":
|
||||
LogUtil.debug("Failed to connect to SIP application.");
|
||||
LOGGER.debug("Failed to connect to SIP application.");
|
||||
event.status = ConnectionStatusEvent.FAILED;
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Closed":
|
||||
LogUtil.debug("Connection to SIP application has closed.");
|
||||
LOGGER.debug("Connection to SIP application has closed.");
|
||||
event.status = ConnectionStatusEvent.CLOSED;
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Rejected":
|
||||
LogUtil.debug("Connection to SIP application was rejected.");
|
||||
LOGGER.debug("Connection to SIP application was rejected.");
|
||||
event.status = ConnectionStatusEvent.REJECTED;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
LogUtil.debug("Phone Module Connection Status: " + event.status);
|
||||
LogUtil.debug("Dispatching " + event.status);
|
||||
LOGGER.debug("Phone Module Connection Status: {0}", [event.status]);
|
||||
LOGGER.debug("Dispatching " + event.status);
|
||||
dispatcher.dispatchEvent(event);
|
||||
}
|
||||
|
||||
private function asyncErrorHandler(event:AsyncErrorEvent):void {
|
||||
LogUtil.debug("AsyncErrorEvent: " + event);
|
||||
LOGGER.debug("AsyncErrorEvent: {0}", [event]);
|
||||
}
|
||||
|
||||
private function securityErrorHandler(event:SecurityErrorEvent):void {
|
||||
LogUtil.debug("securityErrorHandler: " + event);
|
||||
LOGGER.debug("securityErrorHandler: {0}", [event]);
|
||||
}
|
||||
|
||||
public function call():void {
|
||||
LogUtil.debug("Calling " + room);
|
||||
LOGGER.debug("Calling {0}", [room]);
|
||||
doCall(room);
|
||||
}
|
||||
|
||||
@ -127,21 +131,21 @@ package org.bigbluebutton.modules.classyaudio.managers {
|
||||
//
|
||||
//********************************************************************************************
|
||||
public function failedToJoinVoiceConferenceCallback(msg:String):* {
|
||||
LogUtil.debug("failedToJoinVoiceConferenceCallback " + msg);
|
||||
LOGGER.debug("failedToJoinVoiceConferenceCallback {0}", [msg]);
|
||||
var event:CallDisconnectedEvent = new CallDisconnectedEvent();
|
||||
dispatcher.dispatchEvent(event);
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
public function disconnectedFromJoinVoiceConferenceCallback(msg:String):* {
|
||||
LogUtil.debug("disconnectedFromJoinVoiceConferenceCallback " + msg);
|
||||
LOGGER.debug("disconnectedFromJoinVoiceConferenceCallback {0}", [msg]);
|
||||
var event:CallDisconnectedEvent = new CallDisconnectedEvent();
|
||||
dispatcher.dispatchEvent(event);
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
public function successfullyJoinedVoiceConferenceCallback(publishName:String, playName:String, codec:String):* {
|
||||
LogUtil.debug("successfullyJoinedVoiceConferenceCallback " + publishName + " : " + playName + " : " + codec);
|
||||
LOGGER.debug("successfullyJoinedVoiceConferenceCallback {0} : {1} : {2}", [publishName, playName, codec]);
|
||||
isConnected = true;
|
||||
var event:CallConnectedEvent = new CallConnectedEvent();
|
||||
event.publishStreamName = publishName;
|
||||
@ -156,7 +160,7 @@ package org.bigbluebutton.modules.classyaudio.managers {
|
||||
//
|
||||
//********************************************************************************************
|
||||
public function doCall(dialStr:String):void {
|
||||
LogUtil.debug("Calling " + dialStr);
|
||||
LOGGER.debug("Calling ", [dialStr]);
|
||||
netConnection.call("voiceconf.call", null, "default", username, dialStr);
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
|
||||
public function startSharing(uri:String , useTLS:Boolean , room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
|
||||
LOGGER.debug("DS:PublishWindowManager::opening desk share window, autostart={0} autoFullScreen={1}", [autoStart, autoFullScreen]);
|
||||
|
||||
shareWindow = new DesktopPublishWindow();
|
||||
shareWindow.initWindow(service.getConnection(), uri , useTLS , room, autoStart, autoFullScreen);
|
||||
shareWindow.visible = true;
|
||||
|
@ -83,7 +83,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{ViewWindowEvent.CLOSE}">
|
||||
<MethodInvoker generator="{DeskshareManager}" method="handleViewWindowCloseEvent"/>
|
||||
<MethodInvoker generator="{DeskshareManager}" method="handleViewWindowCloseEvent"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{ModuleEvent.STOP}">
|
||||
|
@ -21,6 +21,7 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.AsyncErrorEvent;
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.TimerEvent;
|
||||
@ -30,20 +31,23 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
import flash.net.SharedObject;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import mx.utils.ObjectUtil;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.ReconnectionManager;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.modules.deskshare.events.AppletStartedEvent;
|
||||
import org.bigbluebutton.modules.deskshare.events.CursorEvent;
|
||||
import org.bigbluebutton.modules.deskshare.events.ViewStreamEvent;
|
||||
|
||||
|
||||
|
||||
public class Connection {
|
||||
private static const LOGGER:ILogger = getClassLogger(Connection);
|
||||
private static const LOGGER:ILogger = getClassLogger(Connection);
|
||||
|
||||
private var nc:NetConnection;
|
||||
private var uri:String;
|
||||
private const connectionTimeout:int = 5000;
|
||||
private var nc:NetConnection;
|
||||
private var uri:String;
|
||||
private var retryTimer:Timer = null;
|
||||
private var retryCount:int = 0;
|
||||
private const MAX_RETRIES:int = 5;
|
||||
@ -52,7 +56,10 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
private var width:Number;
|
||||
private var height:Number;
|
||||
private var room:String;
|
||||
|
||||
private var logoutOnUserCommand:Boolean = false;
|
||||
private var reconnecting:Boolean = false;
|
||||
private var wasPresenterBeforeDisconnect:Boolean = false;
|
||||
|
||||
private var dispatcher:Dispatcher = new Dispatcher();
|
||||
|
||||
public function Connection(room:String) {
|
||||
@ -90,6 +97,8 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
nc.objectEncoding = ObjectEncoding.AMF0;
|
||||
nc.client = this;
|
||||
|
||||
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler);
|
||||
nc.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler);
|
||||
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
|
||||
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
|
||||
|
||||
@ -110,12 +119,6 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
}
|
||||
|
||||
nc.connect(getURI(), UsersUtil.getInternalMeetingID());
|
||||
|
||||
if (!retry) {
|
||||
retryTimer = new Timer(connectionTimeout, 1);
|
||||
retryTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTimeoutHandler);
|
||||
retryTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
private function connectTimeoutHandler(e:TimerEvent):void {
|
||||
@ -180,6 +183,11 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
|
||||
switch(event.info.code){
|
||||
case "NetConnection.Connect.Failed":
|
||||
if (reconnecting) {
|
||||
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
|
||||
attemptFailedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptFailedEvent);
|
||||
}
|
||||
ce.status = ConnectionEvent.FAILED;
|
||||
|
||||
dispatcher.dispatchEvent(ce);
|
||||
@ -187,6 +195,17 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
|
||||
case "NetConnection.Connect.Success":
|
||||
ce.status = ConnectionEvent.SUCCESS;
|
||||
if (reconnecting) {
|
||||
reconnecting = false;
|
||||
if (wasPresenterBeforeDisconnect) {
|
||||
wasPresenterBeforeDisconnect = false;
|
||||
stopSharingDesktop(room, room)
|
||||
}
|
||||
|
||||
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
|
||||
attemptSucceeded.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptSucceeded);
|
||||
}
|
||||
dispatcher.dispatchEvent(ce);
|
||||
connectionSuccessHandler();
|
||||
break;
|
||||
@ -199,7 +218,24 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
case "NetConnection.Connect.Closed":
|
||||
LOGGER.debug("Deskshare connection closed.");
|
||||
ce.status = ConnectionEvent.CLOSED;
|
||||
// dispatcher.dispatchEvent(ce);
|
||||
if (UsersUtil.amIPresenter()) {
|
||||
// Let's keep our presenter status before disconnected. We can't
|
||||
// tell the other user's to stop desktop sharing as our connection is broken. (ralam july 24, 2015)
|
||||
wasPresenterBeforeDisconnect = true;
|
||||
|
||||
} else {
|
||||
stopViewing();
|
||||
}
|
||||
|
||||
if (!logoutOnUserCommand) {
|
||||
reconnecting = true;
|
||||
|
||||
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
|
||||
disconnectedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
|
||||
disconnectedEvent.payload.callback = connect;
|
||||
disconnectedEvent.payload.callbackParameters = [];
|
||||
dispatcher.dispatchEvent(disconnectedEvent);
|
||||
}
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.InvalidApp":
|
||||
@ -253,6 +289,7 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
}
|
||||
|
||||
public function disconnect():void{
|
||||
logoutOnUserCommand = true;
|
||||
if (nc != null) nc.close();
|
||||
}
|
||||
|
||||
@ -261,10 +298,20 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
var deskSOName:String = room + "-deskSO";
|
||||
deskSO = SharedObject.getRemote(deskSOName, uri, false);
|
||||
deskSO.client = this;
|
||||
deskSO.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler);
|
||||
deskSO.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler);
|
||||
deskSO.connect(nc);
|
||||
|
||||
checkIfStreamIsPublishing(room);
|
||||
}
|
||||
|
||||
private function debugNetStatusHandler(e:NetStatusEvent):void {
|
||||
LOGGER.debug("netStatusHandler target={0} info={1}", [e.target, ObjectUtil.toString(e.info)]);
|
||||
}
|
||||
|
||||
private function debugAsyncErrorHandler(e:AsyncErrorEvent):void {
|
||||
LOGGER.debug("asyncErrorHandler target={0} info={1}", [e.target, e.text]);
|
||||
}
|
||||
|
||||
public function getConnection():NetConnection{
|
||||
return nc;
|
||||
@ -285,10 +332,13 @@ package org.bigbluebutton.modules.deskshare.services.red5
|
||||
*/
|
||||
public function appletStarted(videoWidth:Number, videoHeight:Number):void{
|
||||
LOGGER.debug("Got applet started");
|
||||
var event:AppletStartedEvent = new AppletStartedEvent();
|
||||
event.videoWidth = videoWidth;
|
||||
event.videoHeight = videoHeight;
|
||||
dispatcher.dispatchEvent(event);
|
||||
if (nc != null && nc.connected) {
|
||||
var event:AppletStartedEvent = new AppletStartedEvent();
|
||||
event.videoWidth = videoWidth;
|
||||
event.videoHeight = videoHeight;
|
||||
dispatcher.dispatchEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,6 +103,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
[Bindable] private var baseIndex:int;
|
||||
[Bindable] private var dsOptions:DeskshareOptions;
|
||||
|
||||
private var calledStopApplet:Boolean = false;
|
||||
|
||||
private function init():void {
|
||||
dsOptions = new DeskshareOptions();
|
||||
baseIndex = dsOptions.baseTabIndex;
|
||||
@ -246,6 +248,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function onAppletStart(event:AppletStartedEvent):void{
|
||||
if (!connection.connected) return;
|
||||
|
||||
LOGGER.debug("DeskShareWindow::onAppletStart");
|
||||
startPreviewStream(connection, room, event.videoWidth, event.videoHeight);
|
||||
var streamEvent:StreamEvent = new StreamEvent(StreamEvent.START);
|
||||
@ -343,15 +347,24 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
private function closeWindow():void {
|
||||
ExternalInterface.call("stopApplet");
|
||||
dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
|
||||
}
|
||||
|
||||
private function restartJava():void {
|
||||
ExternalInterface.call("stopApplet");
|
||||
shareScreen(sharingFullScreen);
|
||||
}
|
||||
private function callStopApplet():void {
|
||||
if (!calledStopApplet) {
|
||||
calledStopApplet = true;
|
||||
LOGGER.debug("Calling stopApplet in callStopApplet()");
|
||||
ExternalInterface.call("stopApplet");
|
||||
}
|
||||
}
|
||||
private function closeWindow():void {
|
||||
LOGGER.debug("Calling stopApplet in closeWindow()");
|
||||
callStopApplet();
|
||||
dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
|
||||
}
|
||||
|
||||
private function restartJava():void {
|
||||
LOGGER.debug("Calling stopApplet in restartJava()");
|
||||
callStopApplet();
|
||||
shareScreen(sharingFullScreen);
|
||||
}
|
||||
|
||||
private function handleDeskshareAppletLaunchedEvent(e:DeskshareAppletLaunchedEvent):void {
|
||||
if (javaTimer && javaTimer.running) {
|
||||
|
@ -187,6 +187,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function onNetStatus(e:NetStatusEvent):void{
|
||||
LOGGER.debug("onNetStatus info={0}", [e.info.text]);
|
||||
|
||||
switch(e.info.code){
|
||||
case "NetStream.Play.Start":
|
||||
LOGGER.debug("NetStream.Publish.Start for broadcast stream {0}", [stream]);
|
||||
|
@ -12,11 +12,13 @@ package org.bigbluebutton.modules.phone.events
|
||||
public static const NETWORK_CHANGE:String = "flash voice connection status network change event";
|
||||
|
||||
public var status:String;
|
||||
public var reconnecting:Boolean;
|
||||
|
||||
public function FlashVoiceConnectionStatusEvent(connStatus:String)
|
||||
public function FlashVoiceConnectionStatusEvent(connStatus:String, isReconnecting:Boolean = false)
|
||||
{
|
||||
super(CONN_STATUS, true, false);
|
||||
status = connStatus;
|
||||
reconnecting = isReconnecting;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,7 +30,9 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.ReconnectionManager;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashCallConnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashCallDisconnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashVoiceConnectionStatusEvent;
|
||||
@ -49,6 +51,8 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
|
||||
private var registered:Boolean = false;
|
||||
private var closedByUser:Boolean = false;
|
||||
|
||||
private var reconnecting:Boolean = false;
|
||||
|
||||
private var dispatcher:Dispatcher;
|
||||
|
||||
@ -99,11 +103,39 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
}
|
||||
}
|
||||
|
||||
private function handleConnectionSuccess():void {
|
||||
if (reconnecting) {
|
||||
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
|
||||
attemptSucceeded.payload.type = ReconnectionManager.SIP_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptSucceeded);
|
||||
}
|
||||
dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.CONNECTED));
|
||||
reconnecting = false;
|
||||
}
|
||||
|
||||
private function handleConnectionFailed():void {
|
||||
if (reconnecting) {
|
||||
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
|
||||
attemptFailedEvent.payload.type = ReconnectionManager.SIP_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptFailedEvent);
|
||||
}
|
||||
dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.FAILED, reconnecting));
|
||||
}
|
||||
|
||||
private function handleConnectionClosed():void {
|
||||
if (!closedByUser) {
|
||||
reconnecting = true;
|
||||
|
||||
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
|
||||
disconnectedEvent.payload.type = ReconnectionManager.SIP_CONNECTION;
|
||||
disconnectedEvent.payload.callback = connect;
|
||||
disconnectedEvent.payload.callbackParameters = [];
|
||||
dispatcher.dispatchEvent(disconnectedEvent);
|
||||
|
||||
dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.DISCONNECTED));
|
||||
}
|
||||
}
|
||||
|
||||
private function netStatus (event:NetStatusEvent ):void {
|
||||
var info : Object = event.info;
|
||||
var statusCode : String = info.code;
|
||||
@ -115,12 +147,12 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
case "NetConnection.Connect.Success":
|
||||
LOGGER.debug("Connection success");
|
||||
JSLog.debug("Successfully connected to BBB Voice", logData);
|
||||
dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.CONNECTED));
|
||||
handleConnectionSuccess();
|
||||
break;
|
||||
case "NetConnection.Connect.Failed":
|
||||
LOGGER.debug("Connection failed");
|
||||
JSLog.error("Failed to connect to BBB Voice", logData);
|
||||
dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.FAILED));
|
||||
handleConnectionFailed();
|
||||
break;
|
||||
case "NetConnection.Connect.NetworkChange":
|
||||
LOGGER.debug("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
|
||||
|
@ -10,6 +10,7 @@
|
||||
import org.as3commons.logging.util.jsonXify;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.modules.phone.PhoneOptions;
|
||||
import org.bigbluebutton.modules.phone.events.FlashCallConnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashCallDisconnectedEvent;
|
||||
@ -368,23 +369,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
public function handleFlashVoiceConnected():void {
|
||||
switch (state) {
|
||||
case JOIN_VOICE_CONFERENCE:
|
||||
callIntoVoiceConference();
|
||||
break;
|
||||
case DO_ECHO_TEST:
|
||||
callIntoEchoTest();
|
||||
break;
|
||||
case CALL_TO_LISTEN_ONLY_STREAM:
|
||||
callToListenOnlyStream();
|
||||
break;
|
||||
case ON_LISTEN_ONLY_STREAM:
|
||||
callToListenOnlyStream();
|
||||
break;
|
||||
case IN_CONFERENCE:
|
||||
LOGGER.debug("Reconnected while transmiting mic. Automatic retransmission not implemented.");
|
||||
state = INITED;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGGER.debug("unhandled state: {0}", [state]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function handleFlashVoiceConnectionStatusEvent(event:FlashVoiceConnectionStatusEvent):void {
|
||||
LOGGER.debug("Connection status event. status=[{0}]", [event.status]);
|
||||
if (event.status == FlashVoiceConnectionStatusEvent.CONNECTED) {
|
||||
switch (state) {
|
||||
case JOIN_VOICE_CONFERENCE:
|
||||
callIntoVoiceConference();
|
||||
break;
|
||||
case DO_ECHO_TEST:
|
||||
callIntoEchoTest();
|
||||
break;
|
||||
case CALL_TO_LISTEN_ONLY_STREAM:
|
||||
callToListenOnlyStream();
|
||||
break;
|
||||
default:
|
||||
LOGGER.debug("unhandled state: {0}", [state]);
|
||||
break;
|
||||
}
|
||||
switch (event.status) {
|
||||
case FlashVoiceConnectionStatusEvent.CONNECTED:
|
||||
handleFlashVoiceConnected();
|
||||
break;
|
||||
|
||||
case FlashVoiceConnectionStatusEvent.FAILED:
|
||||
case FlashVoiceConnectionStatusEvent.DISCONNECTED:
|
||||
// If reconnection is under way the state should de kept
|
||||
if(!event.reconnecting) {
|
||||
state = INITED;
|
||||
}
|
||||
dispatcher.dispatchEvent(new FlashLeftVoiceConferenceEvent());
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGGER.debug("unhandled state: {0}", [state]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,5 +419,12 @@
|
||||
usingFlash = true;
|
||||
startCall(true);
|
||||
}
|
||||
|
||||
public function handleFlashLeftVoiceConference():void {
|
||||
if (isConnected()) {
|
||||
streamManager.stopStreams();
|
||||
connectionManager.disconnect(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ package org.bigbluebutton.modules.phone.managers
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.main.api.JSAPI;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.modules.phone.PhoneModel;
|
||||
import org.bigbluebutton.modules.phone.PhoneOptions;
|
||||
@ -188,6 +189,11 @@ package org.bigbluebutton.modules.phone.managers
|
||||
errorString = ResourceUtil.getInstance().getString("bbb.webrtcWarning.failedError.unknown", [event.errorCode]);
|
||||
}
|
||||
|
||||
var logData:Object = new Object();
|
||||
logData.reason = errorString;
|
||||
logData.user = UsersUtil.getUserData();
|
||||
JSLog.warn("WebRtc Echo test failed.", logData);
|
||||
|
||||
sendWebRTCAlert(ResourceUtil.getInstance().getString("bbb.webrtcWarning.title"), ResourceUtil.getInstance().getString("bbb.webrtcWarning.message", [errorString]), errorString);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.phone.events.FlashCallDisconnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashEchoTestHasAudioEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashEchoTestNoAudioEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashLeftVoiceConferenceEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashStartEchoTestCommand;
|
||||
import org.bigbluebutton.modules.phone.events.FlashStopEchoTestCommand;
|
||||
import org.bigbluebutton.modules.phone.events.FlashVoiceConnectionStatusEvent;
|
||||
@ -106,7 +107,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<EventHandlers type="{LeaveVoiceConferenceCommand.LEAVE_VOICE_CONF}">
|
||||
<MethodInvoker generator="{FlashCallManager}" method="handleLeaveVoiceConferenceCommand" arguments="{event}"/>
|
||||
</EventHandlers>
|
||||
|
||||
|
||||
<EventHandlers type="{FlashLeftVoiceConferenceEvent.LEFT_VOICE_CONFERENCE}">
|
||||
<MethodInvoker generator="{FlashCallManager}" method="handleFlashLeftVoiceConference"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{UseFlashModeCommand.USE_FLASH_MODE}">
|
||||
<MethodInvoker generator="{FlashCallManager}" method="handleUseFlashModeCommand"/>
|
||||
</EventHandlers>
|
||||
|
@ -1,16 +1,19 @@
|
||||
package org.bigbluebutton.modules.polling.model
|
||||
{
|
||||
import mx.collections.ArrayCollection;
|
||||
|
||||
|
||||
public class SimplePollResult
|
||||
{
|
||||
private var _id:String;
|
||||
private var _answers: Array;
|
||||
|
||||
public function SimplePollResult(id:String, answers:Array)
|
||||
private var _numRespondents: int;
|
||||
private var _numResponders: int;
|
||||
|
||||
public function SimplePollResult(id:String, answers:Array, numRespondents: int, numResponders: int)
|
||||
{
|
||||
_id = id;
|
||||
_answers = answers;
|
||||
_numRespondents = numRespondents;
|
||||
_numResponders = numResponders;
|
||||
}
|
||||
|
||||
public function get id():String {
|
||||
@ -22,5 +25,12 @@ package org.bigbluebutton.modules.polling.model
|
||||
return _answers;
|
||||
}
|
||||
|
||||
public function get numRespondents():int {
|
||||
return _numRespondents;
|
||||
}
|
||||
|
||||
public function get numResponders():int {
|
||||
return _numResponders;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,13 @@ package org.bigbluebutton.modules.polling.service
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.accessibility.Accessibility;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.modules.chat.ChatConstants;
|
||||
import org.bigbluebutton.modules.chat.events.PublicChatMessageEvent;
|
||||
import org.bigbluebutton.modules.chat.vo.ChatMessageVO;
|
||||
import org.bigbluebutton.modules.polling.events.PollShowResultEvent;
|
||||
import org.bigbluebutton.modules.polling.events.PollStartedEvent;
|
||||
import org.bigbluebutton.modules.polling.events.PollStoppedEvent;
|
||||
@ -13,6 +18,7 @@ package org.bigbluebutton.modules.polling.service
|
||||
import org.bigbluebutton.modules.polling.model.SimpleAnswerResult;
|
||||
import org.bigbluebutton.modules.polling.model.SimplePoll;
|
||||
import org.bigbluebutton.modules.polling.model.SimplePollResult;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
public class PollDataProcessor
|
||||
{
|
||||
@ -61,19 +67,43 @@ package org.bigbluebutton.modules.polling.service
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
if (map.hasOwnProperty("poll")) {
|
||||
var poll:Object = map.poll;
|
||||
if (poll.hasOwnProperty("id") && poll.hasOwnProperty("answers")) {
|
||||
if (poll.hasOwnProperty("id") && poll.hasOwnProperty("answers")
|
||||
&& poll.hasOwnProperty("num_responders") && poll.hasOwnProperty("num_respondents")) {
|
||||
var pollId:String = poll.id;
|
||||
|
||||
var answers:Array = poll.answers as Array;
|
||||
var accessibleAnswers:String = ResourceUtil.getInstance().getString("bbb.polling.results.accessible.header") + "<br />";
|
||||
|
||||
var ans:Array = new Array();
|
||||
|
||||
for (var j:int = 0; j < answers.length; j++) {
|
||||
var a:Object = answers[j];
|
||||
ans.push(new SimpleAnswerResult(a.id as Number, a.key, a.num_votes as Number));
|
||||
accessibleAnswers += ResourceUtil.getInstance().getString("bbb.polling.results.accessible.answer", [ResourceUtil.getInstance().getString("bbb.polling.answer."+a.key), a.num_votes]) + "<br />";
|
||||
}
|
||||
|
||||
dispatcher.dispatchEvent(new PollShowResultEvent(new SimplePollResult(pollId, ans)));
|
||||
var numRespondents:Number = poll.num_respondents;
|
||||
var numResponders:Number = poll.num_responders;
|
||||
|
||||
dispatcher.dispatchEvent(new PollShowResultEvent(new SimplePollResult(pollId, ans, numRespondents, numResponders)));
|
||||
|
||||
if (Accessibility.active) {
|
||||
var pollResultMessage:ChatMessageVO = new ChatMessageVO();
|
||||
pollResultMessage.chatType = ChatConstants.PUBLIC_CHAT;
|
||||
pollResultMessage.fromUserID = ResourceUtil.getInstance().getString("bbb.chat.chatMessage.systemMessage");
|
||||
pollResultMessage.fromUsername = ResourceUtil.getInstance().getString("bbb.chat.chatMessage.systemMessage");
|
||||
pollResultMessage.fromColor = "86187";
|
||||
pollResultMessage.fromTime = new Date().getTime();
|
||||
pollResultMessage.fromTimezoneOffset = new Date().getTimezoneOffset();
|
||||
pollResultMessage.toUserID = ResourceUtil.getInstance().getString("bbb.chat.chatMessage.systemMessage");
|
||||
pollResultMessage.toUsername = ResourceUtil.getInstance().getString("bbb.chat.chatMessage.systemMessage");
|
||||
pollResultMessage.message = accessibleAnswers;
|
||||
|
||||
var pollResultMessageEvent:PublicChatMessageEvent = new PublicChatMessageEvent(PublicChatMessageEvent.PUBLIC_CHAT_MESSAGE_EVENT);
|
||||
pollResultMessageEvent.message = pollResultMessage;
|
||||
pollResultMessageEvent.history = false;
|
||||
dispatcher.dispatchEvent(pollResultMessageEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,7 +113,8 @@ package org.bigbluebutton.modules.polling.service
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
if (map.hasOwnProperty("poll")) {
|
||||
var poll:Object = map.poll;
|
||||
if (poll.hasOwnProperty("id") && poll.hasOwnProperty("answers")) {
|
||||
if (poll.hasOwnProperty("id") && poll.hasOwnProperty("answers")
|
||||
&& poll.hasOwnProperty("num_responders") && poll.hasOwnProperty("num_respondents")) {
|
||||
var pollId:String = poll.id;
|
||||
|
||||
var answers:Array = poll.answers as Array;
|
||||
@ -95,7 +126,10 @@ package org.bigbluebutton.modules.polling.service
|
||||
ans.push(new SimpleAnswerResult(a.id as Number, a.key, a.num_votes as Number));
|
||||
}
|
||||
|
||||
dispatcher.dispatchEvent(new PollVotedEvent(new SimplePollResult(pollId, ans)));
|
||||
var numRespondents:Number = poll.num_respondents;
|
||||
var numResponders:Number = poll.num_responders;
|
||||
|
||||
dispatcher.dispatchEvent(new PollVotedEvent(new SimplePollResult(pollId, ans, numRespondents, numResponders)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +233,7 @@ package org.bigbluebutton.modules.polling.views
|
||||
private function findFontSize(textField:TextField, defaultSize:Number):int {
|
||||
var tFormat:TextFormat = new TextFormat();
|
||||
tFormat.size = defaultSize;
|
||||
tFormat.font = "arial";
|
||||
tFormat.align = TextFormatAlign.CENTER;
|
||||
textField.setTextFormat(tFormat);
|
||||
var size:Number = defaultSize;
|
||||
|
@ -8,6 +8,10 @@ package org.bigbluebutton.modules.polling.views
|
||||
import mx.containers.HBox;
|
||||
import mx.containers.TitleWindow;
|
||||
import mx.controls.Button;
|
||||
import mx.controls.HRule;
|
||||
import mx.controls.Label;
|
||||
import mx.controls.TextArea;
|
||||
import mx.core.ScrollPolicy;
|
||||
import mx.managers.PopUpManager;
|
||||
|
||||
import org.bigbluebutton.modules.polling.events.PollVotedEvent;
|
||||
@ -21,6 +25,7 @@ package org.bigbluebutton.modules.polling.views
|
||||
public class PollResultsModal extends TitleWindow {
|
||||
private var _voteListener:Listener;
|
||||
|
||||
private var _respondersLabel:Label;
|
||||
private var _pollGraphic:PollGraphic;
|
||||
private var _publishBtn:Button;
|
||||
private var _closeBtn:Button;
|
||||
@ -28,23 +33,33 @@ package org.bigbluebutton.modules.polling.views
|
||||
public function PollResultsModal() {
|
||||
super();
|
||||
|
||||
styleName = "micSettingsWindowStyle";
|
||||
width = 300;
|
||||
height = 300;
|
||||
height = 300;
|
||||
setStyle("verticalGap", 15);
|
||||
showCloseButton = false;
|
||||
layout = "vertical";
|
||||
setStyle("horizontalAlign", "center");
|
||||
setStyle("verticalAlign", "middle");
|
||||
|
||||
var modalTitle:TextArea = new TextArea();
|
||||
modalTitle.setStyle("borderSkin", null);
|
||||
modalTitle.verticalScrollPolicy = ScrollPolicy.OFF;
|
||||
modalTitle.editable = false;
|
||||
modalTitle.text = ResourceUtil.getInstance().getString('bbb.polling.pollModal.title');
|
||||
modalTitle.styleName = "micSettingsWindowTitleStyle";
|
||||
modalTitle.percentWidth = 100;
|
||||
modalTitle.height = 25;
|
||||
addChild(modalTitle);
|
||||
|
||||
var topBox:HBox = new HBox();
|
||||
_publishBtn = new Button();
|
||||
_publishBtn.label = ResourceUtil.getInstance().getString('bbb.polling.publishButton.label');
|
||||
_publishBtn.addEventListener(MouseEvent.CLICK, handlePublishClick);
|
||||
topBox.addChild(_publishBtn);
|
||||
_closeBtn = new Button();
|
||||
_closeBtn.label = ResourceUtil.getInstance().getString('bbb.polling.closeButton.label');
|
||||
_closeBtn.addEventListener(MouseEvent.CLICK, handleCloseClick);
|
||||
topBox.addChild(_closeBtn);
|
||||
addChild(topBox);
|
||||
var hrule:HRule = new HRule();
|
||||
hrule.percentWidth = 100;
|
||||
addChild(hrule);
|
||||
|
||||
_respondersLabel = new Label();
|
||||
_respondersLabel.styleName = "pollResondersLabelStyle";
|
||||
_respondersLabel.text = " ";// ResourceUtil.getInstance().getString('bbb.polling.respondersLabel.novotes');
|
||||
addChild(_respondersLabel);
|
||||
|
||||
_pollGraphic = new PollGraphic();
|
||||
_pollGraphic.data = null;
|
||||
@ -52,6 +67,23 @@ package org.bigbluebutton.modules.polling.views
|
||||
_pollGraphic.minWidth = 130;
|
||||
addChild(_pollGraphic);
|
||||
|
||||
hrule = new HRule();
|
||||
hrule.percentWidth = 100;
|
||||
addChild(hrule);
|
||||
|
||||
var botBox:HBox = new HBox();
|
||||
botBox.setStyle("gap", 10);
|
||||
|
||||
_publishBtn = new Button();
|
||||
_publishBtn.label = ResourceUtil.getInstance().getString('bbb.polling.publishButton.label');
|
||||
_publishBtn.addEventListener(MouseEvent.CLICK, handlePublishClick);
|
||||
botBox.addChild(_publishBtn);
|
||||
_closeBtn = new Button();
|
||||
_closeBtn.label = ResourceUtil.getInstance().getString('bbb.polling.closeButton.label');
|
||||
_closeBtn.addEventListener(MouseEvent.CLICK, handleCloseClick);
|
||||
botBox.addChild(_closeBtn);
|
||||
addChild(botBox);
|
||||
|
||||
_voteListener = new Listener();
|
||||
_voteListener.type = PollVotedEvent.POLL_VOTED;
|
||||
_voteListener.method = handlePollVotedEvent;
|
||||
@ -62,14 +94,14 @@ package org.bigbluebutton.modules.polling.views
|
||||
var answers:Array = poll.answers;
|
||||
for (var j:int = 0; j < answers.length; j++) {
|
||||
var a:SimpleAnswer = answers[j] as SimpleAnswer;
|
||||
resultData.push({a:a.key, v:0});
|
||||
resultData.push({a:ResourceUtil.getInstance().getString('bbb.polling.answer.' + a.key), v:0});
|
||||
}
|
||||
|
||||
_pollGraphic.data = resultData;
|
||||
_pollGraphic.height = ((23+10)*_pollGraphic.data.length+10);
|
||||
_pollGraphic.minHeight = ((16+10)*_pollGraphic.data.length+10);
|
||||
|
||||
height = _pollGraphic.height + 140;
|
||||
height = _pollGraphic.height + 220;
|
||||
}
|
||||
|
||||
private function handlePollVotedEvent(e:PollVotedEvent):void {
|
||||
@ -77,10 +109,11 @@ package org.bigbluebutton.modules.polling.views
|
||||
var answers:Array = e.result.answers;
|
||||
for (var j:int = 0; j < answers.length; j++) {
|
||||
var a:SimpleAnswerResult = answers[j] as SimpleAnswerResult;
|
||||
resultData.push({a:a.key, v:a.numVotes});
|
||||
resultData.push({a:ResourceUtil.getInstance().getString('bbb.polling.answer.' + a.key), v:a.numVotes});
|
||||
}
|
||||
|
||||
_pollGraphic.data = resultData;
|
||||
_respondersLabel.text = ResourceUtil.getInstance().getString('bbb.polling.respondersLabel.text', [e.result.numResponders + "/" + e.result.numRespondents]);
|
||||
}
|
||||
|
||||
private function handlePublishClick(e:MouseEvent):void {
|
||||
|
@ -0,0 +1,43 @@
|
||||
package org.bigbluebutton.modules.polling.views {
|
||||
import com.asfusion.mate.events.Listener;
|
||||
|
||||
import mx.controls.Button;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.modules.present.events.PageLoadedEvent;
|
||||
import org.bigbluebutton.modules.present.model.Page;
|
||||
import org.bigbluebutton.modules.present.model.PresentationModel;
|
||||
|
||||
public class QuickPollButton extends Button {
|
||||
private static const LOGGER:ILogger = getClassLogger(QuickPollButton);
|
||||
|
||||
public function QuickPollButton() {
|
||||
super();
|
||||
visible = false;
|
||||
|
||||
var listener:Listener = new Listener();
|
||||
listener.type = PageLoadedEvent.PAGE_LOADED_EVENT;
|
||||
listener.method = handlePageLoadedEvent;
|
||||
}
|
||||
|
||||
private function handlePageLoadedEvent(e:PageLoadedEvent):void {
|
||||
var page:Page = PresentationModel.getInstance().getPage(e.pageId);
|
||||
if (page != null) {
|
||||
parseSlideText(page.txtData);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSlideText(text:String):void {
|
||||
var regEx:RegExp = new RegExp("\n[^\s]+[\.\)]", "g");
|
||||
var matchedArray:Array = text.match(regEx);
|
||||
LOGGER.debug("Parse Result: {0} {1}", [matchedArray.length, matchedArray.join(" ")]);
|
||||
if (matchedArray.length > 2) {
|
||||
label = "A-" + (matchedArray.length < 8 ? matchedArray.length : 7);
|
||||
visible = true;
|
||||
} else {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<![CDATA[
|
||||
import mx.events.FlexEvent;
|
||||
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
|
||||
import org.bigbluebutton.modules.present.api.PresentationAPI;
|
||||
import org.bigbluebutton.modules.present.business.PresentProxy;
|
||||
@ -71,6 +72,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<MethodInvoker generator="{PresentManager}" method="handleStopModuleEvent" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT}" >
|
||||
<MethodInvoker generator="{PresentProxy}" method="getCurrentPresentationInfo" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{UploadEvent.OPEN_UPLOAD_WINDOW}" >
|
||||
<MethodInvoker generator="{PresentManager}" method="handleOpenUploadWindow" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
@ -11,7 +11,6 @@ package org.bigbluebutton.modules.present.model
|
||||
|
||||
private static var instance:PresentationModel = null;
|
||||
|
||||
private var _pages:ArrayCollection = new ArrayCollection();
|
||||
private var _presentations:ArrayCollection = new ArrayCollection();
|
||||
private var _presenter: Presenter;
|
||||
|
||||
@ -48,10 +47,6 @@ package org.bigbluebutton.modules.present.model
|
||||
return _presenter;
|
||||
}
|
||||
|
||||
public function addPage(page: Page):void {
|
||||
_pages.addItem(page);
|
||||
}
|
||||
|
||||
public function addPresentation(p: Presentation):void {
|
||||
_presentations.addItem(p);
|
||||
}
|
||||
@ -68,6 +63,10 @@ package org.bigbluebutton.modules.present.model
|
||||
return null;
|
||||
}
|
||||
|
||||
public function removeAllPresentations():void {
|
||||
_presentations.removeAll();
|
||||
}
|
||||
|
||||
public function replacePresentation(p: Presentation):void {
|
||||
var oldPres:Presentation = removePresentation(p.id);
|
||||
if (oldPres == null) {
|
||||
|
@ -150,6 +150,10 @@ package org.bigbluebutton.modules.present.services
|
||||
LOGGER.debug("Switching presentation but presentation [{0}] is not current [{0}]", [presVO.id, presVO.isCurrent()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAllPresentations():void {
|
||||
model.removeAllPresentations();
|
||||
}
|
||||
|
||||
public function removePresentation(presentationID:String):void {
|
||||
var removedEvent:RemovePresentationEvent = new RemovePresentationEvent(RemovePresentationEvent.PRESENTATION_REMOVED_EVENT);
|
||||
|
@ -294,6 +294,7 @@ package org.bigbluebutton.modules.present.services.messaging
|
||||
presos.addItem(presVO);
|
||||
}
|
||||
|
||||
service.removeAllPresentations();
|
||||
service.addPresentations(presos);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
width="{DEFAULT_WINDOW_WIDTH}" height="{DEFAULT_WINDOW_HEIGHT}"
|
||||
x="{DEFAULT_X_POSITION}" y="{DEFAULT_Y_POSITION}"
|
||||
title="{ResourceUtil.getInstance().getString('bbb.presentation.titleWithPres',[currentPresentation])}"
|
||||
xmlns:views="org.bigbluebutton.modules.present.ui.views.*"
|
||||
xmlns:views="org.bigbluebutton.modules.present.ui.views.*"
|
||||
xmlns:poll="org.bigbluebutton.modules.polling.views.*"
|
||||
>
|
||||
|
||||
<mate:Dispatcher id="globalDispatcher" />
|
||||
@ -616,6 +617,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
}
|
||||
|
||||
private function quickPollClicked(e:MouseEvent):void {
|
||||
sendStartPollEvent(Button(e.target).label);
|
||||
}
|
||||
|
||||
private function onPollStartButtonClicked():void {
|
||||
openPollTypeMenu();
|
||||
}
|
||||
@ -640,39 +645,45 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
}
|
||||
|
||||
private function sendStartPollEvent(pollType:String):void {
|
||||
// Let's reset the page to display full size so we can display the result
|
||||
// on the bottom right-corner.
|
||||
onResetZoom();
|
||||
var dispatcher:Dispatcher = new Dispatcher();
|
||||
dispatchEvent(new StartPollEvent(pollType));
|
||||
}
|
||||
|
||||
private function menuClickHandler(e:MenuEvent):void {
|
||||
if(pollMenuData[e.index] != undefined) {
|
||||
// start the requested poll
|
||||
var pollEvent:StartPollEvent = null;
|
||||
switch (e.index) {
|
||||
case 0:
|
||||
pollEvent = new StartPollEvent("YN");
|
||||
sendStartPollEvent("YN");
|
||||
break;
|
||||
case 1:
|
||||
pollEvent = new StartPollEvent("TF");
|
||||
sendStartPollEvent("TF");
|
||||
break;
|
||||
case 2:
|
||||
pollEvent = new StartPollEvent("A-2");
|
||||
sendStartPollEvent("A-2");
|
||||
break;
|
||||
case 3:
|
||||
pollEvent = new StartPollEvent("A-3");
|
||||
sendStartPollEvent("A-3");
|
||||
break;
|
||||
case 4:
|
||||
pollEvent = new StartPollEvent("A-4");
|
||||
sendStartPollEvent("A-4");
|
||||
break;
|
||||
case 5:
|
||||
pollEvent = new StartPollEvent("A-5");
|
||||
sendStartPollEvent("A-5");
|
||||
break;
|
||||
case 6:
|
||||
pollEvent = new StartPollEvent("A-6");
|
||||
sendStartPollEvent("A-6");
|
||||
break;
|
||||
case 7:
|
||||
pollEvent = new StartPollEvent("A-7");
|
||||
sendStartPollEvent("A-7");
|
||||
break;
|
||||
}
|
||||
var dispatcher:Dispatcher = new Dispatcher();
|
||||
//dispatcher.
|
||||
dispatchEvent(pollEvent);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -759,30 +770,32 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<mx:Button id="pollStartBtn" visible="false" height="30" styleName="pollStartButtonStyle"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.polling.startButton.tooltip')}"
|
||||
click="onPollStartButtonClicked()" tabIndex="{baseIndex+6}"/>
|
||||
<poll:QuickPollButton id="quickPollBtn" height="30" tabIndex="{baseIndex+7}"
|
||||
click="quickPollClicked(event)" />
|
||||
<mx:Spacer width="100%" id="spacer1"/>
|
||||
<mx:Button id="backButton" visible="false" width="45" height="30" styleName="presentationBackButtonStyle"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.presentation.backBtn.toolTip')}" click="gotoPreviousSlide()"
|
||||
tabIndex="{baseIndex+7}"/>
|
||||
tabIndex="{baseIndex+8}"/>
|
||||
<mx:Button id="btnSlideNum" visible="false" label="" click="showThumbnails()"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.presentation.btnSlideNum.toolTip')}"
|
||||
tabIndex="{baseIndex+8}"/>
|
||||
tabIndex="{baseIndex+9}"/>
|
||||
<mx:Button id="forwardButton" visible="false" width="45" height="30" styleName="presentationForwardButtonStyle"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.presentation.forwardBtn.toolTip')}" click="gotoNextSlide()"
|
||||
tabIndex="{baseIndex+9}"/>
|
||||
tabIndex="{baseIndex+10}"/>
|
||||
<mx:Spacer width="50%" id="spacer3"/>
|
||||
<mx:HSlider id="zoomSlider" visible="false" value="{slideView.zoomPercentage}" styleName="presentationZoomSliderStyle"
|
||||
minimum="100" maximum="400" dataTipPlacement="top" labels="['100%','400%']"
|
||||
useHandCursor="true" snapInterval="5" allowTrackClick="true" liveDragging="true"
|
||||
dataTipFormatFunction="removeDecimalFromDataTip" change="onSliderZoom()" width="100"
|
||||
tabIndex="{baseIndex+10}" accessibilityName="{ResourceUtil.getInstance().getString('bbb.presentation.slider')}"/>
|
||||
tabIndex="{baseIndex+11}" accessibilityName="{ResourceUtil.getInstance().getString('bbb.presentation.slider')}"/>
|
||||
<mx:Button id="btnFitToWidth" visible="false" width="30" height="30" styleName="presentationFitToWidthButtonStyle"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.presentation.fitToWidth.toolTip')}"
|
||||
click="onFitToPage(false)"
|
||||
tabIndex="{baseIndex+11}"/>
|
||||
tabIndex="{baseIndex+12}"/>
|
||||
<mx:Button id="btnFitToPage" visible="false" width="30" height="30" styleName="presentationFitToPageButtonStyle"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.presentation.fitToPage.toolTip')}"
|
||||
click="onFitToPage(true)"
|
||||
tabIndex="{baseIndex+12}"/>
|
||||
tabIndex="{baseIndex+13}"/>
|
||||
<!-- This spacer is to prevent the whiteboard toolbar from overlapping the fit-ot-page button -->
|
||||
<mx:Spacer width="30" height="30" id="spacer4"/>
|
||||
</mx:HBox>
|
||||
|
@ -179,6 +179,14 @@ package org.bigbluebutton.modules.users.services
|
||||
var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS);
|
||||
e.userid = userid;
|
||||
dispatcher.dispatchEvent(e);
|
||||
|
||||
// If the user was the presenter he's reconnecting and must become viewer
|
||||
if (UserManager.getInstance().getConference().amIPresenter) {
|
||||
sendSwitchedPresenterEvent(false, UsersUtil.getPresenterUserID());
|
||||
UserManager.getInstance().getConference().amIPresenter = false;
|
||||
var viewerEvent:MadePresenterEvent = new MadePresenterEvent(MadePresenterEvent.SWITCH_TO_VIEWER_MODE);
|
||||
dispatcher.dispatchEvent(viewerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleMeetingMuted(msg:Object):void {
|
||||
@ -346,6 +354,7 @@ package org.bigbluebutton.modules.users.services
|
||||
|
||||
if (UsersUtil.hasUser(internUserID)) {
|
||||
var bu:BBBUser = UsersUtil.getUser(internUserID);
|
||||
bu.talking = voiceUser.talking;
|
||||
bu.voiceMuted = voiceUser.muted;
|
||||
bu.voiceJoined = true;
|
||||
|
||||
@ -409,6 +418,9 @@ package org.bigbluebutton.modules.users.services
|
||||
LOGGER.debug("*** handleGetUsersReply {0} **** \n", [msg.msg]);
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var users:Object = map.users as Array;
|
||||
|
||||
// since might be a reconnection, clean up users list
|
||||
UserManager.getInstance().getConference().removeAllParticipants();
|
||||
|
||||
if (map.count > 0) {
|
||||
LOGGER.debug("number of users = [{0}]", [users.length]);
|
||||
@ -555,6 +567,10 @@ package org.bigbluebutton.modules.users.services
|
||||
}
|
||||
}
|
||||
|
||||
if (joinedUser.voiceUser.joined) {
|
||||
userJoinedVoice(joinedUser);
|
||||
}
|
||||
|
||||
UserManager.getInstance().getConference().presenterStatusChanged(user.userID, joinedUser.presenter);
|
||||
UserManager.getInstance().getConference().raiseHand(user.userID, joinedUser.raiseHand);
|
||||
|
||||
@ -582,4 +598,4 @@ package org.bigbluebutton.modules.users.services
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,13 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.ReconnectionManager;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
|
||||
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
|
||||
|
||||
public class VideoProxy
|
||||
@ -48,15 +52,12 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
public var videoOptions:VideoConfOptions;
|
||||
|
||||
private var nc:NetConnection;
|
||||
private var ns:NetStream;
|
||||
private var _url:String;
|
||||
private var camerasPublishing:Object = new Object();
|
||||
private var connected:Boolean = false;
|
||||
private var reconnect:Boolean = false;
|
||||
private var reconnect:Boolean = false;
|
||||
private var reconnecting:Boolean = false;
|
||||
|
||||
private var autoReconnectTimer:Timer = new Timer(1000, 1);
|
||||
|
||||
private var dispatcher:Dispatcher = new Dispatcher();
|
||||
|
||||
private function parseOptions():void {
|
||||
videoOptions = new VideoConfOptions();
|
||||
videoOptions.parseOptions();
|
||||
@ -91,32 +92,70 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
LOGGER.debug("VIDEO WEBCAM onIOError");
|
||||
}
|
||||
|
||||
private function onConnectedToVideoApp():void{
|
||||
var dispatcher:Dispatcher = new Dispatcher();
|
||||
dispatcher.dispatchEvent(new ConnectedEvent(reconnecting));
|
||||
reconnecting = false;
|
||||
}
|
||||
private function onConnectedToVideoApp():void{
|
||||
dispatcher.dispatchEvent(new ConnectedEvent(reconnecting));
|
||||
if (reconnecting) {
|
||||
reconnecting = false;
|
||||
|
||||
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
|
||||
attemptSucceeded.payload.type = ReconnectionManager.VIDEO_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptSucceeded);
|
||||
}
|
||||
}
|
||||
|
||||
private function onNetStatus(event:NetStatusEvent):void{
|
||||
|
||||
LOGGER.debug("[{0}] for [{1}]", [event.info.code, _url]);
|
||||
var logData:Object = new Object();
|
||||
logData.user = UsersUtil.getUserData();
|
||||
switch(event.info.code){
|
||||
case "NetConnection.Connect.Success":
|
||||
connected = true;
|
||||
//ns = new NetStream(nc);
|
||||
onConnectedToVideoApp();
|
||||
break;
|
||||
case "NetStream.Play.Failed":
|
||||
disconnect();
|
||||
if (reconnect) {
|
||||
JSLog.warn("NetStream.Play.Failed from bbb-video", logData);
|
||||
}
|
||||
|
||||
break;
|
||||
case "NetStream.Play.Stop":
|
||||
disconnect();
|
||||
if (reconnect) {
|
||||
JSLog.warn("NetStream.Play.Stop from bbb-video", logData);
|
||||
}
|
||||
|
||||
break;
|
||||
case "NetConnection.Connect.Closed":
|
||||
dispatcher.dispatchEvent(new StopBroadcastEvent());
|
||||
|
||||
if (reconnect) {
|
||||
reconnecting = true;
|
||||
|
||||
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
|
||||
disconnectedEvent.payload.type = ReconnectionManager.VIDEO_CONNECTION;
|
||||
disconnectedEvent.payload.callback = connect;
|
||||
disconnectedEvent.payload.callbackParameters = [];
|
||||
dispatcher.dispatchEvent(disconnectedEvent);
|
||||
}
|
||||
break;
|
||||
|
||||
case "NetConnection.Connect.Failed":
|
||||
if (reconnecting) {
|
||||
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
|
||||
attemptFailedEvent.payload.type = ReconnectionManager.VIDEO_CONNECTION;
|
||||
dispatcher.dispatchEvent(attemptFailedEvent);
|
||||
}
|
||||
|
||||
if (reconnect) {
|
||||
JSLog.warn("Disconnected from bbb-video", logData);
|
||||
}
|
||||
|
||||
disconnect();
|
||||
break;
|
||||
break;
|
||||
case "NetConnection.Connect.NetworkChange":
|
||||
JSLog.warn("Detected network change on bbb-video", logData);
|
||||
break;
|
||||
default:
|
||||
LOGGER.debug("[{0}] for [{1}]", [event.info.code, _url]);
|
||||
connected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -129,17 +168,14 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
}
|
||||
|
||||
public function startPublishing(e:StartBroadcastEvent):void{
|
||||
ns = new NetStream(nc);
|
||||
var ns:NetStream = new NetStream(nc);
|
||||
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
|
||||
ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
|
||||
ns.addEventListener( AsyncErrorEvent.ASYNC_ERROR, onAsyncError );
|
||||
ns.client = this;
|
||||
ns.attachCamera(e.camera);
|
||||
// Uncomment if you want to build support for H264. But you need at least FP 11. (ralam july 23, 2011)
|
||||
// if (Capabilities.version.search("11,0") != -1) {
|
||||
|
||||
if ((BBB.getFlashPlayerVersion() >= 11) && e.videoProfile.enableH264) {
|
||||
// if (BBB.getFlashPlayerVersion() >= 11) {
|
||||
LOGGER.info("Using H264 codec for video.");
|
||||
var h264:H264VideoStreamSettings = new H264VideoStreamSettings();
|
||||
var h264profile:String = H264Profile.MAIN;
|
||||
if (e.videoProfile.h264Profile != "main") {
|
||||
@ -165,7 +201,6 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
case "5.1": h264Level = H264Level.LEVEL_5_1; break;
|
||||
}
|
||||
|
||||
LOGGER.info("Codec used: {0}", [h264Level]);
|
||||
|
||||
h264.setProfileLevel(h264profile, h264Level);
|
||||
ns.videoStreamSettings = h264;
|
||||
@ -200,18 +235,6 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
LOGGER.debug("VideoProxy:: disconnecting from Video application");
|
||||
stopAllBroadcasting();
|
||||
if (nc != null) nc.close();
|
||||
|
||||
if (reconnect) {
|
||||
var reconnectTimer:Timer = new Timer(1000, 1);
|
||||
reconnectTimer.addEventListener("timer", reconnectTimerHandler);
|
||||
reconnectTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
private function reconnectTimerHandler(event:TimerEvent):void {
|
||||
LOGGER.debug("rtmptRetryTimerHandler: {0}", [event]);
|
||||
reconnecting = true;
|
||||
connect();
|
||||
}
|
||||
|
||||
public function onBWCheck(... rest):Number {
|
||||
|
@ -23,7 +23,7 @@ package org.bigbluebutton.modules.videoconf.events
|
||||
public class ConnectedEvent extends Event
|
||||
{
|
||||
public static const VIDEO_CONNECTED:String = "connected to video app event";
|
||||
|
||||
|
||||
public var reconnection:Boolean = false;
|
||||
|
||||
public function ConnectedEvent(reconnection:Boolean)
|
||||
|
@ -129,7 +129,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<EventHandlers type="{BBBEvent.CAM_SETTINGS_CLOSED}">
|
||||
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCamSettingsClosedEvent" arguments="{event}"/>
|
||||
</EventHandlers>
|
||||
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}">
|
||||
<MethodInvoker generator="{VideoEventMapDelegate}" method="closeAllWindows"/>
|
||||
</EventHandlers>
|
||||
<!-- ~~~~~~~~~~~~~~~~~~ INJECTORS ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
||||
|
||||
</EventMap>
|
||||
|
@ -110,7 +110,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
|
||||
public function handleStreamStoppedEvent(event:StreamStoppedEvent):void {
|
||||
if (UserManager.getInstance().getConference().amIThisUser(event.userId)) {
|
||||
closePublishWindowWithStream(event.userId, event.streamId);
|
||||
closePublishWindowByStream(event.streamId);
|
||||
} else {
|
||||
closeViewWindowWithStream(event.userId, event.streamId);
|
||||
}
|
||||
@ -262,11 +262,19 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
_graphics.removeGraphicsFor(userID);
|
||||
}
|
||||
|
||||
private function closePublishWindowWithStream(userID:String, stream:String):int {
|
||||
return _graphics.removeVideoByStreamName(userID, stream);
|
||||
private function closePublishWindowByStream(stream:String):int {
|
||||
return _graphics.removeVideoByStreamName(UsersUtil.getMyUserID(), stream);
|
||||
}
|
||||
|
||||
private function closePublishWindow():void {
|
||||
closeWindow(UsersUtil.getMyUserID());
|
||||
}
|
||||
|
||||
private function openViewWindowFor(userID:String):void {
|
||||
if (!proxy.connection.connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug("VideoEventMapDelegate:: [{0}] openViewWindowFor:: Opening VIEW window for [{1}] [{2}]", [me, userID, UsersUtil.getUserName(userID)]);
|
||||
|
||||
var bbbUser:BBBUser = UsersUtil.getUser(userID);
|
||||
@ -305,49 +313,21 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
|
||||
public function stopPublishing(e:StopBroadcastEvent):void{
|
||||
LOGGER.debug("VideoEventMapDelegate:: [{0}] Stop publishing. ready = [{1}]", [me, _ready]);
|
||||
if(streamList.length <= 1) {
|
||||
setStopLastBroadcasting();
|
||||
} else {
|
||||
UsersUtil.setIAmPublishing(true);
|
||||
}
|
||||
checkLastBroadcasting();
|
||||
streamList.removeItem(e.stream);
|
||||
stopBroadcasting(e.stream);
|
||||
button.setCamAsInactive(e.camId);
|
||||
}
|
||||
|
||||
private function stopAllBroadcasting():void {
|
||||
LOGGER.debug("[VideoEventMapDelegate:stopAllBroadcasting]");
|
||||
setStopLastBroadcasting();
|
||||
streamList = new ArrayList();
|
||||
proxy.stopAllBroadcasting();
|
||||
|
||||
var userID:String = UsersUtil.getMyUserID();
|
||||
_graphics.removeGraphicsFor(userID);
|
||||
|
||||
var broadcastEvent:BroadcastStoppedEvent = new BroadcastStoppedEvent();
|
||||
broadcastEvent.stream = "";
|
||||
broadcastEvent.userid = UsersUtil.getMyUserID();
|
||||
broadcastEvent.avatarURL = UsersUtil.getAvatarURL();
|
||||
_dispatcher.dispatchEvent(broadcastEvent);
|
||||
|
||||
if (proxy.videoOptions.showButton) {
|
||||
//Make toolbar button enabled again
|
||||
button.setAllCamAsInactive();
|
||||
}
|
||||
if (options.displayAvatar) {
|
||||
LOGGER.debug("VideoEventMapDelegate:: [{0}] Opening avatar", [me]);
|
||||
openAvatarWindowFor(UsersUtil.getMyUserID());
|
||||
}
|
||||
private function checkLastBroadcasting():void {
|
||||
LOGGER.debug("[VideoEventMapDelegate:checkLastBroadcasting]");
|
||||
_isPublishing = streamList.length > 0;
|
||||
UsersUtil.setIAmPublishing(streamList.length > 0);
|
||||
}
|
||||
|
||||
private function setStopLastBroadcasting():void {
|
||||
LOGGER.debug("[VideoEventMapDelegate:setStopLastBroadcasting]");
|
||||
_isPublishing = false;
|
||||
UsersUtil.setIAmPublishing(false);
|
||||
}
|
||||
|
||||
private function stopBroadcasting(stream:String):void {
|
||||
LOGGER.debug("Stopping broadcast of stream [{0}]", [stream]);
|
||||
private function stopBroadcasting(stream:String = ""):void {
|
||||
if (stream == null) stream = "";
|
||||
LOGGER.debug("Stopping broadcast{0}", [(stream.length > 0? " of stream [" + stream + "]": "")]);
|
||||
|
||||
proxy.stopBroadcasting(stream);
|
||||
|
||||
@ -357,11 +337,20 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
broadcastEvent.avatarURL = UsersUtil.getAvatarURL();
|
||||
_dispatcher.dispatchEvent(broadcastEvent);
|
||||
|
||||
var camId:int = closePublishWindowWithStream(UsersUtil.getMyUserID(), stream);
|
||||
if (stream.length > 0) {
|
||||
var camId:int = closePublishWindowByStream(stream);
|
||||
|
||||
if (proxy.videoOptions.showButton) {
|
||||
//Make toolbar button enabled again
|
||||
button.publishingStatus(button.STOP_PUBLISHING, camId);
|
||||
if (proxy.videoOptions.showButton) {
|
||||
//Make toolbar button enabled again
|
||||
button.publishingStatus(button.STOP_PUBLISHING, camId);
|
||||
}
|
||||
} else {
|
||||
closePublishWindow();
|
||||
|
||||
if (proxy.videoOptions.showButton) {
|
||||
// make toolbar button enabled again
|
||||
button.setAllCamAsInactive();
|
||||
}
|
||||
}
|
||||
|
||||
if (streamList.length == 0 && options.displayAvatar) {
|
||||
@ -373,7 +362,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
public function handleClosePublishWindowEvent(event:ClosePublishWindowEvent):void {
|
||||
LOGGER.debug("Closing publish window");
|
||||
if (_isPublishing || _chromeWebcamPermissionDenied) {
|
||||
stopAllBroadcasting();
|
||||
stopBroadcasting();
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,7 +377,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
|
||||
public function handleStopAllShareCameraRequestEvent(event:StopShareCameraRequestEvent):void {
|
||||
LOGGER.debug("[VideoEventMapDelegate:handleStopAllShareCameraRequestEvent]");
|
||||
stopAllBroadcasting();
|
||||
stopBroadcasting();
|
||||
}
|
||||
|
||||
public function handleStopShareCameraRequestEvent(event:StopShareCameraRequestEvent):void {
|
||||
@ -425,7 +414,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
public function closeAllWindows():void{
|
||||
LOGGER.debug("VideoEventMapDelegate:: closing all windows");
|
||||
if (_isPublishing) {
|
||||
stopAllBroadcasting();
|
||||
stopBroadcasting();
|
||||
}
|
||||
|
||||
_graphics.shutdown();
|
||||
@ -446,7 +435,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
LOGGER.debug("****************** Switching to viewer. Show video button?=[{0}]", [UsersUtil.amIPresenter()]);
|
||||
displayToolbarButton();
|
||||
if (_isPublishing && options.presenterShareOnly) {
|
||||
stopAllBroadcasting();
|
||||
stopBroadcasting();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -454,17 +443,13 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
public function connectedToVideoApp(event: ConnectedEvent):void{
|
||||
LOGGER.debug("VideoEventMapDelegate:: [{0}] Connected to video application.", [me]);
|
||||
_ready = true;
|
||||
if (event.reconnection) {
|
||||
LOGGER.debug("VideoEventMapDelegate:: Got reconnected event.");
|
||||
stopAllBroadcasting();
|
||||
LOGGER.debug("VideoEventMapDelegate:: Closing all webcam windows.");
|
||||
closeAllWindows()
|
||||
openWebcamWindows();
|
||||
} else {
|
||||
addToolbarButton();
|
||||
openWebcamWindows();
|
||||
}
|
||||
|
||||
if (event.reconnection) {
|
||||
closeAllWindows()
|
||||
} else {
|
||||
addToolbarButton();
|
||||
}
|
||||
openWebcamWindows();
|
||||
|
||||
}
|
||||
|
||||
public function handleCameraSetting(event:BBBEvent):void {
|
||||
|
@ -233,17 +233,6 @@ package org.bigbluebutton.modules.videoconf.views
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
override public function validateDisplayList():void {
|
||||
super.validateDisplayList();
|
||||
|
||||
if (priorityMode) {
|
||||
updateDisplayListHelperByPriority(this.width, this.height);
|
||||
} else {
|
||||
updateDisplayListHelper(this.width, this.height);
|
||||
}
|
||||
}
|
||||
*/
|
||||
public function addAvatarFor(userId:String):void {
|
||||
if (! UsersUtil.hasUser(userId)) return;
|
||||
|
||||
|
@ -217,7 +217,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
}
|
||||
|
||||
private function updateTalkingStatus() : void{
|
||||
private function updateTalkingStatus():void {
|
||||
if (user.talking) {
|
||||
titleBox.setStyle("styleName", "videoToolbarBackgroundTalkingStyle");
|
||||
} else {
|
||||
|
@ -1,287 +1,289 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.modules.whiteboard.business.shapes
|
||||
{
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
import flash.text.TextFormatAlign;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.as3commons.logging.util.jsonXify;
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.modules.whiteboard.business.shapes
|
||||
{
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
import flash.text.TextFormatAlign;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.as3commons.logging.util.jsonXify;
|
||||
import org.bigbluebutton.modules.whiteboard.models.Annotation;
|
||||
|
||||
public class PollResultObject extends DrawObject {
|
||||
private static const LOGGER:ILogger = getClassLogger(PollResultObject);
|
||||
|
||||
//private const h:uint = 100;
|
||||
//private const w:uint = 280;
|
||||
private const bgFill:uint = 0XCECECE; //0xFFFFFF;
|
||||
private const colFill:uint = 0x000000;
|
||||
private const vpadding:Number = 10;
|
||||
private const hpadding:Number = 5;
|
||||
private const labelStartWidth:int = 40;
|
||||
private const percentStartWidth:int = 40;
|
||||
|
||||
private var sampledata:Array = [{a:"A", v:3}, {a:"B", v:1}, {a:"C", v:5}, {a:"D", v:8}];
|
||||
private var _data:Array;
|
||||
private var _textFields:Array;
|
||||
|
||||
public function PollResultObject(id:String, type:String, status:String) {
|
||||
super(id, type, status)
|
||||
|
||||
_textFields = new Array();
|
||||
data = null;
|
||||
// temp setter for testing purposes
|
||||
//data = sampledata;
|
||||
|
||||
}
|
||||
|
||||
public function set data(d:Array):void {
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public function get data():Array {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private function makeTextFields(num:int):void {
|
||||
if (num > _textFields.length) {
|
||||
var textField:TextField;
|
||||
for (var i:int=_textFields.length; i < num; i++) {
|
||||
textField = new TextField();
|
||||
addChild(textField);
|
||||
_textFields.push(textField);
|
||||
}
|
||||
} else if (num < _textFields.length) {
|
||||
for (var j:int=_textFields.length; i > num; i--) {
|
||||
removeChild(_textFields.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
|
||||
// graphics.clear();
|
||||
|
||||
if (_data != null && _data.length > 0) {
|
||||
graphics.lineStyle(2);
|
||||
graphics.beginFill(bgFill, 1.0);
|
||||
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
|
||||
graphics.endFill();
|
||||
|
||||
var actualRH:Number = (unscaledHeight-vpadding*(_data.length+1)) / _data.length;
|
||||
LOGGER.debug("PollGraphic - as raw {0} int {1}", [actualRH, int(actualRH)]);
|
||||
// Current problem is that the rowHeight is truncated. It would be nice if the extra pixels
|
||||
// could be distributed for a more even look.
|
||||
var avgRowHeight:int = (unscaledHeight-vpadding*(_data.length+1)) / _data.length;
|
||||
var extraVPixels:int = unscaledHeight - (_data.length * (avgRowHeight+vpadding) + vpadding);
|
||||
LOGGER.debug("PollGraphic - extraVPixels {0}", [extraVPixels]);
|
||||
var largestVal:int = -1;
|
||||
var totalCount:Number = 0;
|
||||
//find largest value
|
||||
for (var i:int=0; i<_data.length; i++) {
|
||||
if (_data[i].v > largestVal) largestVal = _data[i].v;
|
||||
totalCount += _data[i].v;
|
||||
}
|
||||
|
||||
var currTFIdx:int = 0;
|
||||
var answerText:TextField;
|
||||
var percentText:TextField;
|
||||
var answerArray:Array = new Array();
|
||||
var percentArray:Array = new Array();
|
||||
var minFontSize:int = 20;
|
||||
var currFontSize:int;
|
||||
|
||||
graphics.lineStyle(2);
|
||||
graphics.beginFill(colFill, 1.0);
|
||||
for (var j:int=0, vp:int=extraVPixels, ry:int=0, curRowHeight:int=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry += curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
// add row label
|
||||
answerText = _textFields[currTFIdx++];
|
||||
answerText.text = _data[j].a;
|
||||
answerText.width = labelStartWidth;
|
||||
answerText.height = curRowHeight;
|
||||
answerText.selectable = false;
|
||||
//addChild(answerText);
|
||||
answerArray.push(answerText);
|
||||
currFontSize = findFontSize(answerText, 20);
|
||||
if (currFontSize < minFontSize) minFontSize = currFontSize;
|
||||
//rowText.height = rowText.textHeight;
|
||||
answerText.x = hpadding;
|
||||
//rowText.y = ry-rowText.height/2;
|
||||
|
||||
// add percentage
|
||||
percentText = _textFields[currTFIdx++];;// new TextField();
|
||||
var percentNum:Number = (totalCount == 0 ? 0 : ((_data[j].v/totalCount)*100));
|
||||
percentText.text = Math.round(percentNum).toString() + "%";
|
||||
percentText.width = percentStartWidth;
|
||||
percentText.height = curRowHeight;
|
||||
percentText.selectable = false;
|
||||
//addChild(percentText);
|
||||
percentArray.push(percentText);
|
||||
currFontSize = findFontSize(percentText, 20);
|
||||
if (currFontSize < minFontSize) minFontSize = currFontSize;
|
||||
//percentText.height = percentText.textHeight;
|
||||
//percentText.x = unscaledWidth-percentStartWidth/2-percentText.width/2;
|
||||
//percentText.y = ry-percentText.height/2;
|
||||
}
|
||||
|
||||
var maxAnswerWidth:int = 0;
|
||||
var maxPercentWidth:int = 0;
|
||||
|
||||
for (j=0, vp=extraVPixels, ry=0, curRowHeight=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry = curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
|
||||
answerText = TextField(answerArray[j]);
|
||||
findFontSize(answerText, minFontSize);
|
||||
answerText.width = answerText.textWidth+4;
|
||||
answerText.height = answerText.textHeight+4;
|
||||
answerText.y = ry-answerText.height/2;
|
||||
if (answerText.width > maxAnswerWidth) maxAnswerWidth = answerText.width;
|
||||
|
||||
percentText = TextField(percentArray[j]);
|
||||
findFontSize(percentText, minFontSize);
|
||||
percentText.width = percentText.textWidth+4;
|
||||
percentText.height = percentText.textHeight+4;
|
||||
percentText.x = unscaledWidth - hpadding - percentText.width;
|
||||
percentText.y = ry-percentText.height/2;
|
||||
if (percentText.width > maxPercentWidth) maxPercentWidth = percentText.width;
|
||||
|
||||
}
|
||||
|
||||
var countText:TextField;
|
||||
var maxBarWidth:int = unscaledWidth - (hpadding*4) - maxAnswerWidth - maxPercentWidth;
|
||||
var barStartX:int = maxAnswerWidth + (hpadding*2);
|
||||
|
||||
for (j=0, vp=extraVPixels, ry=0, curRowHeight=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry = curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
|
||||
// draw rect
|
||||
var rectWidth:int = maxBarWidth*(_data[j].v/largestVal);
|
||||
graphics.drawRect(barStartX, ry-curRowHeight/2, rectWidth, curRowHeight);
|
||||
// add vote count in middle of rect
|
||||
countText = _textFields[currTFIdx++]; // new TextField();
|
||||
countText.text = _data[j].v;
|
||||
countText.width = rectWidth;
|
||||
countText.height = curRowHeight;
|
||||
countText.textColor = 0xFFFFFF;
|
||||
countText.selectable = false;
|
||||
//addChild(countText);
|
||||
findFontSize(countText, minFontSize);
|
||||
countText.width = countText.textWidth+4;
|
||||
countText.height = countText.textHeight+4;
|
||||
countText.x = barStartX+rectWidth/2-countText.width/2;
|
||||
countText.y = ry-countText.height/2;
|
||||
}
|
||||
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
private function findFontSize(textField:TextField, defaultSize:Number):int {
|
||||
var tFormat:TextFormat = new TextFormat();
|
||||
tFormat.size = defaultSize;
|
||||
tFormat.align = TextFormatAlign.CENTER;
|
||||
textField.setTextFormat(tFormat);
|
||||
var size:Number = defaultSize;
|
||||
while((textField.textWidth+4 > textField.width || textField.textHeight+4 > textField.height) && size > 0) {
|
||||
size = size - 1;
|
||||
tFormat.size = size;
|
||||
textField.setTextFormat(tFormat);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private function drawRect(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
var ao:Object = a.annotation;
|
||||
this.graphics.lineStyle(1 * zoom, 0);
|
||||
|
||||
var arrayEnd:Number = (ao.points as Array).length;
|
||||
var startX:Number = denormalize(21.845575, parentWidth);
|
||||
var startY:Number = denormalize(23.145401, parentHeight);
|
||||
var width:Number = denormalize(46.516006, parentWidth) - startX;
|
||||
var height:Number = denormalize(61.42433, parentHeight) - startY;
|
||||
|
||||
this.graphics.drawRect(startX, startY, width, height);
|
||||
|
||||
}
|
||||
|
||||
override public function draw(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
var ao:Object = a.annotation;
|
||||
LOGGER.debug("RESULT = {0}", [jsonXify(a)]);
|
||||
|
||||
var arrayEnd:Number = (ao.points as Array).length;
|
||||
var startX:Number = denormalize((ao.points as Array)[0], parentWidth);
|
||||
var startY:Number = denormalize((ao.points as Array)[1], parentHeight);
|
||||
var pwidth:Number = denormalize((ao.points as Array)[2], parentWidth) - startX;
|
||||
var pheight:Number = denormalize((ao.points as Array)[3], parentHeight) - startY;
|
||||
|
||||
var answers:Array = ao.result as Array;
|
||||
var ans:Array = new Array();
|
||||
for (var j:int = 0; j < answers.length; j++) {
|
||||
var ar:Object = answers[j];
|
||||
var rs:Object = {a: ar.key, v: ar.num_votes as Number};
|
||||
LOGGER.debug("poll result a=[{0}] v=[{1}]", [ar.key, ar.num_votes]);
|
||||
ans.push(rs);
|
||||
}
|
||||
|
||||
data = ans;
|
||||
makeTextFields((answers != null ? answers.length*3 : 0));
|
||||
|
||||
this.x = startX;
|
||||
this.y = startY;
|
||||
|
||||
updateDisplayList(pwidth, pheight);
|
||||
|
||||
}
|
||||
|
||||
override public function redraw(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
draw(a, parentWidth, parentHeight, zoom);
|
||||
}
|
||||
}
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
public class PollResultObject extends DrawObject {
|
||||
private static const LOGGER:ILogger = getClassLogger(PollResultObject);
|
||||
|
||||
//private const h:uint = 100;
|
||||
//private const w:uint = 280;
|
||||
private const bgFill:uint = 0xFFFFFF;
|
||||
private const colFill:uint = 0x000000;
|
||||
private const vpadding:Number = 10;
|
||||
private const hpadding:Number = 5;
|
||||
private const labelStartWidth:int = 40;
|
||||
private const percentStartWidth:int = 40;
|
||||
|
||||
private var sampledata:Array = [{a:"A", v:3}, {a:"B", v:1}, {a:"C", v:5}, {a:"D", v:8}];
|
||||
private var _data:Array;
|
||||
private var _textFields:Array;
|
||||
|
||||
public function PollResultObject(id:String, type:String, status:String) {
|
||||
super(id, type, status)
|
||||
|
||||
_textFields = new Array();
|
||||
data = null;
|
||||
// temp setter for testing purposes
|
||||
//data = sampledata;
|
||||
|
||||
}
|
||||
|
||||
public function set data(d:Array):void {
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public function get data():Array {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private function makeTextFields(num:int):void {
|
||||
if (num > _textFields.length) {
|
||||
var textField:TextField;
|
||||
for (var i:int=_textFields.length; i < num; i++) {
|
||||
textField = new TextField();
|
||||
addChild(textField);
|
||||
_textFields.push(textField);
|
||||
}
|
||||
} else if (num < _textFields.length) {
|
||||
for (var j:int=_textFields.length; i > num; i--) {
|
||||
removeChild(_textFields.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
|
||||
// graphics.clear();
|
||||
|
||||
if (_data != null && _data.length > 0) {
|
||||
graphics.lineStyle(2);
|
||||
graphics.beginFill(bgFill, 1.0);
|
||||
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
|
||||
graphics.endFill();
|
||||
|
||||
var actualRH:Number = (unscaledHeight-vpadding*(_data.length+1)) / _data.length;
|
||||
LOGGER.debug("PollGraphic - as raw {0} int {1}", [actualRH, int(actualRH)]);
|
||||
// Current problem is that the rowHeight is truncated. It would be nice if the extra pixels
|
||||
// could be distributed for a more even look.
|
||||
var avgRowHeight:int = (unscaledHeight-vpadding*(_data.length+1)) / _data.length;
|
||||
var extraVPixels:int = unscaledHeight - (_data.length * (avgRowHeight+vpadding) + vpadding);
|
||||
LOGGER.debug("PollGraphic - extraVPixels {0}", [extraVPixels]);
|
||||
var largestVal:int = -1;
|
||||
var totalCount:Number = 0;
|
||||
//find largest value
|
||||
for (var i:int=0; i<_data.length; i++) {
|
||||
if (_data[i].v > largestVal) largestVal = _data[i].v;
|
||||
totalCount += _data[i].v;
|
||||
}
|
||||
|
||||
var currTFIdx:int = 0;
|
||||
var answerText:TextField;
|
||||
var percentText:TextField;
|
||||
var answerArray:Array = new Array();
|
||||
var percentArray:Array = new Array();
|
||||
var minFontSize:int = 20;
|
||||
var currFontSize:int;
|
||||
|
||||
graphics.lineStyle(2);
|
||||
graphics.beginFill(colFill, 1.0);
|
||||
for (var j:int=0, vp:int=extraVPixels, ry:int=0, curRowHeight:int=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry += curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
// add row label
|
||||
answerText = _textFields[currTFIdx++];
|
||||
answerText.text = _data[j].a;
|
||||
answerText.width = labelStartWidth;
|
||||
answerText.height = curRowHeight;
|
||||
answerText.selectable = false;
|
||||
//addChild(answerText);
|
||||
answerArray.push(answerText);
|
||||
currFontSize = findFontSize(answerText, 20);
|
||||
if (currFontSize < minFontSize) minFontSize = currFontSize;
|
||||
//rowText.height = rowText.textHeight;
|
||||
answerText.x = hpadding;
|
||||
//rowText.y = ry-rowText.height/2;
|
||||
|
||||
// add percentage
|
||||
percentText = _textFields[currTFIdx++];;// new TextField();
|
||||
var percentNum:Number = (totalCount == 0 ? 0 : ((_data[j].v/totalCount)*100));
|
||||
percentText.text = Math.round(percentNum).toString() + "%";
|
||||
percentText.width = percentStartWidth;
|
||||
percentText.height = curRowHeight;
|
||||
percentText.selectable = false;
|
||||
//addChild(percentText);
|
||||
percentArray.push(percentText);
|
||||
currFontSize = findFontSize(percentText, 20);
|
||||
if (currFontSize < minFontSize) minFontSize = currFontSize;
|
||||
//percentText.height = percentText.textHeight;
|
||||
//percentText.x = unscaledWidth-percentStartWidth/2-percentText.width/2;
|
||||
//percentText.y = ry-percentText.height/2;
|
||||
}
|
||||
|
||||
var maxAnswerWidth:int = 0;
|
||||
var maxPercentWidth:int = 0;
|
||||
|
||||
for (j=0, vp=extraVPixels, ry=0, curRowHeight=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry = curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
|
||||
answerText = TextField(answerArray[j]);
|
||||
findFontSize(answerText, minFontSize);
|
||||
answerText.width = answerText.textWidth+4;
|
||||
answerText.height = answerText.textHeight+4;
|
||||
answerText.y = ry-answerText.height/2;
|
||||
if (answerText.width > maxAnswerWidth) maxAnswerWidth = answerText.width;
|
||||
|
||||
percentText = TextField(percentArray[j]);
|
||||
findFontSize(percentText, minFontSize);
|
||||
percentText.width = percentText.textWidth+4;
|
||||
percentText.height = percentText.textHeight+4;
|
||||
percentText.x = unscaledWidth - hpadding - percentText.width;
|
||||
percentText.y = ry-percentText.height/2;
|
||||
if (percentText.width > maxPercentWidth) maxPercentWidth = percentText.width;
|
||||
|
||||
}
|
||||
|
||||
var countText:TextField;
|
||||
var maxBarWidth:int = unscaledWidth - (hpadding*4) - maxAnswerWidth - maxPercentWidth;
|
||||
var barStartX:int = maxAnswerWidth + (hpadding*2);
|
||||
|
||||
for (j=0, vp=extraVPixels, ry=0, curRowHeight=0; j<_data.length; j++) {
|
||||
ry += Math.round(curRowHeight/2)+vpadding; // add the last row's height plus padding
|
||||
|
||||
curRowHeight = avgRowHeight;
|
||||
if (j%2==0 && vp > 0) {
|
||||
curRowHeight += 1;
|
||||
vp--;
|
||||
}
|
||||
ry += curRowHeight/2;
|
||||
|
||||
//ry = curRowHeight * (j+0.5) + vpadding*(j+1);
|
||||
|
||||
// draw rect
|
||||
var rectWidth:int = maxBarWidth*(_data[j].v/largestVal);
|
||||
graphics.drawRect(barStartX, ry-curRowHeight/2, rectWidth, curRowHeight);
|
||||
// add vote count in middle of rect
|
||||
countText = _textFields[currTFIdx++]; // new TextField();
|
||||
countText.text = _data[j].v;
|
||||
countText.width = rectWidth;
|
||||
countText.height = curRowHeight;
|
||||
countText.textColor = 0xFFFFFF;
|
||||
countText.selectable = false;
|
||||
//addChild(countText);
|
||||
findFontSize(countText, minFontSize);
|
||||
countText.width = countText.textWidth+4;
|
||||
countText.height = countText.textHeight+4;
|
||||
countText.x = barStartX+rectWidth/2-countText.width/2;
|
||||
countText.y = ry-countText.height/2;
|
||||
}
|
||||
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
private function findFontSize(textField:TextField, defaultSize:Number):int {
|
||||
var tFormat:TextFormat = new TextFormat();
|
||||
tFormat.size = defaultSize;
|
||||
tFormat.font = "arial";
|
||||
tFormat.align = TextFormatAlign.CENTER;
|
||||
textField.setTextFormat(tFormat);
|
||||
var size:Number = defaultSize;
|
||||
while((textField.textWidth+4 > textField.width || textField.textHeight+4 > textField.height) && size > 0) {
|
||||
size = size - 1;
|
||||
tFormat.size = size;
|
||||
textField.setTextFormat(tFormat);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private function drawRect(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
var ao:Object = a.annotation;
|
||||
this.graphics.lineStyle(1 * zoom, 0);
|
||||
|
||||
var arrayEnd:Number = (ao.points as Array).length;
|
||||
var startX:Number = denormalize(21.845575, parentWidth);
|
||||
var startY:Number = denormalize(23.145401, parentHeight);
|
||||
var width:Number = denormalize(46.516006, parentWidth) - startX;
|
||||
var height:Number = denormalize(61.42433, parentHeight) - startY;
|
||||
|
||||
this.graphics.drawRect(startX, startY, width, height);
|
||||
|
||||
}
|
||||
|
||||
override public function draw(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
var ao:Object = a.annotation;
|
||||
LOGGER.debug("RESULT = {0}", [jsonXify(a)]);
|
||||
|
||||
var arrayEnd:Number = (ao.points as Array).length;
|
||||
var startX:Number = denormalize((ao.points as Array)[0], parentWidth);
|
||||
var startY:Number = denormalize((ao.points as Array)[1], parentHeight);
|
||||
var pwidth:Number = denormalize((ao.points as Array)[2], parentWidth);
|
||||
var pheight:Number = denormalize((ao.points as Array)[3], parentHeight);
|
||||
|
||||
var answers:Array = ao.result as Array;
|
||||
var ans:Array = new Array();
|
||||
for (var j:int = 0; j < answers.length; j++) {
|
||||
var ar:Object = answers[j];
|
||||
var rs:Object = {a: ResourceUtil.getInstance().getString('bbb.polling.answer.' + ar.key), v: ar.num_votes as Number};
|
||||
LOGGER.debug("poll result a=[{0}] v=[{1}]", [ar.key, ar.num_votes]);
|
||||
ans.push(rs);
|
||||
}
|
||||
|
||||
data = ans;
|
||||
makeTextFields((answers != null ? answers.length*3 : 0));
|
||||
|
||||
this.x = startX;
|
||||
this.y = startY;
|
||||
|
||||
updateDisplayList(pwidth, pheight);
|
||||
|
||||
}
|
||||
|
||||
override public function redraw(a:Annotation, parentWidth:Number, parentHeight:Number, zoom:Number):void {
|
||||
draw(a, parentWidth, parentHeight, zoom);
|
||||
}
|
||||
}
|
||||
}
|
@ -138,5 +138,10 @@ package org.bigbluebutton.modules.whiteboard.managers
|
||||
public function handlePageChangedEvent(e:PageLoadedEvent):void {
|
||||
displayModel.changePage(e.pageId);
|
||||
}
|
||||
|
||||
public function removeAnnotationsHistory():void {
|
||||
// it will dispatch the cleanAnnotations in the displayModel later
|
||||
whiteboardModel.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/" xmlns:mate="org.bigbluebutton.common.mate.*">
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ModuleStartedEvent;
|
||||
import org.bigbluebutton.modules.present.events.AddOverlayCanvasEvent;
|
||||
import org.bigbluebutton.modules.present.events.NavigationEvent;
|
||||
@ -122,6 +123,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<MethodInvoker generator="{WhiteboardManager}" method="handlePageChangedEvent" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.RECONNECT_BIGBLUEBUTTON_SUCCEEDED_EVENT}" >
|
||||
<MethodInvoker generator="{WhiteboardManager}" method="removeAnnotationsHistory" />
|
||||
</EventHandlers>
|
||||
|
||||
<Injectors target="{WhiteboardManager}">
|
||||
<ObjectBuilder generator="{WhiteboardModel}" cache="global" constructorArguments="{scope.dispatcher}"/>
|
||||
<PropertyInjector targetKey="whiteboardModel" source="{WhiteboardModel}"/>
|
||||
|
@ -135,11 +135,16 @@ package org.bigbluebutton.modules.whiteboard.models
|
||||
|
||||
}
|
||||
|
||||
public function clear(wbId:String):void {
|
||||
public function clear(wbId:String = null):void {
|
||||
LOGGER.debug("Clearing whiteboard");
|
||||
var wb:Whiteboard = getWhiteboard(wbId);
|
||||
if (wb != null) {
|
||||
wb.clear();
|
||||
if (wbId != null) {
|
||||
var wb:Whiteboard = getWhiteboard(wbId);
|
||||
if (wb != null) {
|
||||
wb.clear();
|
||||
_dispatcher.dispatchEvent(new WhiteboardUpdate(WhiteboardUpdate.CLEAR_ANNOTATIONS));
|
||||
}
|
||||
} else {
|
||||
_whiteboards.removeAll();
|
||||
_dispatcher.dispatchEvent(new WhiteboardUpdate(WhiteboardUpdate.CLEAR_ANNOTATIONS));
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ package org.bigbluebutton.modules.whiteboard.services
|
||||
}
|
||||
|
||||
public function onMessage(messageName:String, message:Object):void {
|
||||
// LogUtil.debug("WB: received message " + messageName);
|
||||
// trace("WB: received message " + messageName);
|
||||
|
||||
switch (messageName) {
|
||||
case "WhiteboardRequestAnnotationHistoryReply":
|
||||
|
@ -26,3 +26,5 @@ pagebakers:ionicons
|
||||
ewall:foundation
|
||||
maibaum:foundation-icons
|
||||
gthacoder:sled
|
||||
chriswessels:hammer@3.1.1
|
||||
fastclick
|
||||
|
@ -1,5 +1,6 @@
|
||||
agnito:raphael@0.1.0
|
||||
aldeed:simple-schema@1.3.2
|
||||
aldeed:template-extension@3.4.3
|
||||
amplify@1.0.0
|
||||
arunoda:npm@0.2.6
|
||||
autoupdate@1.2.1
|
||||
@ -12,6 +13,7 @@ brentjanderson:winston-client@0.2.1
|
||||
callback-hook@1.0.3
|
||||
cfs:http-methods@0.0.27
|
||||
check@1.0.5
|
||||
chriswessels:hammer@3.1.1
|
||||
clinical:nightwatch@2.0.1
|
||||
coffeescript@1.0.6
|
||||
ddp@1.1.0
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
# Convert a color `value` as integer to a hex color (e.g. 255 to #0000ff)
|
||||
@colourToHex = (value) ->
|
||||
hex = parseInt(value).toString(16)
|
||||
hex = "0" + hex while hex.length < 6
|
||||
"##{hex}"
|
||||
hex = parseInt(value).toString(16)
|
||||
hex = "0" + hex while hex.length < 6
|
||||
"##{hex}"
|
||||
|
||||
# color can be a number (a hex converted to int) or a string (e.g. "#ffff00")
|
||||
@formatColor = (color) ->
|
||||
@ -34,6 +34,10 @@
|
||||
@getTime = -> # returns epoch in ms
|
||||
(new Date).valueOf()
|
||||
|
||||
# checks if the pan gesture is mostly horizontal
|
||||
@isPanHorizontal = (event) ->
|
||||
Math.abs(event.deltaX) > Math.abs(event.deltaY)
|
||||
|
||||
# helper to determine whether user has joined any type of audio
|
||||
Handlebars.registerHelper "amIInAudio", ->
|
||||
BBB.amIInAudio()
|
||||
@ -212,11 +216,55 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
$('.sl-left-drawer').addClass('hiddenInLandscape')
|
||||
setTimeout(redrawWhiteboard, 0)
|
||||
|
||||
@populateNotifications = (msg) ->
|
||||
myUserId = getInSession "userId"
|
||||
users = Meteor.Users.find().fetch()
|
||||
|
||||
# assuming that I only have access only to private messages where I am the sender or the recipient
|
||||
myPrivateChats = Meteor.Chat.find({'message.chat_type': 'PRIVATE_CHAT'}).fetch()
|
||||
|
||||
uniqueArray = []
|
||||
for chat in myPrivateChats
|
||||
if chat.message.to_userid is myUserId
|
||||
uniqueArray.push({userId: chat.message.from_userid, username: chat.message.from_username})
|
||||
if chat.message.from_userid is myUserId
|
||||
uniqueArray.push({userId: chat.message.to_userid, username: chat.message.to_username})
|
||||
|
||||
#keep unique entries only
|
||||
uniqueArray = uniqueArray.filter((itm, i, a) ->
|
||||
i is a.indexOf(itm)
|
||||
)
|
||||
|
||||
if msg.message.to_userid is myUserId
|
||||
new_msg_userid = msg.message.from_userid
|
||||
if msg.message.from_userid is myUserId
|
||||
new_msg_userid = msg.message.to_userid
|
||||
|
||||
chats = getInSession('chats')
|
||||
if chats is undefined
|
||||
initChats = [
|
||||
userId: "PUBLIC_CHAT"
|
||||
gotMail: false
|
||||
number: 0;
|
||||
]
|
||||
setInSession 'chats', initChats
|
||||
|
||||
#insert the unique entries in the collection
|
||||
for u in uniqueArray
|
||||
chats = getInSession('chats')
|
||||
if chats.filter((chat) -> chat.userId == u.userId).length is 0 and u.userId is new_msg_userid
|
||||
chats.push {userId: u.userId, gotMail: false, number: 0}
|
||||
setInSession 'chats', chats
|
||||
|
||||
@toggleShield = ->
|
||||
if $('.shield').hasClass('darken')
|
||||
$('.shield').removeClass('darken')
|
||||
else
|
||||
if parseFloat($('.shield').css('opacity')) is 0.5 # triggered during a pan gesture
|
||||
$('.shield').css('opacity', '')
|
||||
|
||||
if !$('.shield').hasClass('darken') and !$('.shield').hasClass('animatedShield')
|
||||
$('.shield').addClass('darken')
|
||||
else
|
||||
$('.shield').removeClass('darken')
|
||||
$('.shield').removeClass('animatedShield')
|
||||
|
||||
@removeFullscreenStyles = ->
|
||||
$('#whiteboard-paper').removeClass('verticallyCentered')
|
||||
@ -327,7 +375,7 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
@clearSessionVar = (callback) ->
|
||||
amplify.store('authToken', null)
|
||||
amplify.store('bbbServerVersion', null)
|
||||
amplify.store('chatTabs', null)
|
||||
amplify.store('chats', null)
|
||||
amplify.store('dateOfBuild', null)
|
||||
amplify.store('display_chatPane', null)
|
||||
amplify.store('display_chatbar', null)
|
||||
@ -351,9 +399,11 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
setInSession "display_chatbar", true
|
||||
setInSession "display_whiteboard", true
|
||||
setInSession "display_chatPane", true
|
||||
if not getInSession "inChatWith" then setInSession "inChatWith", 'PUBLIC_CHAT'
|
||||
|
||||
#if it is a desktop version of the client
|
||||
if isPortraitMobile() or isLandscapeMobile()
|
||||
setInSession "messageFontSize", Meteor.config.app.mobileFont
|
||||
#if this is a mobile version of the client
|
||||
else
|
||||
setInSession "messageFontSize", Meteor.config.app.desktopFont
|
||||
setInSession 'display_slidingMenu', false
|
||||
@ -363,8 +413,34 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
else
|
||||
setInSession 'display_usersList', false
|
||||
setInSession 'display_menu', false
|
||||
setInSession 'chatInputMinHeight', 0
|
||||
|
||||
#keep notifications and an opened private chat tab if page was refreshed
|
||||
#reset to default if that's a new user
|
||||
if loginOrRefresh()
|
||||
initChats = [
|
||||
userId: "PUBLIC_CHAT"
|
||||
gotMail: false
|
||||
number: 0
|
||||
]
|
||||
setInSession 'chats', initChats
|
||||
setInSession "inChatWith", 'PUBLIC_CHAT'
|
||||
|
||||
TimeSync.loggingEnabled = false # suppresses the log messages from timesync
|
||||
|
||||
#true if it is a new user, false if the client was just refreshed
|
||||
@loginOrRefresh = ->
|
||||
userId = getInSession 'userId'
|
||||
checkId = getInSession 'checkId'
|
||||
if checkId is undefined
|
||||
setInSession 'checkId', userId
|
||||
return true
|
||||
else if userId isnt checkId
|
||||
setInSession 'checkId', userId
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@onLoadComplete = ->
|
||||
document.title = "BigBlueButton #{BBB.getMeetingName() ? 'HTML5'}"
|
||||
setDefaultSettings()
|
||||
@ -386,7 +462,14 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
navigator.userAgent.match(/webOS/i)
|
||||
|
||||
@isLandscape = ->
|
||||
window.matchMedia('(orientation: landscape)').matches
|
||||
not isMobile() and
|
||||
window.matchMedia('(orientation: landscape)').matches and # browser is landscape
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches # device is landscape
|
||||
|
||||
@isPortrait = ->
|
||||
not isMobile() and
|
||||
window.matchMedia('(orientation: portrait)').matches and # browser is portrait
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches # device is landscape
|
||||
|
||||
# Checks if the view is portrait and a mobile device is being used
|
||||
@isPortraitMobile = () ->
|
||||
@ -397,8 +480,8 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
# Checks if the view is landscape and mobile device is being used
|
||||
@isLandscapeMobile = () ->
|
||||
isMobile() and
|
||||
window.matchMedia('(orientation: landscape)').matches and # browser is landscape
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches # device is landscape
|
||||
window.matchMedia('(orientation: landscape)').matches and # browser is landscape
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches # device is landscape
|
||||
|
||||
# Checks if only one panel (userlist/whiteboard/chatbar) is currently open
|
||||
@isOnlyOnePanelOpen = () ->
|
||||
@ -417,3 +500,22 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
|
||||
return 'IE'
|
||||
else
|
||||
return null
|
||||
|
||||
# changes the height of the chat input area if needed (based on the textarea content)
|
||||
@adjustChatInputHeight = () ->
|
||||
$('#newMessageInput').css('height', 'auto')
|
||||
projectedHeight = $('#newMessageInput')[0].scrollHeight + 23
|
||||
if projectedHeight isnt $('.panel-footer').height() and
|
||||
projectedHeight >= getInSession('chatInputMinHeight')
|
||||
$('#newMessageInput').css('overflow', 'hidden') # prevents a scroll bar
|
||||
|
||||
# resizes the chat input area
|
||||
$('.panel-footer').css('top', - (projectedHeight - 70) + 'px')
|
||||
$('.panel-footer').css('height', projectedHeight + 'px')
|
||||
|
||||
$('#newMessageInput').height($('#newMessageInput')[0].scrollHeight)
|
||||
|
||||
# resizes the chat messages container
|
||||
$('#chatbody').height($('#chat').height() - projectedHeight - 45)
|
||||
$('#chatbody').scrollTop($('#chatbody')[0]?.scrollHeight)
|
||||
$('#newMessageInput').css('height', '')
|
||||
|
@ -141,5 +141,177 @@ Template.main.events
|
||||
$('.signOutIcon').blur()
|
||||
$("#logoutModal").foundation('reveal', 'open');
|
||||
|
||||
Template.main.gestures
|
||||
'panstart #container': (event, template) ->
|
||||
if isPortraitMobile() and isPanHorizontal(event)
|
||||
panIsValid = getInSession('panIsValid')
|
||||
initTransformValue = getInSession('initTransform')
|
||||
menuPanned = getInSession('menuPanned')
|
||||
screenWidth = $('#container').width()
|
||||
|
||||
setInSession 'panStarted', true
|
||||
|
||||
if panIsValid and
|
||||
menuPanned is 'left' and
|
||||
initTransformValue + event.deltaX >= 0 and
|
||||
initTransformValue + event.deltaX <= $('.left-drawer').width()
|
||||
$('.left-drawer').css('transform', 'translateX(' + (initTransformValue + event.deltaX) + 'px)')
|
||||
|
||||
else if panIsValid and
|
||||
menuPanned is 'right' and
|
||||
initTransformValue + event.deltaX >= screenWidth - $('.right-drawer').width() and
|
||||
initTransformValue + event.deltaX <= screenWidth
|
||||
$('.right-drawer').css('transform', 'translateX(' + (initTransformValue + event.deltaX) + 'px)')
|
||||
|
||||
'panend #container': (event, template) ->
|
||||
if isPortraitMobile()
|
||||
panIsValid = getInSession('panIsValid')
|
||||
menuPanned = getInSession('menuPanned')
|
||||
leftDrawerWidth = $('.left-drawer').width()
|
||||
screenWidth = $('#container').width()
|
||||
|
||||
setInSession 'panStarted', false
|
||||
|
||||
if panIsValid and
|
||||
menuPanned is 'left' and
|
||||
$('.left-drawer').css('transform') isnt 'none'
|
||||
|
||||
if parseInt($('.left-drawer').css('transform').split(',')[4]) < leftDrawerWidth / 2
|
||||
$('.shield').removeClass('animatedShield')
|
||||
$('.shield').css('opacity', '')
|
||||
$('.left-drawer').removeClass('sl-left-drawer-out')
|
||||
$('.left-drawer').css('transform', '')
|
||||
$('.toggleUserlistButton').removeClass('sl-toggled-on')
|
||||
$('.shield').removeClass('darken') # in case it was opened by clicking a button
|
||||
else
|
||||
$('.left-drawer').css('transform', 'translateX(' + leftDrawerWidth + 'px)')
|
||||
$('.shield').css('opacity', 0.5)
|
||||
$('.left-drawer').addClass('sl-left-drawer-out')
|
||||
$('.left-drawer').css('transform', '')
|
||||
$('.toggleUserlistButton').addClass('sl-toggled-on')
|
||||
|
||||
if panIsValid and
|
||||
menuPanned is 'right' and
|
||||
parseInt($('.right-drawer').css('transform').split(',')[4]) isnt leftDrawerWidth
|
||||
|
||||
if parseInt($('.right-drawer').css('transform').split(',')[4]) > screenWidth - $('.right-drawer').width() / 2
|
||||
$('.shield').removeClass('animatedShield')
|
||||
$('.shield').css('opacity', '')
|
||||
$('.right-drawer').css('transform', 'translateX(' + screenWidth + 'px)')
|
||||
$('.right-drawer').removeClass('sl-right-drawer-out')
|
||||
$('.right-drawer').css('transform', '')
|
||||
$('.toggleMenuButton').removeClass('sl-toggled-on')
|
||||
$('.shield').removeClass('darken') # in case it was opened by clicking a button
|
||||
else
|
||||
$('.shield').css('opacity', 0.5)
|
||||
$('.right-drawer').css('transform', 'translateX(' + (screenWidth - $('.right-drawer').width()) + 'px)')
|
||||
$('.right-drawer').addClass('sl-right-drawer-out')
|
||||
$('.right-drawer').css('transform', '')
|
||||
$('.toggleMenuButton').addClass('sl-toggled-on')
|
||||
|
||||
$('.left-drawer').addClass('sl-left-drawer')
|
||||
$('.sl-left-drawer').removeClass('left-drawer')
|
||||
|
||||
$('.right-drawer').addClass('sl-right-drawer')
|
||||
$('.sl-right-drawer').removeClass('right-drawer')
|
||||
|
||||
'panright #container, panleft #container': (event, template) ->
|
||||
if isPortraitMobile() and isPanHorizontal(event)
|
||||
|
||||
# panright/panleft is always triggered once right before panstart
|
||||
if !getInSession('panStarted')
|
||||
|
||||
# opening the left-hand menu
|
||||
if event.type is 'panright' and
|
||||
event.center.x <= $('#container').width() * 0.1
|
||||
setInSession 'panIsValid', true
|
||||
setInSession 'menuPanned', 'left'
|
||||
|
||||
# closing the left-hand menu
|
||||
else if event.type is 'panleft' and
|
||||
event.center.x < $('#container').width() * 0.9
|
||||
setInSession 'panIsValid', true
|
||||
setInSession 'menuPanned', 'left'
|
||||
|
||||
# opening the right-hand menu
|
||||
else if event.type is 'panleft' and
|
||||
event.center.x >= $('#container').width() * 0.9
|
||||
setInSession 'panIsValid', true
|
||||
setInSession 'menuPanned', 'right'
|
||||
|
||||
# closing the right-hand menu
|
||||
else if event.type is 'panright' and
|
||||
event.center.x > $('#container').width() * 0.1
|
||||
setInSession 'panIsValid', true
|
||||
setInSession 'menuPanned', 'right'
|
||||
|
||||
else
|
||||
setInSession 'panIsValid', false
|
||||
|
||||
setInSession 'eventType', event.type
|
||||
|
||||
if getInSession('menuPanned') is 'left'
|
||||
if $('.sl-left-drawer').css('transform') isnt 'none' # menu is already transformed
|
||||
setInSession 'initTransform', parseInt($('.sl-left-drawer').css('transform').split(',')[4]) # translateX value
|
||||
else if $('.sl-left-drawer').hasClass('sl-left-drawer-out')
|
||||
setInSession 'initTransform', $('.sl-left-drawer').width()
|
||||
else
|
||||
setInSession 'initTransform', 0
|
||||
$('.sl-left-drawer').addClass('left-drawer')
|
||||
$('.left-drawer').removeClass('sl-left-drawer') # to prevent animations from Sled library
|
||||
$('.left-drawer').removeClass('sl-left-drawer-content-delay') # makes the menu content movable too
|
||||
|
||||
else if getInSession('menuPanned') is 'right'
|
||||
if $('.sl-right-drawer').css('transform') isnt 'none' # menu is already transformed
|
||||
setInSession 'initTransform', parseInt($('.sl-right-drawer').css('transform').split(',')[4]) # translateX value
|
||||
else if $('.sl-right-drawer').hasClass('sl-right-drawer-out')
|
||||
setInSession 'initTransform', $('.sl-right-drawer').width()
|
||||
else
|
||||
setInSession 'initTransform', 0
|
||||
$('.sl-right-drawer').addClass('right-drawer')
|
||||
$('.right-drawer').removeClass('sl-right-drawer') # to prevent animations from Sled library
|
||||
$('.right-drawer').removeClass('sl-right-drawer-content-delay') # makes the menu content movable too
|
||||
|
||||
initTransformValue = getInSession('initTransform')
|
||||
panIsValid = getInSession('panIsValid')
|
||||
menuPanned = getInSession('menuPanned')
|
||||
leftDrawerWidth = $('.left-drawer').width()
|
||||
rightDrawerWidth = $('.right-drawer').width()
|
||||
screenWidth = $('#container').width()
|
||||
|
||||
# moving the left-hand menu
|
||||
if panIsValid and
|
||||
menuPanned is 'left' and
|
||||
initTransformValue + event.deltaX >= 0 and
|
||||
initTransformValue + event.deltaX <= leftDrawerWidth
|
||||
|
||||
if $('.sl-right-drawer').hasClass('sl-right-drawer-out')
|
||||
toggleRightDrawer()
|
||||
toggleRightArrowClockwise()
|
||||
|
||||
$('.left-drawer').css('transform', 'translateX(' + (initTransformValue + event.deltaX) + 'px)')
|
||||
|
||||
if !getInSession('panStarted')
|
||||
$('.shield').addClass('animatedShield')
|
||||
$('.shield').css('opacity',
|
||||
0.5 * (initTransformValue + event.deltaX) / leftDrawerWidth)
|
||||
|
||||
# moving the right-hand menu
|
||||
else if panIsValid and
|
||||
menuPanned is 'right' and
|
||||
initTransformValue + event.deltaX >= screenWidth - rightDrawerWidth and
|
||||
initTransformValue + event.deltaX <= screenWidth
|
||||
|
||||
if $('.sl-left-drawer').hasClass('sl-left-drawer-out')
|
||||
toggleLeftDrawer()
|
||||
toggleLeftArrowClockwise()
|
||||
|
||||
$('.right-drawer').css('transform', 'translateX(' + (initTransformValue + event.deltaX) + 'px)')
|
||||
|
||||
if !getInSession('panStarted')
|
||||
$('.shield').addClass('animatedShield')
|
||||
$('.shield').css('opacity',
|
||||
0.5 * (screenWidth - initTransformValue - event.deltaX) / rightDrawerWidth)
|
||||
|
||||
Template.makeButton.rendered = ->
|
||||
$('button[rel=tooltip]').tooltip()
|
||||
|
@ -1,9 +1,7 @@
|
||||
<template name="header">
|
||||
<nav id="navbar" class="myNavbar gradientBar top-bar" role="navigation">
|
||||
<button class="btn toggleUserlistButton navbarButton sl-hamburger sl-ham-la-cw sl-portrait-mobile sl-portrait-keyboard">
|
||||
<span></span>
|
||||
</button>
|
||||
|
||||
<nav id="navbar" class="myNavbar top-bar" role="navigation">
|
||||
{{> makeButton btn_class="btn toggleUserlistButton navbarButton sl-hamburger sl-ham-la-cw sl-portrait-mobile sl-portrait-keyboard" rel="tooltip" title="Toggle Userlist" span=true notification="all_chats"}}
|
||||
|
||||
{{#if amIInAudio}}
|
||||
{{#if amIListenOnlyAudio}}
|
||||
{{> makeButton btn_class="navbarButton leaveAudioButton" i_class="icon fi-volume-none" rel="tooltip" title="Exit Audio"}}
|
||||
|
@ -11,9 +11,21 @@
|
||||
height: 45px;
|
||||
min-height: 45px;
|
||||
.btn {
|
||||
&:hover {
|
||||
color: #3896D3;
|
||||
background-color:white;
|
||||
border-bottom: 1px solid #E5E5E5;
|
||||
}
|
||||
&:focus {
|
||||
background-color:white;
|
||||
border-bottom: 1px solid #E5E5E5;
|
||||
}
|
||||
padding: 0.625rem 0.5rem 0.6875rem 0.5rem;
|
||||
background-color: inherit;
|
||||
margin: 0;
|
||||
height: 45px;
|
||||
outline: none;
|
||||
min-width: 50px;
|
||||
}
|
||||
span {
|
||||
position: relative;
|
||||
@ -29,23 +41,40 @@
|
||||
}
|
||||
}
|
||||
.privateChatName {
|
||||
width: calc(~'100% - 105px');
|
||||
min-width: 101px;
|
||||
text-align: right;
|
||||
float: right;
|
||||
padding: 0.65rem 1.25rem 0.6rem 0.3rem;
|
||||
font-family: "Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;
|
||||
font-size: 18px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.toPublic .unreadChatNumber {
|
||||
@media @desktop-portrait, @landscape {
|
||||
padding: 2px;
|
||||
position: absolute;
|
||||
top: 31%;
|
||||
left: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#chat {
|
||||
@media @landscape {
|
||||
-webkit-flex: 3 3 30%;
|
||||
-moz-flex: 3 3 30%;
|
||||
-ms-flex: 3 3 30%;
|
||||
flex: 3 3 30%;
|
||||
-webkit-flex: 1 1;
|
||||
-moz-flex: 1 1;
|
||||
-ms-flex: 1 1;
|
||||
flex: 1 1;
|
||||
height: 100%;
|
||||
border-top: 0px;
|
||||
border-right: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
order: 3;
|
||||
}
|
||||
@ -136,23 +165,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gotUnreadMail {
|
||||
background: extract(@yellow, 2) !important;
|
||||
}
|
||||
|
||||
#newMessageInput {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 75%;
|
||||
resize: none;
|
||||
padding-top: 5px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
|
||||
border:1px solid extract(@lightGrey, 3);
|
||||
margin: 0px;
|
||||
|
||||
@media @landscape {
|
||||
height: 40px; /* same height as send button */
|
||||
height: 100%; /* same height as send button */
|
||||
}
|
||||
@media @mobile-portrait {
|
||||
font-size: 4vw;
|
||||
@ -173,22 +198,24 @@
|
||||
position: relative;
|
||||
background: extract(@white, 1);
|
||||
padding: 0;
|
||||
border-top: 1px solid #E5E5E5;
|
||||
border-top: 1px solid extract(@lightGrey, 3);
|
||||
}
|
||||
|
||||
#sendMessageButton {
|
||||
width: 20%; /* 75% for the message input, 5% margin between the two */
|
||||
color: extract(@black, 1);
|
||||
background-color: extract(@white, 1);
|
||||
background-color: #3896D3;
|
||||
font-weight: bold;
|
||||
height: 50px;
|
||||
margin: 0px;
|
||||
border: 1px solid extract(@lightGrey, 3);
|
||||
|
||||
@media @desktop-portrait {
|
||||
width: 20%; /* 75% for the message input, 5% margin between the two */
|
||||
font-size: 30px;
|
||||
height: 60px;
|
||||
&:hover {
|
||||
background: #3A82D4;
|
||||
}
|
||||
}
|
||||
@media @mobile-portrait {
|
||||
width: 15vw;
|
||||
@ -203,6 +230,11 @@
|
||||
@media @landscape {
|
||||
height: 50px;
|
||||
padding: 0px;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
&:hover {
|
||||
background: #3A82D4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,3 +247,16 @@
|
||||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
@media @landscape {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#chatInput {
|
||||
@media @landscape {
|
||||
height: 100%;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -33,9 +33,42 @@ body {
|
||||
margin-left:5px
|
||||
}
|
||||
|
||||
@media @mobile-portrait {
|
||||
.sl-toggled-on .unreadChat {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbarButton {
|
||||
color: #469DCF;
|
||||
.unreadChat {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background:red;
|
||||
border-radius:80%;
|
||||
@media @desktop-portrait, @landscape {
|
||||
top: 20%;
|
||||
left: 65%;
|
||||
width:19%;
|
||||
height:20%;
|
||||
}
|
||||
@media @mobile-portrait {
|
||||
top: 14%;
|
||||
left: 70%;
|
||||
width:25%;
|
||||
height:25%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.myNavbar {
|
||||
border-bottom: 0px;
|
||||
&.gradientBar {
|
||||
border-bottom: 1px;
|
||||
@media @desktop-portrait, @landscape {
|
||||
background-color: white;
|
||||
border-bottom: 1px solid extract(@lightGrey, 1);
|
||||
}
|
||||
|
||||
@media @mobile-portrait-with-keyboard, @mobile-portrait {
|
||||
.linear-gradient(rgb(72,76,85), rgb(65,68,77));
|
||||
}
|
||||
.btn {
|
||||
@ -45,13 +78,15 @@ body {
|
||||
top: 0 !important;
|
||||
padding-left: 1% !important;
|
||||
padding-right: 1% !important;
|
||||
background-color: white;
|
||||
border-bottom: 1px solid extract(@lightGrey, 1);
|
||||
}
|
||||
@media @mobile-portrait-with-keyboard, @mobile-portrait {
|
||||
height: 100px !important;
|
||||
width: 10%;
|
||||
min-width: 60px;
|
||||
.linear-gradient(rgb(72,76,85), rgb(65,68,77));
|
||||
}
|
||||
.linear-gradient(rgb(72,76,85), rgb(65,68,77));
|
||||
.push-menu-icon {
|
||||
.icon-bar {
|
||||
margin-left: auto;
|
||||
@ -71,10 +106,9 @@ body {
|
||||
&.toggleUserlistButton, &.toggleMenuButton {
|
||||
background: transparent;
|
||||
}
|
||||
span { background-color: white; }
|
||||
span { background-color: #469DCF; }
|
||||
}
|
||||
@media @landscape {
|
||||
min-width: 768px;
|
||||
height: 50px;
|
||||
}
|
||||
@media @mobile-portrait-with-keyboard, @mobile-portrait {
|
||||
@ -87,28 +121,35 @@ body {
|
||||
}
|
||||
|
||||
.navbarTitle {
|
||||
color: extract(@white, 1);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@media @landscape {
|
||||
font-size: 16px;
|
||||
width: 30% !important;
|
||||
}
|
||||
position: absolute;
|
||||
@media @mobile-portrait-with-keyboard, @mobile-portrait {
|
||||
font-size: 30px;
|
||||
padding-top: 30px;
|
||||
padding-left: 5px;
|
||||
color: extract(@white, 1);
|
||||
font-size: 35px;
|
||||
height: 72px;
|
||||
width: 70%;
|
||||
max-width: 70%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 110px;
|
||||
padding-top: 55px; // half the height
|
||||
padding-left: 30px;
|
||||
}
|
||||
@media @desktop-portrait {
|
||||
max-width: calc(~'100% - 155px'); // navbar width minus the space for 3 buttons
|
||||
}
|
||||
@media @landscape {
|
||||
max-width: calc(~'100% - 125px'); // navbar width minus the space for 4 buttons
|
||||
}
|
||||
@media @landscape, @desktop-portrait {
|
||||
color: extract(@darkGrey, 1);
|
||||
font-size: 16px;
|
||||
width: calc(~'100% - 102.4px');
|
||||
height: 50px;
|
||||
padding-top: 25px; // half the height
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,30 +182,32 @@ body {
|
||||
}
|
||||
|
||||
.meetingTitle {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 2em;
|
||||
margin: 0;
|
||||
@media @mobile-portrait, @mobile-portrait-with-keyboard, @desktop-portrait {
|
||||
@media @mobile-portrait, @mobile-portrait-with-keyboard {
|
||||
.linear-gradient(rgb(72,76,85), rgb(65,68,77));
|
||||
padding-left: 140px;
|
||||
padding-top: 20px;
|
||||
color: white;
|
||||
font-size: 4vw;
|
||||
height: 110px;
|
||||
}
|
||||
@media @desktop-portrait {
|
||||
.linear-gradient(rgb(72,76,85), rgb(65,68,77));
|
||||
padding-top: 10px;
|
||||
color: white;
|
||||
}
|
||||
@media @landscape {
|
||||
border-bottom: 1px solid extract(@lightGrey, 1);
|
||||
color: extract(@darkGrey, 1);
|
||||
padding-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
@media @desktop-portrait, @landscape {
|
||||
font-size: 18px;
|
||||
height: 50px;
|
||||
}
|
||||
@media @mobile-portrait, @mobile-portrait-with-keyboard {
|
||||
font-size: 4vw;
|
||||
height: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
#container {
|
||||
@ -272,10 +315,10 @@ body {
|
||||
|
||||
#main {
|
||||
@media @landscape {
|
||||
-webkit-flex: 4 4 80%;
|
||||
-moz-flex: 4 4 80%;
|
||||
-ms-flex: 4 4 80%;
|
||||
flex: 4 4 80%;
|
||||
-webkit-flex: 1 1;
|
||||
-moz-flex: 1 1;
|
||||
-ms-flex: 1 1;
|
||||
flex: 1 1;
|
||||
height: 100%;
|
||||
}
|
||||
@media @mobile-portrait-with-keyboard, @desktop-portrait, @mobile-portrait {
|
||||
@ -303,6 +346,12 @@ body {
|
||||
|
||||
.signOutIcon {
|
||||
@media @landscape {
|
||||
&:hover {
|
||||
color: #2A5E7C;
|
||||
}
|
||||
&:focus {
|
||||
color: #2A5E7C;
|
||||
}
|
||||
height: 28px;
|
||||
width: 75px;
|
||||
margin-left: 10px;
|
||||
@ -333,8 +382,18 @@ body {
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.toggleUserlistButton {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.settingsIcon {
|
||||
@media @landscape {
|
||||
&:hover {
|
||||
color: #2A5E7C;
|
||||
}
|
||||
&:focus {
|
||||
color: #2A5E7C;
|
||||
}
|
||||
width: 57px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
@ -459,7 +518,7 @@ body {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
left: -400px;
|
||||
width: 400px;
|
||||
width: 400px !important; // overrides any width value set manually in landscape
|
||||
&.sl-left-drawer-out {
|
||||
left: 0px;
|
||||
}
|
||||
@ -478,10 +537,7 @@ body {
|
||||
.sl-left-drawer {
|
||||
height: 100%;
|
||||
@media @landscape {
|
||||
-webkit-flex: 1 1 20%;
|
||||
-moz-flex: 1 1 20%;
|
||||
-ms-flex: 1 1 20%;
|
||||
flex: 1 1 20%;
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,7 +570,7 @@ body {
|
||||
@media @landscape {
|
||||
display: none;
|
||||
}
|
||||
&:not(.darken) {
|
||||
&:not(.darken):not(.animatedShield) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -532,3 +588,28 @@ body {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-resizable-handle {
|
||||
@media @mobile-portrait, @desktop-portrait, @mobile-portrait-with-keyboard {
|
||||
display: none; // hides the sizing handle everywhere except the landscape mode
|
||||
}
|
||||
}
|
||||
|
||||
// same as Sled's left drawer except for animations
|
||||
.left-drawer {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
width: 60vw;
|
||||
left: -60vw;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// same as Sled's right drawer except for animations
|
||||
.right-drawer {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
transform: translateX(100vw);
|
||||
height: 100%;
|
||||
.linear-gradient(rgb(65,68,77), rgb(58,60,69));
|
||||
width: 60vw;
|
||||
}
|
||||
|
@ -1,26 +1,48 @@
|
||||
@import "variables";
|
||||
@import "mixins";
|
||||
|
||||
.usericon {
|
||||
font-size: 16px;
|
||||
.statusIcon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#usericons {
|
||||
text-align: right;
|
||||
color: white;
|
||||
span i {
|
||||
@media @landscape, @desktop-portrait {
|
||||
margin-right: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.unreadChatNumber {
|
||||
@media @landscape, @desktop-portrait{
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
width: 28px;
|
||||
height: 18px;
|
||||
border-radius: 41%;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background: #3896D3;
|
||||
}
|
||||
}
|
||||
|
||||
.usernameEntry {
|
||||
cursor: pointer;
|
||||
line-height: 1.1;
|
||||
float:left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 60%;
|
||||
width: calc(~'100% - 130px');
|
||||
@media @landscape {
|
||||
height: 20px;
|
||||
height: 27px;
|
||||
font-size: 4.5mm;
|
||||
}
|
||||
@media @desktop-portrait {
|
||||
height: 25px;
|
||||
height: 27px;
|
||||
font-size: 4.5mm;
|
||||
}
|
||||
@media @mobile-portrait, @mobile-portrait-with-keyboard {
|
||||
@ -34,7 +56,6 @@
|
||||
border-left: 0px;
|
||||
border-top: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
@media @mobile-portrait-with-keyboard, @desktop-portrait, @mobile-portrait {
|
||||
width: 100%;
|
||||
@ -62,15 +83,24 @@
|
||||
}
|
||||
|
||||
#user-contents {
|
||||
height: 83vh; /* for the inside scrolling list to utilize as much room as possible, this surrounding window must also take up as much room as possible */
|
||||
|
||||
height: calc(~'100% - 50px'); /* user-contents = user-contens - meetingTitle */
|
||||
@media @landscape, @desktop-portrait{
|
||||
background-color: #34495E;
|
||||
}
|
||||
.userlist {
|
||||
height: calc(~'100% - 29px'); /* height of user contents - user list */
|
||||
max-height: 100% !important;
|
||||
height: 100%;
|
||||
|
||||
#content:hover {
|
||||
@media @landscape, @desktop-portrait {
|
||||
background-color: #2C4155;
|
||||
}
|
||||
}
|
||||
#content {
|
||||
span:hover {
|
||||
/*background-color: #EEEEEE;*/
|
||||
background: transparent;
|
||||
@media @mobile-portrait-with-keyboard, @desktop-portrait, @mobile-portrait {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
margin-top: 10px;
|
||||
&:first-child { margin-top: 0px; }
|
||||
@ -103,17 +133,13 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.closeUserlistIcon {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
@media @mobile-portrait-with-keyboard, @desktop-portrait, @mobile-portrait {
|
||||
display: none;
|
||||
}
|
||||
.closeSettings {
|
||||
@media @landscape {
|
||||
color: #aaa;
|
||||
font-style: normal;
|
||||
font-size: 30px;
|
||||
.toggleUserlistButton, .toggleMenuButton {
|
||||
span {
|
||||
@media @landscape, @desktop-portrait {
|
||||
width: 26px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 8%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,3 +154,18 @@
|
||||
.muteIcon .ion-ios-mic-off:hover:before {
|
||||
content: "\f461";
|
||||
}
|
||||
|
||||
.userName {
|
||||
color: #E6E6E6;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.status {
|
||||
float: left;
|
||||
width: 15px;
|
||||
min-width: 15px;
|
||||
height: 27px;
|
||||
margin-right: 10px;
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
@ -7,19 +7,15 @@
|
||||
border-top: 0px;
|
||||
border-left: 0px;
|
||||
border-right: 0px;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
-webkit-flex: 7 7 70%;
|
||||
-moz-flex: 7 7 70%;
|
||||
-ms-flex: 7 7 70%;
|
||||
flex: 7 7 70%;
|
||||
min-width: 50%;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
}
|
||||
@media @mobile-portrait-with-keyboard, @desktop-portrait, @mobile-portrait {
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
width: 100%;
|
||||
width: 100% !important; // overrides any width value set manually in landscape
|
||||
}
|
||||
&:-webkit-full-screen {
|
||||
width: 100%;
|
||||
@ -126,6 +122,7 @@
|
||||
}
|
||||
|
||||
.switchSlideButton {
|
||||
outline: none;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
margin-bottom: 0;
|
||||
@ -200,11 +197,12 @@
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
background: #3896D3;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
background: #3A82D4;
|
||||
}
|
||||
&:focus {
|
||||
background: #3896D3;
|
||||
outline:0;
|
||||
}
|
||||
i {
|
||||
|
@ -22,13 +22,15 @@
|
||||
else
|
||||
chatMessage.message?.from_userid
|
||||
Tracker.autorun (comp) ->
|
||||
tabsTime = getInSession('tabsRenderedTime')
|
||||
tabsTime = getInSession('userListRenderedTime')
|
||||
if tabsTime? and chatMessage.message.from_userid isnt "SYSTEM_MESSAGE" and chatMessage.message.from_time - tabsTime > 0
|
||||
populateChatTabs(chatMessage) # check if we need to open a new tab
|
||||
populateNotifications(chatMessage) # check if we need to show a new notification
|
||||
destinationTab = findDestinationTab()
|
||||
if destinationTab isnt getInSession "inChatWith"
|
||||
setInSession 'chatTabs', getInSession('chatTabs').map((tab) ->
|
||||
tab.gotMail = true if tab.userId is destinationTab
|
||||
setInSession 'chats', getInSession('chats').map((tab) ->
|
||||
if tab.userId is destinationTab
|
||||
tab.gotMail = true
|
||||
tab.number++
|
||||
tab
|
||||
)
|
||||
comp.stop()
|
||||
@ -120,22 +122,51 @@ Template.chatbar.helpers
|
||||
return Meteor.Users.findOne({userId: getInSession('inChatWith')})?
|
||||
|
||||
# When chatbar gets rendered, launch the auto-check for unread chat
|
||||
Template.chatbar.rendered = -> detectUnreadChat()
|
||||
Template.chatbar.rendered = ->
|
||||
detectUnreadChat()
|
||||
|
||||
# When "< Public" is clicked, go to public chat
|
||||
# When "< Public" is clicked, go to public chat
|
||||
Template.chatbar.events
|
||||
'click .toPublic': (event) ->
|
||||
setInSession 'inChatWith', 'PUBLIC_CHAT'
|
||||
setInSession 'chats', getInSession('chats').map((chat) ->
|
||||
if chat.userId is "PUBLIC_CHAT"
|
||||
chat.gotMail = false
|
||||
chat.number = 0
|
||||
chat
|
||||
)
|
||||
|
||||
Template.privateChatTab.rendered = ->
|
||||
if isLandscape() or isPortrait()
|
||||
$("#newMessageInput").focus()
|
||||
|
||||
# When message gets rendered, scroll to the bottom
|
||||
Template.message.rendered = ->
|
||||
$('#chatbody').scrollTop($('#chatbody')[0]?.scrollHeight)
|
||||
false
|
||||
|
||||
Template.chatInput.rendered = ->
|
||||
$('.panel-footer').resizable
|
||||
handles: 'n'
|
||||
minHeight: 70
|
||||
resize: (event, ui) ->
|
||||
if $('.panel-footer').css('top') is '0px'
|
||||
$('.panel-footer').height(70) # prevents the element from shrinking vertically for 1-2 px
|
||||
else
|
||||
$('.panel-footer').css('top', parseInt($('.panel-footer').css('top')) + 1 + 'px')
|
||||
$('#chatbody').height($('#chat').height() - $('.panel-footer').height() - 45)
|
||||
$('#chatbody').scrollTop($('#chatbody')[0]?.scrollHeight)
|
||||
start: (event, ui) ->
|
||||
$('#newMessageInput').css('overflow', '')
|
||||
$('.panel-footer').resizable('option', 'maxHeight', Math.max($('.panel-footer').height(), $('#chat').height() / 2))
|
||||
stop: (event, ui) ->
|
||||
setInSession 'chatInputMinHeight', $('.panel-footer').height() + 1
|
||||
|
||||
Template.chatInput.events
|
||||
'click #sendMessageButton': (event) ->
|
||||
$('#sendMessageButton').blur()
|
||||
sendMessage()
|
||||
adjustChatInputHeight()
|
||||
|
||||
'keypress #newMessageInput': (event) -> # user pressed a button inside the chatbox
|
||||
key = (if event.charCode then event.charCode else (if event.keyCode then event.keyCode else 0))
|
||||
@ -152,6 +183,11 @@ Template.chatInput.events
|
||||
$('#newMessageInput').val("")
|
||||
return false
|
||||
|
||||
Template.chatInputControls.rendered = ->
|
||||
$('#newMessageInput').on('keydown paste cut', () -> setTimeout(() ->
|
||||
adjustChatInputHeight()
|
||||
, 0))
|
||||
|
||||
Template.message.helpers
|
||||
sanitizeAndFormat: (str) ->
|
||||
if typeof str is 'string'
|
||||
|
@ -2,13 +2,7 @@
|
||||
<div id="{{id}}" {{visibility name}} class="component">
|
||||
<div class="chatBodyContainer">
|
||||
{{#if inPrivateChat}}
|
||||
<div class="privateChatTab">
|
||||
{{> makeButton id="close" btn_class="secondary tiny round toPublic " i_class="ion-ios-arrow-left" rel="tooltip"
|
||||
data_placement="bottom" title="Back to public" label="Public"}}
|
||||
<div class="privateChatName">
|
||||
{{privateChatName}}
|
||||
</div>
|
||||
</div>
|
||||
{{> privateChatTab}}
|
||||
{{/if}}
|
||||
<div id="chatbody">
|
||||
<ul class="chat" {{messageFontSize}}>
|
||||
@ -27,6 +21,16 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="privateChatTab">
|
||||
<div class="privateChatTab">
|
||||
{{> makeButton id="close" btn_class="secondary tiny toPublic " i_class="ion-ios-arrow-left" rel="tooltip"
|
||||
data_placement="bottom" title="Back to public" label="Public" notification="PUBLIC_CHAT"}}
|
||||
<div class="privateChatName">
|
||||
{{privateChatName}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="chatInput">
|
||||
<div id="chatInput" class="chat-input-wrapper">
|
||||
{{#if inPrivateChat}}
|
||||
|
@ -46,12 +46,12 @@
|
||||
|
||||
<div class="bar bottomBar">
|
||||
<a href="#" class="closeSettings close-reveal-modal"><u>Cancel</u></a>
|
||||
{{> makeButton id="saveSettings" btn_class="settingsButton" rel="tooltip" title="Save Changes" text="Save"}}
|
||||
{{> makeButton id="saveSettings" btn_class="settingsButton" rel="tooltip" title="Save Changes" label="Save"}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="logoutModal">
|
||||
<p>Are you sure you want to logout?</p>
|
||||
{{> makeButton id="yes" btn_class="logoutButton" rel="tooltip" title="Logout" text="Yes"}}
|
||||
{{> makeButton id="no" btn_class="logoutButton" rel="tooltip" title="Logout" text="No"}}
|
||||
{{> makeButton id="yes" btn_class="logoutButton" rel="tooltip" title="Logout" label="Yes"}}
|
||||
{{> makeButton id="no" btn_class="logoutButton" rel="tooltip" title="Logout" label="No"}}
|
||||
</template>
|
||||
|
33
bigbluebutton-html5/app/client/views/sharedTemplates.coffee
Executable file
33
bigbluebutton-html5/app/client/views/sharedTemplates.coffee
Executable file
@ -0,0 +1,33 @@
|
||||
Template.makeButton.helpers
|
||||
hasGotUnreadMail: (userId) ->
|
||||
chats = getInSession('chats')
|
||||
if chats isnt undefined
|
||||
if userId is "all_chats"
|
||||
for tabs in chats
|
||||
if tabs.gotMail is true
|
||||
return true
|
||||
else if userId is "PUBLIC_CHAT"
|
||||
for tabs in chats
|
||||
if tabs.userId is userId and tabs.gotMail is true
|
||||
return true
|
||||
return false
|
||||
|
||||
getNumberOfUnreadMessages: (userId) ->
|
||||
if userId is "all_chats"
|
||||
return
|
||||
else
|
||||
chats = getInSession('chats')
|
||||
if chats isnt undefined
|
||||
for chat in chats
|
||||
if chat.userId is userId and chat.gotMail
|
||||
if chat.number > 9
|
||||
return "9+"
|
||||
else
|
||||
return chat.number
|
||||
return
|
||||
|
||||
getNotificationClass: (userId) ->
|
||||
if userId is "all_chats"
|
||||
return "unreadChat"
|
||||
if userId is "PUBLIC_CHAT"
|
||||
return "unreadChatNumber"
|
@ -1,11 +1,18 @@
|
||||
<template name="makeButton">
|
||||
<button type="submit" id="{{id}}" class="btn {{btn_class}}" {{isDisabled}} rel="{{rel}}" data-placement="{{data_placement}}" title="{{title}}" style="{{style}}">
|
||||
{{#if text}}
|
||||
<span>{{text}}</span>
|
||||
{{else}}
|
||||
{{#if i_class}}
|
||||
<i class="{{i_class}}"></i><span>{{label}}</span>
|
||||
{{/if}}
|
||||
<button type="submit" id="{{id}}" class="btn {{btn_class}}" {{isDisabled}} rel="{{rel}}" data-placement="{{data_placement}}" title="{{title}}" style="{{style}}">
|
||||
{{#if notification}}
|
||||
{{#if hasGotUnreadMail notification }}
|
||||
<div class="{{getNotificationClass notification}}">{{getNumberOfUnreadMessages notification}}</div>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if i_class}}
|
||||
<i class="{{i_class}}"></i>
|
||||
{{/if}}
|
||||
{{#if label}}
|
||||
<span>{{label}}</span>
|
||||
{{/if}}
|
||||
{{#if span}}
|
||||
<span></span>
|
||||
{{/if}}
|
||||
</button>
|
||||
</template>
|
||||
|
@ -26,5 +26,49 @@ Template.displayUserIcons.helpers
|
||||
Template.usernameEntry.events
|
||||
'click .usernameEntry': (event) ->
|
||||
userIdSelected = @.userId
|
||||
unless userIdSelected is null or userIdSelected is BBB.getCurrentUser()?.userId
|
||||
setInSession "inChatWith", userIdSelected
|
||||
unless userIdSelected is null
|
||||
if userIdSelected is BBB.getCurrentUser()?.userId
|
||||
setInSession "inChatWith", "PUBLIC_CHAT"
|
||||
else
|
||||
setInSession "inChatWith", userIdSelected
|
||||
if isLandscape()
|
||||
$("#newMessageInput").focus()
|
||||
if isPortrait() or isPortraitMobile()
|
||||
toggleUsersList()
|
||||
$("#newMessageInput").focus()
|
||||
|
||||
'click .gotUnreadMail': (event) ->
|
||||
_this = @
|
||||
currentId = getInSession('userId')
|
||||
if currentId isnt undefined and currentId is _this.userId
|
||||
_id = "PUBLIC_CHAT"
|
||||
else
|
||||
_id = _this.userId
|
||||
chats = getInSession('chats')
|
||||
if chats isnt undefined
|
||||
for chat in chats
|
||||
if chat.userId is _id
|
||||
chat.gotMail = false
|
||||
chat.number = 0
|
||||
break
|
||||
setInSession 'chats', chats
|
||||
|
||||
Template.usernameEntry.helpers
|
||||
hasGotUnreadMailClass: (userId) ->
|
||||
chats = getInSession('chats')
|
||||
if chats isnt undefined
|
||||
for chat in chats
|
||||
if chat.userId is userId and chat.gotMail
|
||||
return true
|
||||
return false
|
||||
|
||||
getNumberOfUnreadMessages: (userId) ->
|
||||
chats = getInSession('chats')
|
||||
if chats isnt undefined
|
||||
for chat in chats
|
||||
if chat.userId is userId and chat.gotMail
|
||||
if chat.number > 9
|
||||
return "9+"
|
||||
else
|
||||
return chat.number
|
||||
return
|
||||
|
@ -54,38 +54,50 @@
|
||||
<i class="icon fi-lock usericon"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if user.presenter}}
|
||||
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter">
|
||||
<i class="icon fi-projection-screen usericon"></i>
|
||||
</span>
|
||||
{{else}}
|
||||
{{#if equals user.role "MODERATOR"}}
|
||||
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is a moderator">
|
||||
<i class="icon fi-torso usericon"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if user.raise_hand}}
|
||||
{{#if isCurrentUser userId}}
|
||||
<span class="ion-android-hand usericon" rel="tooltip" data-placement="bottom" title="Lower your hand"></span>
|
||||
{{else}}
|
||||
<span class="ion-android-hand usericon" rel="tooltip" data-placement="bottom" title="{{user.name}} has raised their hand"></span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="usernameEntry">
|
||||
{{#if isCurrentUser userId}}
|
||||
<span class="userCurrent usernameEntry" rel="tooltip" data-placement="bottom" title="{{user.name}} (you)">
|
||||
{{user.name}} {{#if user.presenter}} (presenter) {{/if}} (you)
|
||||
</span>
|
||||
{{else}}
|
||||
<span class="usernameEntry" rel="tooltip" data-placement="bottom" title="{{user.name}}">
|
||||
{{user.name}} {{#if user.presenter}} (presenter) {{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<div class="status">
|
||||
{{#if user.raise_hand}}
|
||||
{{#if isCurrentUser userId}}
|
||||
<span rel="tooltip" data-placement="bottom" title="Lower your hand">
|
||||
<i class="icon ion-android-hand statusIcon"></i>
|
||||
</span>
|
||||
{{else}}
|
||||
<span rel="tooltip" data-placement="bottom" title="{{user.name}} has raised their hand">
|
||||
<i class="icon ion-android-hand statusIcon"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if user.presenter}}
|
||||
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter">
|
||||
<i class="icon fi-projection-screen statusIcon"></i>
|
||||
</span>
|
||||
{{else}}
|
||||
{{#if equals user.role "MODERATOR"}}
|
||||
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is a moderator">
|
||||
<i class="icon fi-torso statusIcon"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if isCurrentUser userId}}
|
||||
<span class="userCurrent usernameEntry {{#if hasGotUnreadMailClass 'PUBLIC_CHAT'}}gotUnreadMail{{/if}}" rel="tooltip" data-placement="bottom" title="{{user.name}} (you)">
|
||||
<span class="userName">{{user.name}} {{#if user.presenter}} (presenter) {{/if}} (you)</span>
|
||||
</span>
|
||||
{{#if hasGotUnreadMailClass 'PUBLIC_CHAT' }}
|
||||
<div class="unreadChatNumber">{{getNumberOfUnreadMessages 'PUBLIC_CHAT'}}</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="usernameEntry {{#if hasGotUnreadMailClass user.userid}}gotUnreadMail{{/if}}" rel="tooltip" data-placement="bottom" title="{{user.name}}">
|
||||
<span class="userName"> {{user.name}} {{#if user.presenter}} (presenter) {{/if}}</span>
|
||||
</span>
|
||||
{{#if hasGotUnreadMailClass user.userid }}
|
||||
<div class="unreadChatNumber">{{getNumberOfUnreadMessages user.userid}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="userItem">
|
||||
|
@ -5,6 +5,14 @@ Template.usersList.helpers
|
||||
return "Users: #{numberUsers}"
|
||||
# do not display the label if there are just a few users
|
||||
|
||||
Template.usersList.events
|
||||
"click .closeUserlistIcon": (event, template) ->
|
||||
toggleUsersList()
|
||||
Template.usersList.rendered = ->
|
||||
$('.sl-left-drawer').resizable
|
||||
handles: 'e'
|
||||
maxWidth: 600
|
||||
minWidth: 200
|
||||
resize: () ->
|
||||
adjustChatInputHeight()
|
||||
Tracker.autorun (comp) ->
|
||||
setInSession 'userListRenderedTime', TimeSync.serverTime()
|
||||
if getInSession('userListRenderedTime') isnt undefined
|
||||
comp.stop()
|
||||
|
@ -2,11 +2,6 @@
|
||||
<div id="{{id}}" {{visibility name}} class="component {{class}}">
|
||||
<h3 class="meetingTitle">
|
||||
{{getMeetingName}}
|
||||
<span class="closeUserlistIcon">
|
||||
<a href="#">
|
||||
<i class="closeSettings close-reveal-modal">×</i>
|
||||
</a>
|
||||
</span>
|
||||
</h3>
|
||||
<div id="user-contents">
|
||||
<div class="userlist ScrollableWindowY">
|
||||
|
@ -6,7 +6,8 @@ Template.slide.rendered = ->
|
||||
setInSession 'slideOriginalHeight', this.height
|
||||
$(window).resize( ->
|
||||
# redraw the whiteboard to adapt to the resized window
|
||||
redrawWhiteboard()
|
||||
if !$('.panel-footer').hasClass('ui-resizable-resizing') # not in the middle of resizing the message input
|
||||
redrawWhiteboard()
|
||||
)
|
||||
if currentSlide?.slide?.png_uri?
|
||||
createWhiteboardPaper (wpm) ->
|
||||
|
@ -46,3 +46,18 @@ Template.whiteboard.events
|
||||
|
||||
'click .lowerHand': (event) ->
|
||||
BBB.lowerHand(BBB.getMeetingId(), getInSession('userId'), getInSession('userId'), getInSession('authToken'))
|
||||
|
||||
Template.whiteboard.rendered = ->
|
||||
$('#whiteboard').resizable
|
||||
handles: 'e'
|
||||
minWidth: 150
|
||||
resize: () ->
|
||||
adjustChatInputHeight()
|
||||
start: () ->
|
||||
if $('#chat').width() / $('#panels').width() > 0.2 # chat shrinking can't make it smaller than one fifth of the whiteboard-chat area
|
||||
$('#whiteboard').resizable('option', 'maxWidth', $('#panels').width() - 200) # gives the chat enough space (200px)
|
||||
else
|
||||
$('#whiteboard').resizable('option', 'maxWidth', $('#whiteboard').width())
|
||||
stop: () ->
|
||||
$('#whiteboard').css('width', 100 * $('#whiteboard').width() / $('#panels').width() + '%') # transforms width to %
|
||||
$('#whiteboard').resizable('option', 'maxWidth', null)
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template name="whiteboard">
|
||||
<div id="{{id}}" {{visibility name}} class="component gradientBar">
|
||||
<div id="{{id}}" {{visibility name}} class="component">
|
||||
{{#each getCurrentSlide}}
|
||||
{{> slide}}
|
||||
{{/each}}
|
||||
|
@ -103,8 +103,12 @@ public class RecordingServiceHelperImp implements RecordingServiceHelper {
|
||||
r.setPlaybackFormat(rec.playback.format.text());
|
||||
r.setPlaybackLink(rec.playback.link.text());
|
||||
r.setPlaybackDuration(rec.playback.duration.text());
|
||||
r.setPlaybackExtensions(rec.playback.extension.children());
|
||||
|
||||
/*
|
||||
Commenting this out to see if this is causing memory to hang around resulting in
|
||||
OOM in tomcat7 (ralam july 23, 2015)
|
||||
r.setPlaybackExtensions(rec.playback.extension.children());
|
||||
*/
|
||||
Map<String, String> meta = new HashMap<String, String>();
|
||||
rec.meta.children().each { anode ->
|
||||
log.debug("metadata: "+anode.name()+" "+anode.text())
|
||||
|
@ -201,7 +201,9 @@ public class DeskShareApplet extends JApplet implements ClientListener {
|
||||
}
|
||||
|
||||
public void onClientStop(ExitCode reason) {
|
||||
// determine if client is disconnected _PTS_272_
|
||||
client.stop();
|
||||
|
||||
/*
|
||||
if ( ExitCode.CONNECTION_TO_DESKSHARE_SERVER_DROPPED == reason ){
|
||||
JFrame pframe = new JFrame("Desktop Sharing Disconneted");
|
||||
if ( null != pframe ){
|
||||
@ -215,6 +217,7 @@ public class DeskShareApplet extends JApplet implements ClientListener {
|
||||
}else{
|
||||
client.stop();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user